? ? ? ? ?本文主要探讨使用210的gpiolib库编写led驱动。
gpio.h
#define S5PV210_GPIO_A0_NR (8)
......
#define S5PV210_GPIO_ETC4_NR?? ?(6)
????????定义端口的GPIO数目
#define S5PV210_GPIO_NEXT(__gpio) \
?? ?((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
????????定义端口起始GPIO值(gpiochip0,gpiochip9...)
#define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr))
...
#define S5PV210_ETC4(_nr)?? ?(S5PV210_GPIO_ETC4_START + (_nr))
????????定义每个GPIO号码,宏可获取每个GPIO号码,?? ?S5PV210_GPA0(4)为GPA0_4的号码
/sys/class/gpio
????????export文件写入端口n号到export将生成gpion目录(用于端口测试)
????????unexport文件写入端口n号到export将删除gpion目录
????????gpiochipn:
????????base为端口号(GPA0为0)
????????label端口名称
????????ngpio为gpio数目(gpio = base)
????????device
????????power电源相关
????????subsystem GPIO所属的子系统
? ? ? ? uevent GPIO状态改变,通知用户
????????gpion:
????????active_low(1:GPIO高是逻辑低,0:GPIO高是逻辑高)
????????device
????????direction(输入:in, 输出:out)
????????edge中断触发(none:无,rising:上升沿触发,falling:下降沿触发,both:上升沿和下降沿触发)
? ? ? ? power电源相关
????????subsystem GPIO所属的子系统
????????uevent GPIO状态改变,通知用户
????????value(0、1)
LED内核驱动框架
????????drivers/leds为LED硬件驱动目录,led-class.c和led-core.c为内核提供,其他leds-xxx.c为厂商提供,leds-xxx.c调用led-class.c和led-core.c的接口实现led驱动
????????leds-s3c24xx.c调用led_classdev_register注册LED驱动,led_classdev_register在drivers/leds/led-class.c定义
????????led_classdev_register函数创建leds类设备
????????210的led驱动为drivers/char/led/x210-led.c
static int s3c24xx_led_probe(struct platform_device *dev)
{
?? ?...
?? ?/* register our new led device */
?? ?ret = led_classdev_register(&dev->dev, &led->cdev);
?? ?if (ret < 0) {
?? ??? ?dev_err(&dev->dev, "led_classdev_register failed\n");
?? ??? ?kfree(led);
?? ??? ?return ret;
?? ?}
}
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
?? ?led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
?? ??? ??? ??? ? ? ? ?"%s", led_cdev->name);
?? ?if (IS_ERR(led_cdev->dev))
?? ??? ?return PTR_ERR(led_cdev->dev);
#ifdef CONFIG_LEDS_TRIGGERS
?? ?init_rwsem(&led_cdev->trigger_lock);
#endif
?? ?/* add to the list of leds */
?? ?down_write(&leds_list_lock);
?? ?list_add_tail(&led_cdev->node, &leds_list);
?? ?up_write(&leds_list_lock);
?? ?if (!led_cdev->max_brightness)
?? ??? ?led_cdev->max_brightness = LED_FULL;
?? ?led_update_brightness(led_cdev);
#ifdef CONFIG_LEDS_TRIGGERS
?? ?led_trigger_set_default(led_cdev);
#endif
?? ?printk(KERN_DEBUG "Registered led device: %s\n",
?? ??? ??? ?led_cdev->name);
?? ?return 0;
}
struct led_classdev {
?? ?const char?? ??? ?*name;
?? ?int?? ??? ??? ? brightness;
?? ?int?? ??? ??? ? max_brightness;
?? ?int?? ??? ??? ? flags;
?? ?/* Lower 16 bits reflect status */
#define LED_SUSPENDED?? ??? ?(1 << 0)
?? ?/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME?? ?(1 << 16)
?? ?/* Set LED brightness level */
?? ?/* Must not sleep, use a workqueue if needed */
?? ?void?? ??? ?(*brightness_set)(struct led_classdev *led_cdev,
?? ??? ??? ??? ??? ? ?enum led_brightness brightness);
?? ?/* Get LED brightness level */
?? ?enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
?? ?/* Activate hardware accelerated blink, delays are in
?? ? * miliseconds and if none is provided then a sensible default
?? ? * should be chosen. The call can adjust the timings if it can't
?? ? * match the values specified exactly. */
?? ?int?? ??? ?(*blink_set)(struct led_classdev *led_cdev,
?? ??? ??? ??? ? ? ? unsigned long *delay_on,
?? ??? ??? ??? ? ? ? unsigned long *delay_off);
?? ?struct device?? ??? ?*dev;
?? ?struct list_head?? ? node;?? ??? ??? ?/* LED Device list */
?? ?const char?? ??? ?*default_trigger;?? ?/* Trigger to use */
#ifdef CONFIG_LEDS_TRIGGERS
?? ?/* Protects the trigger data below */
?? ?struct rw_semaphore?? ? trigger_lock;
?? ?struct led_trigger?? ?*trigger;
?? ?struct list_head?? ? trig_list;
?? ?void?? ??? ??? ?*trigger_data;
#endif
};
//led-class.c
subsys_initcall(leds_init);
//include/linux/init.h
#define subsys_initcall(fn)?? ??? ?__define_initcall("4",fn,4)
//include/linux/init.h
#define __define_initcall(level,fn,id) \
?? ?static initcall_t __initcall_##fn##id __used \
?? ?__attribute__((__section__(".initcall" level ".init"))) = fn
//推导出自定义段
.initcall4.init
//arch/arm/kernel/vmlinux.lds
? __initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
????????subsys_initcall宏,定义在linux/init.h中,宏将其声明的函数放到.initcall4.init段
????????module_init宏将声明函数放到.initcall6.init段
????????内核在启动过程中按照.initcalln.init(n=1..8)的层级依次初始化
led_class_attrs
subsys_initcall(leds_init);
static int __init leds_init(void)
{
?? ?leds_class = class_create(THIS_MODULE, "leds");
?? ?if (IS_ERR(leds_class))
?? ??? ?return PTR_ERR(leds_class);
?? ?leds_class->suspend = led_suspend;
?? ?leds_class->resume = led_resume;
?? ?leds_class->dev_attrs = led_class_attrs;
?? ?return 0;
}
static struct device_attribute led_class_attrs[] = {
?? ?__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
?? ?__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS
?? ?__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif
?? ?__ATTR_NULL,
};
????????leds_init中创建了leds类(/sys/class/leds),led_class_attrs数组内容为/sys/class/leds/目录里的文件,该文件为内核向应用层提供的操作接口(类似设备文件)
????????led_brightness_show函数类似read,led_brightness_store雷日write,他们对应struct led_classdev结构体的读和写方法
????????写方法:void (*brightness_set)(struct led_classdev *led_cdev,enum led_brightness brightness);
????????读方法:法enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
gpiolib?? ??? ?
//mach-mdkc110.c
#ifdef CONFIG_MACH_SMDKC110
MACHINE_START(SMDKC110, "SMDKC110")
#elif CONFIG_MACH_SMDKV210
MACHINE_START(SMDKV210, "SMDKV210")
#endif
?? ?/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
?? ?.phys_io?? ?= S3C_PA_UART & 0xfff00000,
?? ?.io_pg_offst?? ?= (((u32)S3C_VA_UART) >> 18) & 0xfffc,
?? ?.boot_params?? ?= S5P_PA_SDRAM + 0x100,
?? ?//.fixup?? ??? ?= smdkv210_fixup,
?? ?.init_irq?? ?= s5pv210_init_irq,
?? ?.map_io?? ??? ?= smdkc110_map_io,
?? ?.init_machine?? ?= smdkc110_machine_init,
?? ?.timer?? ??? ?= &s5p_systimer,
MACHINE_END
static void __init smdkc110_map_io(void)
{
?? ?s5p_init_io(NULL, 0, S5P_VA_CHIPID);
?? ?s3c24xx_init_clocks(24000000);
?? ?s5pv210_gpiolib_init();
?? ?s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));
?? ?s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs));
#ifdef CONFIG_MTD_ONENAND
?? ?s5pc110_device_onenand.name = "s5pc110-onenand";
#endif
#ifdef CONFIG_MTD_NAND
?? ?s3c_device_nand.name = "s5pv210-nand";
#endif
?? ?s5p_device_rtc.name = "smdkc110-rtc";
}
初始化gpiolib库
//gpiolib.c
__init int s5pv210_gpiolib_init(void)
{
?? ?struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;
?? ?int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
?? ?int i = 0;
?? ?for (i = 0; i < nr_chips; i++, chip++) {
?? ??? ?if (chip->config == NULL)
?? ??? ??? ?chip->config = &gpio_cfg;
?? ??? ?if (chip->base == NULL)
?? ??? ??? ?chip->base = S5PV210_BANK_BASE(i);
?? ?}
?? ?samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);
?? ?return 0;
}
struct s3c_gpio_chip {
?? ?struct gpio_chip?? ?chip;?? ??? ?
?? ?struct s3c_gpio_cfg?? ?*config;
?? ?struct s3c_gpio_pm?? ?*pm;
?? ?void __iomem?? ??? ?*base;
?? ?int?? ??? ??? ?eint_offset;
?? ?spinlock_t?? ??? ? lock;
#ifdef CONFIG_PM
?? ?u32?? ??? ??? ?pm_save[7];
#endif
};
struct gpio_chip {
?? ?const char?? ??? ?*label;
?? ?struct device?? ??? ?*dev;
?? ?struct module?? ??? ?*owner;
?? ?int?? ??? ??? ?(*request)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset);
?? ?void?? ??? ??? ?(*free)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset);
?? ?int?? ??? ??? ?(*direction_input)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset);
?? ?int?? ??? ??? ?(*get)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset);
?? ?int?? ??? ??? ?(*direction_output)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset, int value);
?? ?int?? ??? ??? ?(*set_debounce)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset, unsigned debounce);
?? ?void?? ??? ??? ?(*set)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset, int value);
?? ?int?? ??? ??? ?(*to_irq)(struct gpio_chip *chip,
?? ??? ??? ??? ??? ??? ?unsigned offset);
?? ?void?? ??? ??? ?(*dbg_show)(struct seq_file *s,
?? ??? ??? ??? ??? ??? ?struct gpio_chip *chip);
?? ?int?? ??? ??? ?base;
?? ?u16?? ??? ??? ?ngpio;
?? ?const char?? ??? ?*const *names;
?? ?unsigned?? ??? ?can_sleep:1;
?? ?unsigned?? ??? ?exported:1;
};
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
?? ?{
?? ??? ?.chip?? ?= {
?? ??? ??? ?.base?? ?= S5PV210_GPA0(0),
?? ??? ??? ?.ngpio?? ?= S5PV210_GPIO_A0_NR,
?? ??? ??? ?.label?? ?= "GPA0",
?? ??? ??? ?.to_irq = s5p_gpiolib_gpioint_to_irq,
?? ??? ?},
?? ?},?
?? ?{
?? ??? ?.chip?? ?= {
?? ??? ??? ?.base?? ?= S5PV210_GPA1(0),
?? ??? ??? ?.ngpio?? ?= S5PV210_GPIO_A1_NR,
?? ??? ??? ?.label?? ?= "GPA1",
?? ??? ??? ?.to_irq = s5p_gpiolib_gpioint_to_irq,
?? ??? ?},
?? ?}
?? ?......
?? ?}
?struct s3c_gpio_cfg {
?? ?unsigned int?? ?cfg_eint;
?? ?s3c_gpio_pull_t?? ?(*get_pull)(struct s3c_gpio_chip *chip, unsigned offs);
?? ?int?? ??? ?(*set_pull)(struct s3c_gpio_chip *chip, unsigned offs,
?? ??? ??? ??? ? ? ?s3c_gpio_pull_t pull);
?? ?int?? ??? ?(*set_pin)(struct s3c_gpio_chip *chip, unsigned offs,
?? ??? ??? ??? ? ? ?s3c_gpio_pull_t level);
?? ?unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs);
?? ?int?? ? (*set_config)(struct s3c_gpio_chip *chip, unsigned offs,
?? ??? ??? ? ? ? ? unsigned config);
};
void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,
?? ??? ??? ??? ??? ? ? int nr_chips)
{
?? ?for (; nr_chips > 0; nr_chips--, chip++) {
?? ??? ?samsung_gpiolib_add_4bit(chip);
?? ??? ?s3c_gpiolib_add(chip);
?? ?}
}
????????struct gpio_chip?? ?chip;单口各模式定义(输入,输出,设置,读取)
????????int?? ??? ??? ?base;端口的基址编号
????????u16?? ??? ??? ?ngpio;io口数量
????????const char?? ??? ?*const *names;端口名
????????int?? ??? ??? ?(*to_irq)(struct gpio_chip *chip;基于io口计算中号方法
????????s5pv210_gpio_4bit为是s3c_gpio_chip结构体数组,用于gpiolib定义注册端口(未定义注册模式及模式操作方法)
????????struct s3c_gpio_cfg?? ?*config;端口上下拉等操作定义
????????void __iomem?? ??? ?*base;端口寄存器虚拟地址基地址(JPA0端口地址,端口包含多个io口)
????????samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);定义注册端口模式及模式操作方法和端口注册
? ? ? ? samsung_gpiolib_add_4bit(chip);定义注册端口模式及模式操作方法
????????s3c_gpiolib_add(chip);端口注册(调用gpiochip_add函数将GPIO端口信息chip结构体链接gpiolib定义的gpio_desc数组)
int gpiochip_add(struct gpio_chip *chip)
{
?? ?......
?? ?/* these GPIO numbers must not be managed by another gpio_chip */
?? ?for (id = base; id < base + chip->ngpio; id++) {
?? ??? ?if (gpio_desc[id].chip != NULL) {
?? ??? ??? ?status = -EBUSY;
?? ??? ??? ?break;
?? ??? ?}
?? ?}
?? ?.....
}
gpiolib通用函数(drivers/gpio/gpiolib.c)
int gpiochip_add(struct gpio_chip *chip);
注册gpiolib
int __must_check gpiochip_remove(struct gpio_chip *chip);
清除注册
struct gpio {
?? ?unsigned?? ?gpio;
?? ?unsigned long?? ?flags;
?? ?const char?? ?*label;
};
flag(gpio.h):
#define GPIOF_DIR_OUT?? ?(0 << 0)
#define GPIOF_DIR_IN?? ?(1 << 0)
#define GPIOF_INIT_LOW?? ?(0 << 1)
#define GPIOF_INIT_HIGH?? ?(1 << 1)
#define GPIOF_IN?? ??? ?(GPIOF_DIR_IN)
#define GPIOF_OUT_INIT_LOW?? ?(GPIOF_DIR_OUT | GPIOF_INIT_LOW)
#define GPIOF_OUT_INIT_HIGH?? ?(GPIOF_DIR_OUT | GPIOF_INIT_HIGH)
GPIOF_DIR_IN?? ??? ? ??? ??? ?输入
GPIOF_DIR_OUT?? ??? ? ??? ??? ?输出
GPIOF_INIT_LOW?? ? ??? ? ?? ?输出低电平
GPIOF_INIT_HIGH?? ? ? ?? ??? ?输出高电平
GPIOF_IN?? ??? ? ?? ??? ??? ? ?? ??? ?输入
GPIOF_OUT_INIT_LOW?? ? ?? ?输出低电平
GPIOF_OUT_INIT_HIGH?? ??? ?输出高电平
int gpio_request(unsigned gpio, const char *label)
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
int gpio_request_array(struct gpio *array, size_t num);
申请gpio口
int gpio_is_valid(int number)
检查GPIO是否有效
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
设置GPIO为输入或输出模式
int __gpio_get_value(unsigned gpio);
读取gpio
void __gpio_set_value(unsigned gpio, int value);
设置GPIO
void gpio_free(unsigned gpio);?? ??? ??? ??? ?
void gpio_free_array(struct gpio *array, size_t num);?? ?
释放申请的gpio
demo:?
? ? ? ? 使用gpiolib编写led驱动
Makefile
# LED Platform Drivers
obj-$(CONFIG_LEDS_CXB_210) += led-cxb_210.o
Kconfig?
if LEDS_CLASS
comment "LED drivers"
config LEDS_CXB_210
tristate "LED Support for x210 led "
help
x210 led form cxb.
menuconfig?
Device Drivers --->
[*] LED Support --->
--- LED Support <*> LED Class Support
*** LED drivers ***
<*> LED Support for x210 led
?led-cxb_210.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#define LED1 S5PV210_GPJ0(3)
#define LED2 S5PV210_GPJ0(4)
#define LED3 S5PV210_GPJ0(5)
#define LED1_GPIO "led1_gpio3"
#define LED2_GPIO "led2_gpio4"
#define LED3_GPIO "led3_gpio5"
#define LED1_NAME "led1"
#define LED2_NAME "led2"
#define LED3_NAME "led3"
static struct led_classdev s5pv210_led1;
static struct led_classdev s5pv210_led2;
static struct led_classdev s5pv210_led3;
#define LED_OFF 1
#define LED_ON 0
struct gpio led[] = {
{S5PV210_GPJ0(3),GPIOF_OUT_INIT_HIGH,LED1_GPIO},
{S5PV210_GPJ0(4),GPIOF_OUT_INIT_HIGH,LED2_GPIO},
{S5PV210_GPJ0(5),GPIOF_OUT_INIT_HIGH,LED3_GPIO},
};
static void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness value)
{
if(value == LED_OFF)
{
gpio_set_value(LED1,LED_OFF);
}
else
{
gpio_set_value(LED1,LED_ON);
}
}
static void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness value)
{
if(value == LED_OFF)
{
gpio_set_value(LED1,LED_OFF);
}
else
{
gpio_set_value(LED1,LED_ON);
}
}
static void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness value)
{
if(value == LED_OFF)
{
gpio_set_value(LED1,LED_OFF);
}
else
{
gpio_set_value(LED1,LED_ON);
}
}
static int __init led_dev_init(void)
{
int ret = -1;
s5pv210_led1.name = LED1_NAME;
s5pv210_led1.brightness = 0;
s5pv210_led1.brightness_set = s5pv210_led1_set;
ret = led_classdev_register(NULL, &s5pv210_led1);
if(ret < 0)
{
printk(KERN_INFO "210_led1 register fail\n");
goto flag_1;
}
s5pv210_led2.name = LED2_NAME;
s5pv210_led2.brightness = 0;
s5pv210_led2.brightness_set = s5pv210_led2_set;
ret = led_classdev_register(NULL, &s5pv210_led2);
if(ret < 0)
{
printk(KERN_INFO "210_led2 register fail\n");
goto flag_2;
}
s5pv210_led3.name = LED3_NAME;
s5pv210_led3.brightness = 0;
s5pv210_led3.brightness_set = s5pv210_led3_set;
ret = led_classdev_register(NULL, &s5pv210_led3);
if(ret < 0)
{
printk(KERN_INFO "210_led3 register fail\n");
goto flag_3;
}
ret = gpio_request_array(led, ARRAY_SIZE(led));
if(ret < 0)
{
printk(KERN_INFO "request gpio fail\n");
goto flag_4;
}
return ret;
flag_4:
gpio_free_array(led,ARRAY_SIZE(led));
flag_3:
led_classdev_unregister(&s5pv210_led3);
flag_2:
led_classdev_unregister(&s5pv210_led2);
flag_1:
led_classdev_unregister(&s5pv210_led1);
return ret;
}
static void __exit led_dev_exit(void)
{
led_classdev_unregister(&s5pv210_led1);
led_classdev_unregister(&s5pv210_led2);
led_classdev_unregister(&s5pv210_led2);
gpio_free_array(led,ARRAY_SIZE(led));
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxb");
MODULE_DESCRIPTION("led gpio module");
MODULE_ALIAS("led");