diff options
author | Beniamino Galvani <b.galvani@gmail.com> | 2017-07-10 00:30:04 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2017-07-26 11:26:48 -0400 |
commit | 2009a8d03fe5aa660c4da04a731a4d75785d4b6b (patch) | |
tree | 1ad1615d5929c738856faa133522c3f1faa93e2d /drivers/pinctrl/meson/pinctrl-meson.c | |
parent | 4a63a75c8313613da8f11b928c296ed7e08a9d67 (diff) |
pinctrl: meson: add GPIO support
This commit adds GPIO support to the Amlogic Meson pin controller
driver, based on code from Linux kernel.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
Diffstat (limited to 'drivers/pinctrl/meson/pinctrl-meson.c')
-rw-r--r-- | drivers/pinctrl/meson/pinctrl-meson.c | 167 |
1 files changed, 165 insertions, 2 deletions
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 6281f529ea..a860200dfb 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -6,11 +6,14 @@ #include <common.h> #include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> #include <dm/pinctrl.h> #include <fdt_support.h> #include <linux/err.h> #include <linux/io.h> #include <linux/sizes.h> +#include <asm/gpio.h> #include "pinctrl-meson.h" @@ -117,6 +120,143 @@ const struct pinctrl_ops meson_pinctrl_ops = { .set_state = pinctrl_generic_set_state, }; +static int meson_gpio_calc_reg_and_bit(struct udevice *dev, unsigned int offset, + enum meson_reg_type reg_type, + unsigned int *reg, unsigned int *bit) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + struct meson_bank *bank = NULL; + struct meson_reg_desc *desc; + unsigned int pin; + int i; + + pin = priv->data->pin_base + offset; + + for (i = 0; i < priv->data->num_banks; i++) { + if (pin >= priv->data->banks[i].first && + pin <= priv->data->banks[i].last) { + bank = &priv->data->banks[i]; + break; + } + } + + if (!bank) + return -EINVAL; + + desc = &bank->regs[reg_type]; + *reg = desc->reg * 4; + *bit = desc->bit + pin - bank->first; + + return 0; +} + +static int meson_gpio_get(struct udevice *dev, unsigned int offset) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_IN, ®, &bit); + if (ret) + return ret; + + return !!(readl(priv->reg_gpio + reg) & BIT(bit)); +} + +static int meson_gpio_set(struct udevice *dev, unsigned int offset, int value) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_OUT, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), value ? BIT(bit) : 0); + + return 0; +} + +static int meson_gpio_get_direction(struct udevice *dev, unsigned int offset) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit, val; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DIR, ®, &bit); + if (ret) + return ret; + + val = readl(priv->reg_gpio + reg); + + return (val & BIT(bit)) ? GPIOF_INPUT : GPIOF_OUTPUT; +} + +static int meson_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DIR, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), 1); + + return 0; +} + +static int meson_gpio_direction_output(struct udevice *dev, + unsigned int offset, int value) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DIR, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), 0); + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_OUT, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), value ? BIT(bit) : 0); + + return 0; +} + +static int meson_gpio_probe(struct udevice *dev) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + struct gpio_dev_priv *uc_priv; + + uc_priv = dev_get_uclass_priv(dev); + uc_priv->bank_name = priv->data->name; + uc_priv->gpio_count = priv->data->num_pins; + + return 0; +} + +static const struct dm_gpio_ops meson_gpio_ops = { + .set_value = meson_gpio_set, + .get_value = meson_gpio_get, + .get_function = meson_gpio_get_direction, + .direction_input = meson_gpio_direction_input, + .direction_output = meson_gpio_direction_output, +}; + +static struct driver meson_gpio_driver = { + .name = "meson-gpio", + .id = UCLASS_GPIO, + .probe = meson_gpio_probe, + .ops = &meson_gpio_ops, +}; + static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) { int index, len = 0; @@ -138,9 +278,12 @@ static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) int meson_pinctrl_probe(struct udevice *dev) { struct meson_pinctrl *priv = dev_get_priv(dev); + struct uclass_driver *drv; + struct udevice *gpio_dev; fdt_addr_t addr; int node, gpio = -1, len; int na, ns; + char *name; na = fdt_address_cells(gd->fdt_blob, dev_of_offset(dev->parent)); if (na < 1) { @@ -168,12 +311,32 @@ int meson_pinctrl_probe(struct udevice *dev) addr = parse_address(gpio, "mux", na, ns); if (addr == FDT_ADDR_T_NONE) { - debug("mux not found\n"); + debug("mux address not found\n"); return -EINVAL; } - priv->reg_mux = (void __iomem *)addr; + + addr = parse_address(gpio, "gpio", na, ns); + if (addr == FDT_ADDR_T_NONE) { + debug("gpio address not found\n"); + return -EINVAL; + } + priv->reg_gpio = (void __iomem *)addr; priv->data = (struct meson_pinctrl_data *)dev_get_driver_data(dev); + /* Lookup GPIO driver */ + drv = lists_uclass_lookup(UCLASS_GPIO); + if (!drv) { + puts("Cannot find GPIO driver\n"); + return -ENOENT; + } + + name = calloc(1, 32); + sprintf(name, "meson-gpio"); + + /* Create child device UCLASS_GPIO and bind it */ + device_bind(dev, &meson_gpio_driver, name, NULL, gpio, &gpio_dev); + dev_set_of_offset(gpio_dev, gpio); + return 0; } |