2016年5月26日 星期四

Linux kernel - platform_get_resouce 和 platform_get_irq 用法

1. 在寫platform driver的時候,有些硬體相關的資源,如記憶體位址和中斷號,需要透過platform_get_resouce去取得,核心這邊也定義了resource的結構來放這些硬體的資訊,resource的結構如下,我們通常比較關心start、end和flag的定義,

當flag為IORESOURCE_MEM時,start、end表示platform device使用的記憶體開始位址和結束位址‧
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

當flag為IORESOURCE_IRQ時,start、end表示platform device使用的中斷號的開始值和結束值‧
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

2. include/linux/ioport.h
/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
};

/*
 * IO resources have these defined flags.
 */
#define IORESOURCE_BITS         0x000000ff      /* Bus-specific bits */

#define IORESOURCE_TYPE_BITS    0x00001f00      /* Resource type */
#define IORESOURCE_IO           0x00000100      /* PCI/ISA I/O ports */
#define IORESOURCE_MEM          0x00000200
#define IORESOURCE_REG          0x00000300      /* Register offsets */
#define IORESOURCE_IRQ          0x00000400
#define IORESOURCE_DMA          0x00000800
#define IORESOURCE_BUS          0x00001000

#define IORESOURCE_PREFETCH     0x00002000      /* No side effects */
#define IORESOURCE_READONLY     0x00004000
#define IORESOURCE_CACHEABLE    0x00008000
#define IORESOURCE_RANGELENGTH  0x00010000
#define IORESOURCE_SHADOWABLE   0x00020000

#define IORESOURCE_SIZEALIGN    0x00040000      /* size indicates alignment */
#define IORESOURCE_STARTALIGN   0x00080000      /* start field is alignment */

#define IORESOURCE_MEM_64       0x00100000
#define IORESOURCE_WINDOW       0x00200000      /* forwarded by bridge */
#define IORESOURCE_MUXED        0x00400000      /* Resource is software muxed */

#define IORESOURCE_EXCLUSIVE    0x08000000      /* Userland may not map this resource */
#define IORESOURCE_DISABLED     0x10000000
#define IORESOURCE_UNSET        0x20000000      /* No address assigned yet */
#define IORESOURCE_AUTO         0x40000000
#define IORESOURCE_BUSY         0x80000000      /* Driver has marked this resource busy */
3. 當resouce中定義了兩個以上的記憶體空間和一個中斷號資源
/* device tree 定義方式 */
  test: test@09100000 {
   compatible = "uio";
   reg = <0x09100000 0x10000>,
         <0x09200000 0x10000>;
   interrupts = <0 29 0x4>;
  };


/* 結構定義方式 */
static struct resource cns3xxx_usb_ehci_resources[] = {
        [0] = {
                .start = CNS3XXX_USB0_BASE,
                .end   = CNS3XXX_USB0_BASE + SZ_16M - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = CNS3XXX_USB1_BASE,
                .end   = CNS3XXX_USB1_BASE + SZ_16M - 1,
                .flags = IORESOURCE_MEM,
        },
        [2] = {
                .start = IRQ_CNS3XXX_USB_EHCI,
                .flags = IORESOURCE_IRQ,
        },
};
可透過platform_get_resouice第三個參數指定index
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
4. 在使用IRQ資源,platform_get_irq封裝了platform_get_resource,也可以使用platform_get_irq(struct platform_device *dev, unsigned int num);去取得中斷號資源,

drivers/base/platform.c
/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
                                       unsigned int type, unsigned int num)
{
        int i;

        for (i = 0; i < dev->num_resources; i++) {
                struct resource *r = &dev->resource[i];

                if (type == resource_type(r) && num-- == 0)
                        return r;
        }
        return NULL;
}
EXPORT_SYMBOL_GPL(platform_get_resource);

/**
 * platform_get_irq - get an IRQ for a device
 * @dev: platform device
 * @num: IRQ number index
 */
int platform_get_irq(struct platform_device *dev, unsigned int num)
{
#ifdef CONFIG_SPARC
        /* sparc does not have irqs represented as IORESOURCE_IRQ resources */
        if (!dev || num >= dev->archdata.num_irqs)
                return -ENXIO;
        return dev->archdata.irqs[num];
#else
        struct resource *r;
        if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
                int ret;

                ret = of_irq_get(dev->dev.of_node, num);
                if (ret >= 0 || ret == -EPROBE_DEFER)
                        return ret;
        }

        r = platform_get_resource(dev, IORESOURCE_IRQ, num);
        /*
         * The resources may pass trigger flags to the irqs that need
         * to be set up. It so happens that the trigger flags for
         * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER*
         * settings.
         */
        if (r && r->flags & IORESOURCE_BITS)
                irqd_set_trigger_type(irq_get_irq_data(r->start),
                                      r->flags & IORESOURCE_BITS);

        return r ? r->start : -ENXIO;
#endif
}
5. 範例
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/platform_device.h>

struct sample_timer {
        void __iomem *base;
        void __iomem *memory;
        int irq;
};

static int sample_probe(struct platform_device *pdev)
{
        struct resource *base_res, *memory_res;
        static struct sample_timer *timer; 

        pr_alert("%s\n", __FUNCTION__);

        timer = devm_kzalloc(&pdev->dev, sizeof(struct sample_timer), GFP_KERNEL);
        if (NULL == timer) {
                dev_err(&pdev->dev, "cannot allocate memory\n");
                return -ENOMEM;
        }

        base_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (NULL == base_res) {
                dev_err(&pdev->dev, "cannot get IORESOUCE_MEM\n");
                return -ENOENT;
        }

        memory_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        if (NULL == memory_res) {
                dev_err(&pdev->dev, "cannot get IORESOUCE_MEM\n");
                return -ENOENT;
        }

        timer->base = devm_ioremap_resource(&pdev->dev, base_res);
        timer->memory = devm_ioremap_resource(&pdev->dev, memory_res);

        if ((IS_ERR((const void *)(timer->base))) || (IS_ERR((const void *)(timer->memory)))) {
                dev_err(&pdev->dev, "cannot get ioremap\n");
                return -ENOENT;
        }

        timer->irq = platform_get_irq(pdev, 0);
        if (timer->irq < 0) {
                dev_err(&pdev->dev, "no irq specified\n");
                return -ENOENT;
        }

        return 0;
}

static int sample_remove(struct platform_device *pdev)
{
        return 0;
}

#define sample_suspend NULL
#define sample_resume NULL

static const struct of_device_id sample_dt_ids[] = {
        {.compatible = "sample,timer",},
        { }
};

MODULE_DEVICE_TABLE(of, sample_dt_ids);

static struct platform_driver sample_driver = {
        .probe = sample_probe,
        .remove = sample_remove,
        .suspend = sample_suspend,
        .resume = sample_resume,
        .driver = {
                .name = "sample",
                .owner = THIS_MODULE,
                .of_match_table = sample_dt_ids,
        },
};

static int __init sample_init(void)
{
        int ret;

        ret = platform_driver_register(&sample_driver);

        return ret;
}

static void __exit sample_exit(void)
{
        platform_driver_unregister(&sample_driver);
}

module_init(sample_init);
module_exit(sample_exit);

MODULE_AUTHOR("Allen");
MODULE_DESCRIPTION("sample");
MODULE_LICENSE("GPL");
6. 參考來源
platform设备驱动全透析 http://www.bianceng.cn/OS/Linux/201301/34910_3.htm

2016年5月14日 星期六

Linux kernel - platform_set_drvdata 和 platform_get_drvdata 用法

1. 當裝置的platform data在probe函數中定義為區域變數,如果我們想要在其它地方使用,核心提供了這個方法,使用platform_set_drvdata()可以將data保存成裝置的私有變數,等到之後要使用時,只需要呼叫platform_get_drvdata(),就可以取出data變數。

2. platform_set_drvdata() 和 platform_get_drvdata()函數被定義在include/linux/platform_device.h
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
        return dev_get_drvdata(&pdev->dev);
}

static inline void platform_set_drvdata(struct platform_device *pdev,
                                        void *data)
{
        dev_set_drvdata(&pdev->dev, data);
}
dev_get_drvdata() 和 dev_set_drvdata()函數被定義在include/linux/device.h
static inline void *dev_get_drvdata(const struct device *dev)
{
        return dev->driver_data;
}

static inline void dev_set_drvdata(struct device *dev, void *data)
{
        dev->driver_data = data;
}
3. 範例 drivers/leds/leds-gpio.c
static int gpio_led_probe(struct platform_device *pdev)
{
        struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct gpio_leds_priv *priv;
        int i, ret = 0;

        if (pdata && pdata->num_leds) {
                priv = devm_kzalloc(&pdev->dev,
                                sizeof_gpio_leds_priv(pdata->num_leds),
                                        GFP_KERNEL);
                if (!priv)
                        return -ENOMEM;

                priv->num_leds = pdata->num_leds;
                for (i = 0; i < priv->num_leds; i++) {
                        ret = create_gpio_led(&pdata->leds[i],
                                              &priv->leds[i],
                                              &pdev->dev, pdata->gpio_blink_set);
                        if (ret < 0) {
                                /* On failure: unwind the led creations */
                                for (i = i - 1; i >= 0; i--)
                                        delete_gpio_led(&priv->leds[i]);
                                return ret;
                        }
                }
        } else {
                priv = gpio_leds_create(pdev);
                if (IS_ERR(priv))
                        return PTR_ERR(priv);
        }

        platform_set_drvdata(pdev, priv);

        return 0;
}

static int gpio_led_remove(struct platform_device *pdev)
{
        struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
        int i;

        for (i = 0; i < priv->num_leds; i++)
                delete_gpio_led(&priv->leds[i]);

        return 0;
}

static struct platform_driver gpio_led_driver = {
        .probe          = gpio_led_probe,
        .remove         = gpio_led_remove,
        .driver         = {
                .name   = "leds-gpio",
                .of_match_table = of_gpio_leds_match,
        },
};

module_platform_driver(gpio_led_driver);

MODULE_AUTHOR("Raphael Assenat , Trent Piepho ");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:leds-gpio");

2016年5月12日 星期四

編譯錯誤 - 需指定平台 ARCH=arm

$ make
cc1: error: unrecognized command line option "-m64"
cc1: error: unrecognized command line option "-mno-80387"
cc1: error: unrecognized command line option "-mno-fp-ret-in-387"
cc1: error: unrecognized command line option "-mno-red-zone"
cc1: error: unrecognized command line option "-mcmodel=kernel"
cc1: error: unrecognized command line option "-maccumulate-outgoing-args"
cc1: error: unrecognized command line option "-mno-sse"
cc1: error: unrecognized command line option "-mno-mmx"
cc1: error: unrecognized command line option "-mno-sse2"
cc1: error: unrecognized command line option "-mno-3dnow"
cc1: error: unrecognized command line option "-mno-avx"
cc1: error: unrecognized command line option "-fno-var-tracking-assignments"
cc1: warning: unrecognized command line option "-Wno-unused-but-set-variable"

$make ARCH=arm

2016年5月5日 星期四

Raspberry Pi - 啟用 root 帳號

1. Raspberry Pi預設帳號為pi:raspberry,如要啟用root帳號,需要執行下列命令
pi@raspberrypi: $ sudo passwd root
Enter new UNIX password: raspberry
Retype new UNIX password: raspberry
passwd: password updated successfully
2. 解鎖root帳號
pi@raspberrypi: $ sudo passwd --unlock root
3. 登入root帳號
pi@raspberrypi: $ su
Password: raspberry

root@raspberrypi:
4. 參考來源
樹莓派(raspberry)啟用root 帳號

2016年5月4日 星期三

Raspberry Pi - 控制內建LED燈 PWR LED 和 ACT LED

1. Raspberry Pi有兩個內建的LED燈,一個為ACT LED,另一個是PWR LED。
OK (ACT) LED = led0
Power (PWR) LED = led1
2. 查詢所有點燈模式
$ cat /sys/class/leds/led0/trigger
none mmc0 timer oneshot [heartbeat] backlight gpio cpu0 cpu1 cpu2 cpu3 default-on input
3.1. 點亮LED燈
$ sudo sh -c 'echo none > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo none > /sys/class/leds/led1/trigger'
$ sudo sh -c 'echo 0 > /sys/class/leds/led0/brightness'
$ sudo sh -c 'echo 0 > /sys/class/leds/led1/brightness'
點暗LED燈
$ sudo sh -c 'echo none > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo none > /sys/class/leds/led1/trigger'
$ sudo sh -c 'echo 1 > /sys/class/leds/led0/brightness'
$ sudo sh -c 'echo 1 > /sys/class/leds/led1/brightness'
3.2. 以 SD 卡讀寫狀態觸發LED燈
$ sudo sh -c 'echo mmc0 > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo mmc0 > /sys/class/leds/led1/trigger'
3.3. 持續閃爍LED燈,delay_on為點亮時間,delay_off為點暗時間
$ sudo sh -c 'echo timer > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo 250 > /sys/class/leds/led0/delay_on'
$ sudo sh -c 'echo 250 > /sys/class/leds/led0/delay_off'

$ sudo sh -c 'echo timer > /sys/class/leds/led1/trigger'
$ sudo sh -c 'echo 2000 > /sys/class/leds/led1/delay_on'
$ sudo sh -c 'echo 2000 > /sys/class/leds/led1/delay_off'
3.4. 點亮led0,單次觸發後,持續delay_on時間後led熄滅
$ sudo sh -c 'echo oneshot > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo 3000 > /sys/class/leds/led0/delay_on'

$ sudo sh -c 'echo 1 > /sys/class/leds/led0/shot'
3.5. 心跳閃爍LED燈
$ sudo sh -c 'echo heartbeat > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo heartbeat > /sys/class/leds/led1/trigger'
3.6. 以 CPU 狀態觸發LED燈
$ sudo sh -c 'echo cpu0 > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo cpu0 > /sys/class/leds/led1/trigger'
3.7. 點亮LED燈
$ sudo sh -c 'echo default-on > /sys/class/leds/led0/trigger'
$ sudo sh -c 'echo default-on > /sys/class/leds/led1/trigger'
4. Dump GPIO相關資訊
$ mount -t debugfs debugfs /sys/kernel/debug
mount: debugfs is already mounted or /sys/kernel/debug busy
       debugfs is already mounted on /sys/kernel/debug

$ cat /sys/kernel/debug/gpio
GPIOs 0-53, platform/3f200000.gpio, pinctrl-bcm2835:
 gpio-35  (?                   ) out lo
 gpio-47  (?                   ) out lo
5. 參考來源
https://gist.github.com/taktran/1b691c08216dd30b70bf
以程式控制樹莓派 Raspberry Pi 的 ACT LED 指示燈
Linux PSP GPIO Driver Guide
Raspberry Pi と Raspberry Pi2の非互換性