Linux gpio驱动学习
首先我们来看一下gpio驱动框架吧
GPIO驱动的头文件为:include/linux/gpio.h和include/asm-generic/gpio.h。
GPIO驱动框架的实现代码为:drivers/gpio/gpiolib.c。
gpio操作增删改查架构:
/*
* Add a new chip to the global chips list, keeping the list of chips sorted
* by base order.
*
* Return -EBUSY if the new chip overlaps with some other chip's integer
* space.
*/
static int gpiochip_add_to_list(struct gpio_chip *chip)
{
struct list_head *pos;
struct gpio_chip *_chip;
int err = 0;
/* find where to insert our chip */
list_for_each(pos, &gpio_chips) {
_chip = list_entry(pos, struct gpio_chip, list);
/* shall we insert before _chip? */
if (_chip->base >= chip->base + chip->ngpio)
break;
}
/* are we stepping on the chip right before? */
if (pos != &gpio_chips && pos->prev != &gpio_chips) {
_chip = list_entry(pos->prev, struct gpio_chip, list);
if (_chip->base + _chip->ngpio > chip->base) {
dev_err(chip->dev,
"GPIO integer space overlap, cannot add chip\n");
err = -EBUSY;
}
}
if (!err)
list_add_tail(&chip->list, pos);
return err;
}
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;
struct gpio_desc *descs;
descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
if (!descs)
return -ENOMEM;
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_descs;
}
chip->base = base;
}
status = gpiochip_add_to_list(chip);
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_descs;
}
for (id = 0; id < chip->ngpio; id++) {
struct gpio_desc *desc = &descs[id];
desc->chip = chip;
/* REVISIT: most hardware initializes GPIOs as inputs (often
* with pullups enabled) so power usage is minimized. Linux
* code should set the gpio direction first thing; but until
* it does, and in case chip->get_direction is not set, we may
* expose the wrong direction in sysfs.
*/
desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
}
chip->desc = descs;
spin_unlock_irqrestore(&gpio_lock, flags);
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&chip->pin_ranges);
#endif
if (!chip->owner && chip->dev && chip->dev->driver)
chip->owner = chip->dev->driver->owner;
status = gpiochip_set_desc_names(chip);
if (status)
goto err_remove_from_list;
status = of_gpiochip_add(chip);
if (status)
goto err_remove_chip;
acpi_gpiochip_add(chip);
status = gpiochip_sysfs_register(chip);
if (status)
goto err_remove_chip;
pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return 0;
err_remove_chip:
acpi_gpiochip_remove(chip);
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
list_del(&chip->list);
spin_unlock_irqrestore(&gpio_lock, flags);
chip->desc = NULL;
err_free_descs:
kfree(descs);
/* failures here can mean systems won't boot... */
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);
/**
* gpiochip_remove() - unregister a gpio_chip
* @chip: the chip to unregister
*
* A gpio_chip with any GPIOs still requested may not be removed.
*/
void gpiochip_remove(struct gpio_chip *chip)
{
struct gpio_desc *desc;
unsigned long flags;
unsigned id;
bool requested = false;
gpiochip_sysfs_unregister(chip);
gpiochip_irqchip_remove(chip);
acpi_gpiochip_remove(chip);
gpiochip_remove_pin_ranges(chip);
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
spin_lock_irqsave(&gpio_lock, flags);
for (id = 0; id < chip->ngpio; id++) {
desc = &chip->desc[id];
desc->chip = NULL;
if (test_bit(FLAG_REQUESTED, &desc->flags))
requested = true;
}
list_del(&chip->list);
spin_unlock_irqrestore(&gpio_lock, flags);
if (requested)
dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
kfree(chip->desc);
chip->desc = NULL;
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
* @callback: Callback function to check gpio_chip
*
* Similar to bus_find_device. It returns a reference to a gpio_chip as
* determined by a user supplied @match callback. The callback should return
* 0 if the device doesn't match and non-zero if it does. If the callback is
* non-zero, this function will return to the caller and not iterate over any
* more gpio_chips.
*/
struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *chip,
void *data))
{
struct gpio_chip *chip;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(chip, &gpio_chips, list)
if (match(chip, data))
break;
/* No match? */
if (&chip->list == &gpio_chips)
chip = NULL;
spin_unlock_irqrestore(&gpio_lock, flags);
return chip;
}
EXPORT_SYMBOL_GPL(gpiochip_find);
static int gpiochip_match_name(struct gpio_chip *chip, void *data)
{
const char *name = data;
return !strcmp(chip->label, name);
}
static struct gpio_chip *find_chip_by_name(const char *name)
{
return gpiochip_find((void *)name, gpiochip_match_name);
}
gpio驱动输入输出设置,高低电平设置:
static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_g+
pio_regs *const regs = plat->regs;
setbits_le32(®s->outputenb_reset, 1 << pin);
return 0;
}
static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned pin,
int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
if (val)
setbits_le32(®s->data, 1 << pin);
else
setbits_le32(®s->pad_reset, 1 << pin);
setbits_le32(®s->outputenb, 1 << pin);
return 0;
}
static int nx_alive_gpio_get_value(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
unsigned int mask = 1UL << pin;
unsigned int value;
value = (readl(®s->pad_read) & mask) >> pin;
return value;
}
static int nx_alive_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->pad_reset, 1 << pin);
return 0;
}
static int nx_alive_gpio_get_function(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
unsigned int mask = (1UL << pin);
unsigned int output;
output = readl(®s->outputenb_read) & mask;
if (output)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
static int nx_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_input(dev, pin);
clrbits_le32(®s->outputenb, 1 << pin);
return 0;
}
static int nx_gpio_direction_output(struct udevice *dev, unsigned pin,
int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_output(dev, pin, val);
if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->data, 1 << pin);
setbits_le32(®s->outputenb, 1 << pin);
return 0;
}
static int nx_gpio_get_value(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
unsigned int mask = 1UL << pin;
unsigned int value;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_value(dev, pin);
value = (readl(®s->pad) & mask) >> pin;
return value;
}
static int nx_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_set_value(dev, pin, val);
if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->data, 1 << pin);
return 0;
}
static int nx_gpio_get_function(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
unsigned int mask = (1UL << pin);
unsigned int output;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_function(dev, pin);
output = readl(®s->outputenb) & mask;
if (output)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
还没有评论,来说两句吧...