[U-Boot] [PATCH 0/3] Extend pinctrl-single driver with APIs

This patch set adds APIs for pinctrl-single driver.
1. Support to use different register read/write api's based on register width.
2. pinctrl_ops->request api to configure pctrl pad register in gpio mode.
3. Parse different gpio properties from dt as part of the probe function. This is required to enable pinctrl pad.
Rayagonda Kokatanur (3): pinctrl: pinctrl-single: Handle different register width pinctrl: pinctrl-single: Add request api pinctrl: pinctrl-single: Parse gpio details from dt
drivers/pinctrl/pinctrl-single.c | 200 ++++++++++++++++++++++++++----- 1 file changed, 169 insertions(+), 31 deletions(-)

From: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
Add support to use different register read/write api's based on register width.
Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com Signed-off-by: Vladimir Olovyannikov vladimir.olovyannikov@broadcom.com --- drivers/pinctrl/pinctrl-single.c | 111 ++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 31 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 1dfc97dcea..6c6a33e4c5 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -11,12 +11,23 @@
DECLARE_GLOBAL_DATA_PTR;
+/** + * struct single_pdata - pinctrl device instance + * @base first configuration register + * @offset index of last configuration register + * @mask configuration-value mask bits + * @width configuration register bit width + * @read register read function to use + * @write register write function to use + */ struct single_pdata { - fdt_addr_t base; /* first configuration register */ - int offset; /* index of last configuration register */ - u32 mask; /* configuration-value mask bits */ - int width; /* configuration register bit width */ + void __iomem *base; + int offset; + u32 mask; + int width; bool bits_per_mux; + u32 (*read)(void __iomem *reg); + void (*write)(u32 val, void __iomem *reg); };
struct single_fdt_pin_cfg { @@ -30,6 +41,36 @@ struct single_fdt_bits_cfg { fdt32_t mask; /* configuration register mask */ };
+static u32 __maybe_unused single_readb(void __iomem *reg) +{ + return readb(reg); +} + +static u32 __maybe_unused single_readw(void __iomem *reg) +{ + return readw(reg); +} + +static u32 __maybe_unused single_readl(void __iomem *reg) +{ + return readl(reg); +} + +static void __maybe_unused single_writeb(u32 val, void __iomem *reg) +{ + writeb(val, reg); +} + +static void __maybe_unused single_writew(u32 val, void __iomem *reg) +{ + writew(val, reg); +} + +static void __maybe_unused single_writel(u32 val, void __iomem *reg) +{ + writel(val, reg); +} + /** * single_configure_pins() - Configure pins based on FDT data * @@ -55,24 +96,15 @@ static int single_configure_pins(struct udevice *dev,
for (n = 0; n < count; n++, pins++) { reg = fdt32_to_cpu(pins->reg); - if ((reg < 0) || (reg > pdata->offset)) { + if (reg > pdata->offset) { dev_dbg(dev, " invalid register offset 0x%pa\n", ®); continue; } - reg += pdata->base; + reg += (phys_addr_t)pdata->base; val = fdt32_to_cpu(pins->val) & pdata->mask; - switch (pdata->width) { - case 16: - writew((readw(reg) & ~pdata->mask) | val, reg); - break; - case 32: - writel((readl(reg) & ~pdata->mask) | val, reg); - break; - default: - dev_warn(dev, "unsupported register width %i\n", - pdata->width); - continue; - } + val |= pdata->read((void __iomem *)reg) & ~pdata->mask; + pdata->write(val, (void __iomem *)reg); + dev_dbg(dev, " reg/val 0x%pa/0x%08x\n", ®, val); } return 0; @@ -97,19 +129,9 @@ static int single_configure_bits(struct udevice *dev,
mask = fdt32_to_cpu(pins->mask); val = fdt32_to_cpu(pins->val) & mask; + val |= pdata->read((void __iomem *)reg) & ~mask; + pdata->write(val, (void __iomem *)reg);
- switch (pdata->width) { - case 16: - writew((readw(reg) & ~mask) | val, reg); - break; - case 32: - writel((readl(reg) & ~mask) | val, reg); - break; - default: - dev_warn(dev, "unsupported register width %i\n", - pdata->width); - continue; - } dev_dbg(dev, " reg/val 0x%pa/0x%08x\n", ®, val); } return 0; @@ -153,6 +175,32 @@ static int single_set_state(struct udevice *dev, return len; }
+static int single_probe(struct udevice *dev) +{ + struct single_pdata *pdata = dev->platdata; + + switch (pdata->width) { + case 8: + pdata->read = single_readb; + pdata->write = single_writeb; + break; + case 16: + pdata->read = single_readw; + pdata->write = single_writew; + break; + case 32: + pdata->read = single_readl; + pdata->write = single_writel; + break; + default: + dev_warn(dev, "%s: unsupported register width %d\n", + __func__, pdata->width); + return -EINVAL; + } + + return 0; +} + static int single_ofdata_to_platdata(struct udevice *dev) { fdt_addr_t addr; @@ -174,7 +222,7 @@ static int single_ofdata_to_platdata(struct udevice *dev) dev_dbg(dev, "no valid base register address\n"); return -EINVAL; } - pdata->base = addr; + pdata->base = (void __iomem *)addr;
pdata->mask = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "pinctrl-single,function-mask", @@ -201,4 +249,5 @@ U_BOOT_DRIVER(single_pinctrl) = { .ops = &single_pinctrl_ops, .platdata_auto_alloc_size = sizeof(struct single_pdata), .ofdata_to_platdata = single_ofdata_to_platdata, + .probe = single_probe, };

From: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
Add pinctrl_ops->request api to configure pctrl pad register to configure in gpio mode.
Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com Signed-off-by: Vladimir Olovyannikov vladimir.olovyannikov@broadcom.com --- drivers/pinctrl/pinctrl-single.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 6c6a33e4c5..2dcc131513 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -136,6 +136,35 @@ static int single_configure_bits(struct udevice *dev, } return 0; } + +static int single_request(struct udevice *dev, int pin, int flags) +{ + struct single_pdata *pdata = dev->platdata; + struct single_gpiofunc_range *frange = NULL; + struct list_head *pos, *tmp; + int mux_bytes = 0; + u32 data; + + if (!pdata->mask) + return -ENOTSUPP; + + list_for_each_safe(pos, tmp, &pdata->gpiofuncs) { + frange = list_entry(pos, struct single_gpiofunc_range, node); + if ((pin >= frange->offset + frange->npins) || + pin < frange->offset) + continue; + + mux_bytes = pdata->width / BITS_PER_BYTE; + data = pdata->read(pdata->base + pin * mux_bytes); + data &= ~pdata->mask; + data |= frange->gpiofunc; + pdata->write(data, pdata->base + pin * mux_bytes); + break; + } + + return 0; +} + static int single_set_state(struct udevice *dev, struct udevice *config) { @@ -235,6 +264,7 @@ static int single_ofdata_to_platdata(struct udevice *dev)
const struct pinctrl_ops single_pinctrl_ops = { .set_state = single_set_state, + .request = single_request, };
static const struct udevice_id single_pinctrl_match[] = {

From: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com
Parse different gpio properties from dt as part of probe function. This detail is required to enable pinctrl pad.
Signed-off-by: Rayagonda Kokatanur rayagonda.kokatanur@broadcom.com Signed-off-by: Vladimir Olovyannikov vladimir.olovyannikov@broadcom.com --- drivers/pinctrl/pinctrl-single.c | 61 +++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-)
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 2dcc131513..449e8a2bfe 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -5,18 +5,35 @@
#include <common.h> #include <dm.h> +#include <dm/of_access.h> #include <dm/pinctrl.h> #include <linux/libfdt.h> #include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
+/** + * struct single_gpiofunc_range - pin ranges with same mux value of gpio fun + * @offset: offset base of pins + * @npins: number pins with the same mux value of gpio function + * @gpiofunc: mux value of gpio function + * @node: list node + */ +struct single_gpiofunc_range { + u32 offset; + u32 npins; + u32 gpiofunc; + struct list_head node; +}; + /** * struct single_pdata - pinctrl device instance * @base first configuration register * @offset index of last configuration register * @mask configuration-value mask bits * @width configuration register bit width + * @mutex mutex protecting the list + * @gpiofuncs list of gpio functions * @read register read function to use * @write register write function to use */ @@ -26,6 +43,8 @@ struct single_pdata { u32 mask; int width; bool bits_per_mux; + struct mutex mutex; + struct list_head gpiofuncs; u32 (*read)(void __iomem *reg); void (*write)(u32 val, void __iomem *reg); }; @@ -204,9 +223,42 @@ static int single_set_state(struct udevice *dev, return len; }
+static int single_add_gpio_func(struct udevice *dev, + struct single_pdata *pdata) +{ + const char *propname = "pinctrl-single,gpio-range"; + const char *cellname = "#pinctrl-single,gpio-range-cells"; + struct single_gpiofunc_range *range; + struct ofnode_phandle_args gpiospec; + int ret, i; + + for (i = 0; ; i++) { + ret = ofnode_parse_phandle_with_args(dev->node, propname, + cellname, 0, i, &gpiospec); + /* Do not treat it as error. Only treat it as end condition. */ + if (ret) { + ret = 0; + break; + } + range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL); + if (!range) { + ret = -ENOMEM; + break; + } + range->offset = gpiospec.args[0]; + range->npins = gpiospec.args[1]; + range->gpiofunc = gpiospec.args[2]; + mutex_lock(&pdata->mutex); + list_add_tail(&range->node, &pdata->gpiofuncs); + mutex_unlock(&pdata->mutex); + } + return ret; +} + static int single_probe(struct udevice *dev) { struct single_pdata *pdata = dev->platdata; + int ret;
switch (pdata->width) { case 8: @@ -227,7 +279,14 @@ static int single_probe(struct udevice *dev) return -EINVAL; }
- return 0; + mutex_init(&pdata->mutex); + INIT_LIST_HEAD(&pdata->gpiofuncs); + + ret = single_add_gpio_func(dev, pdata); + if (ret < 0) + dev_err(dev, "%s: Failed to add gpio functions\n", __func__); + + return ret; }
static int single_ofdata_to_platdata(struct udevice *dev)
participants (1)
-
Vladimir Olovyannikov