字符设备驱动(1)代码分析---之gpio_get_value
在中断处理函数中,调用gpio_get_value/gpio_set_value()函数来获取/设置gpio端口的值,在这里简单分析一下内核的实现流程。
tmp = gpio_get_value(S5PV210_GPH2(0)); #define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio) { struct gpio_chip *chip; int value; chip = gpio_to_chip(gpio); ## { .base = (S5P_VA_GPIO + 0xC40), .config = &gpio_cfg_noint, .irq_base = IRQ_EINT(16), .chip = { .base = S5PV210_GPH2(0), .ngpio = S5PV210_GPIO_H2_NR, .label = "GPH2", .to_irq = samsung_gpiolib_to_irq, }, ## value = chip->get ? chip->get(chip, gpio - chip->base) : 0; //offset = gpio - chip->base trace_gpio_value(gpio, 1, value); return value; ## /**************************************************** chip->get()函数的内核实现 ****************************************************/ //drivers/gpio/gpio-plat-samsung.c void __init samsung_gpiolib_add_4bit_chips (structs 3c_gpio_chip *chip,int nr_chips) { for (; nr_chips > 0; nr_chips--, chip++) { samsung_gpiolib_add_4bit(chip); s3c_gpiolib_add(chip); } } void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip) { chip->chip.direction_input = samsung_gpiolib_4bit_input; chip->chip.direction_output = samsung_gpiolib_4bit_output; chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); } //arch/arm/plat_samsung/gpio.c __init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) { struct gpio_chip *gc = &chip->chip; int ret; .... .... if (!gc->direction_input) gc->direction_input = s3c_gpiolib_input; if (!gc->direction_output) gc->direction_output = s3c_gpiolib_output; if (!gc->set) gc->set = s3c_gpiolib_set; if (!gc->get) gc->get = s3c_gpiolib_get; //至此完善后的结构体gc变为:
gc = { .base = (S5P_VA_GPIO + 0xC40), .config = &gpio_cfg_noint, .irq_base = IRQ_EINT(16), .pm = __gpio_pm(&s3c_gpio_pm_4bit), .chip = { .base = S5PV210_GPH2(0), .ngpio = S5PV210_GPIO_H2_NR, .label = "GPH2", .to_irq = samsung_gpiolib_to_irq, .direction_input = samsung_gpiolib_4bit_input, .direction_output = samsung_gpiolib_4bit_output, .set = s3c_gpiolib_set, .get = s3c_gpiolib_get, },
###
//samsung_gpiolib_4bit_input具体实现函数
static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, unsigned int offset) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; unsigned long con; con = __raw_readl(base + GPIOCON_OFF); con &= ~(0xf << con_4bit_shift(offset)); __raw_writel(con, base + GPIOCON_OFF); gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); return 0; }
static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, unsigned int offset, int value) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; unsigned long con; unsigned long dat; con = __raw_readl(base + GPIOCON_OFF); con &= ~(0xf << con_4bit_shift(offset)); con |= 0x1 << con_4bit_shift(offset); dat = __raw_readl(base + GPIODAT_OFF); if (value) dat |= 1 << offset; else dat &= ~(1 << offset); //有点小疑问:为啥要写两次数据??? __raw_writel(dat, base + GPIODAT_OFF); __raw_writel(con, base + GPIOCON_OFF); __raw_writel(dat, base + GPIODAT_OFF); gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); return 0; }
//s3c_gpiolib_set、s3c_gpiolib_get的具体实现函数
static void s3c_gpiolib_set(struct gpio_chip *chip, unsigned offset, int value) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; unsigned long flags; unsigned long dat; s3c_gpio_lock(ourchip, flags); dat = __raw_readl(base + 0x04); //将需要设定的位清零 dat &= ~(1 << offset); //如果设定的值是1,则将相应位置1 if (value) dat |= 1 << offset; __raw_writel(dat, base + 0x04); s3c_gpio_unlock(ourchip, flags); }
static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); unsigned long val; val = __raw_readl(ourchip->base + 0x04); //将需要获取的位右移至最低位 val >>= offset; //获取最低位的值并返回 val &= 1; return val; }
###
....
}
##
}