Hi, On Sun, Oct 5, 2014 at 1:29 AM, Simon Glass <s...@chromium.org> wrote: > This adds driver model support to the sunxi GPIO driver, using the device > tree to trigger binding of the driver. The driver will still operate > without driver model too. > > Signed-off-by: Simon Glass <s...@chromium.org> > --- > > drivers/gpio/sunxi_gpio.c | 170 > ++++++++++++++++++++++++++++++++++++++++++++++ > include/configs/sun7i.h | 1 + > 2 files changed, 171 insertions(+) > > diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c > index 0c50a8f..f633c04 100644 > --- a/drivers/gpio/sunxi_gpio.c > +++ b/drivers/gpio/sunxi_gpio.c > @@ -11,9 +11,25 @@ > */ > > #include <common.h> > +#include <dm.h> > +#include <errno.h> > +#include <fdtdec.h> > +#include <malloc.h> > #include <asm/io.h> > #include <asm/gpio.h> > +#include <dm/device-internal.h> > > +DECLARE_GLOBAL_DATA_PTR; > + > +#define SUNXI_GPIOS_PER_PORT SUNXI_GPIO_A_NR > + > +struct sunxi_gpio_platdata { > + struct sunxi_gpio *regs; > + const char *bank_name; /* Name of port, e.g. "B" */ > + int gpio_count; > +}; > + > +#ifndef CONFIG_DM_GPIO > static int sunxi_gpio_output(u32 pin, u32 val) > { > u32 dat; > @@ -100,3 +116,157 @@ int sunxi_name_to_gpio(const char *name) > return -1; > return group * 32 + pin; > } > +#endif > + > +#ifdef CONFIG_DM_GPIO > +static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset) > +{ > + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); > + > + sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); > + > + return 0; > +} > + > +static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset, > + int value) > +{ > + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); > + u32 num = GPIO_NUM(offset); > + > + sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); > + clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); > + > + return 0; > +} > + > +static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) > +{ > + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); > + u32 num = GPIO_NUM(offset); > + unsigned dat; > + > + dat = readl(&plat->regs->dat); > + dat >>= num; > + > + return dat & 0x1; > +} > + > +static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset, > + int value) > +{ > + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); > + u32 num = GPIO_NUM(offset); > + > + clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); > + return 0; > +} > + > +static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) > +{ > + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); > + int func; > + > + func = sunxi_gpio_get_cfgbank(plat->regs, offset); > + if (func == SUNXI_GPIO_OUTPUT) > + return GPIOF_OUTPUT; > + else if (func == SUNXI_GPIO_INPUT) > + return GPIOF_INPUT; > + else > + return GPIOF_FUNC; > +} > + > +static const struct dm_gpio_ops gpio_sunxi_ops = { > + .direction_input = sunxi_gpio_direction_input, > + .direction_output = sunxi_gpio_direction_output, > + .get_value = sunxi_gpio_get_value, > + .set_value = sunxi_gpio_set_value, > + .get_function = sunxi_gpio_get_function, > +}; > + > +/** > + * Returns the name of a GPIO bank > + * > + * GPIO banks are named A, B, C, ... > + * > + * @bank: Bank number (0, 1..n-1) > + * @return allocated string containing the name > + */ > +static char *gpio_bank_name(int bank) > +{ > + char *name; > + > + name = malloc(2); > + if (name) { > + name[0] = 'A' + bank; > + name[1] = '\0'; > + } > + > + return name; > +} > + > +static int gpio_sunxi_probe(struct udevice *dev) > +{ > + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); > + struct gpio_dev_priv *uc_priv = dev->uclass_priv; > + > + /* Tell the uclass how many GPIOs we have */ > + if (plat) { > + uc_priv->gpio_count = plat->gpio_count; > + uc_priv->bank_name = plat->bank_name; > + } > + > + return 0; > +} > +/** > + * We have a top-level GPIO device with no actual GPIOs. It has a child > + * device for each Tegra port.
Might we get rid of references to Tegra? > + */ > +static int gpio_sunxi_bind(struct udevice *parent) > +{ > + struct sunxi_gpio_platdata *plat = parent->platdata; > + struct sunxi_gpio_reg *ctlr; > + int bank; > + int ret; > + > + /* If this is a child device, there is nothing to do here */ > + if (plat) > + return 0; > + > + ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob, > + parent->of_offset, "reg"); > + for (bank = 0; bank < SUNXI_GPIO_BANKS; bank++) { > + struct sunxi_gpio_platdata *plat; > + struct udevice *dev; > + > + plat = calloc(1, sizeof(*plat)); > + if (!plat) > + return -ENOMEM; > + plat->regs = &ctlr->gpio_bank[bank]; > + plat->bank_name = gpio_bank_name(bank); > + plat->gpio_count = SUNXI_GPIOS_PER_PORT; > + > + ret = device_bind(parent, parent->driver, > + plat->bank_name, plat, -1, &dev); > + if (ret) > + return ret; > + dev->of_offset = parent->of_offset; > + } > + > + return 0; > +} > + > +static const struct udevice_id exynos_gpio_ids[] = { And Exynos? > + { .compatible = "allwinner,sun7i-a20-pinctrl" }, > + { } > +}; > + > +U_BOOT_DRIVER(gpio_sunxi) = { > + .name = "gpio_sunxi", > + .id = UCLASS_GPIO, > + .ops = &gpio_sunxi_ops, > + .of_match = exynos_gpio_ids, > + .bind = gpio_sunxi_bind, > + .probe = gpio_sunxi_probe, > +}; > +#endif > diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h > index 500d0e3..2314e97 100644 > --- a/include/configs/sun7i.h > +++ b/include/configs/sun7i.h > @@ -38,6 +38,7 @@ > > #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_DM) > # define CONFIG_CMD_DM > +# define CONFIG_DM_GPIO > #endif > > /* > -- ChenYu _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot