[U-Boot] [PATCH 0/2] GPIO: CREG: improve flexibility of hsdk-creg-gpio driver

CREG GPIO is a driver for weird soc-specific output ports, which are controlled by some fields in memory mapped register.
Example:
31 9 7 5 0 < bit number | | | | | [ not used | gpio-1 | gpio-0 | <-shift-> ] < 32 bit register ^ ^ | | write 0x2 == set output to "1" (activate) write 0x3 == set output to "0" (deactivate)
As of tooday we only support fixed (hardcoded) bit per gpio line, activate / deactivatei and shift values. Fix that by read them from device tree to be able to use this driver for other boards.
Eugeniy Paltsev (2): GPIO: CREG: improve flexibility of hsdk-creg-gpio driver CREG GPIO: add device tree bindings
MAINTAINERS | 1 + arch/arc/dts/hsdk.dts | 7 +- doc/device-tree-bindings/gpio/snps,creg-gpio.txt | 43 ++++++++++ drivers/gpio/hsdk-creg-gpio.c | 103 ++++++++++++++++++----- 4 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 doc/device-tree-bindings/gpio/snps,creg-gpio.txt

CREG GPIO is a driver for weird soc-specific output ports, which are controlled by some fields in memory mapped register.
Example:
31 9 7 5 0 < bit number | | | | | [ not used | gpio-1 | gpio-0 | <-shift-> ] < 32 bit register ^ ^ | | write 0x2 == set output to "1" (activate) write 0x3 == set output to "0" (deactivate)
As of tooday we only support fixed (hardcoded) bit per gpio line, activate / deactivatei and shift values. Fix that by read them from device tree to be able to use this driver for other boards.
Remove "hsdk" prefix from compatible string as this driver can be used with different boards like HSDK, AXS101, AXS103, etc.
Signed-off-by: Eugeniy Paltsev Eugeniy.Paltsev@synopsys.com --- arch/arc/dts/hsdk.dts | 7 ++- drivers/gpio/hsdk-creg-gpio.c | 103 +++++++++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 23 deletions(-)
diff --git a/arch/arc/dts/hsdk.dts b/arch/arc/dts/hsdk.dts index 264512877e..e41e4ce84b 100644 --- a/arch/arc/dts/hsdk.dts +++ b/arch/arc/dts/hsdk.dts @@ -101,11 +101,16 @@ };
cs_gpio: gpio@f00014b0 { - compatible = "snps,hsdk-creg-gpio"; + compatible = "snps,creg-gpio"; reg = <0xf00014b0 0x4>; gpio-controller; #gpio-cells = <1>; gpio-bank-name = "hsdk-spi-cs"; gpio-count = <1>; + gpio-first-shift = <0>; + gpio-bit-per-line = <2>; + gpio-activate-val = <2>; + gpio-deactivate-val = <3>; + gpio-default-val = <1>; }; }; diff --git a/drivers/gpio/hsdk-creg-gpio.c b/drivers/gpio/hsdk-creg-gpio.c index 084a2da652..800027f18e 100644 --- a/drivers/gpio/hsdk-creg-gpio.c +++ b/drivers/gpio/hsdk-creg-gpio.c @@ -16,25 +16,24 @@ #include <errno.h> #include <linux/printk.h>
-#define HSDK_CREG_MAX_GPIO 8 - -#define GPIO_ACTIVATE 0x2 -#define GPIO_DEACTIVATE 0x3 -#define GPIO_PIN_MASK 0x3 -#define BIT_PER_GPIO 2 +#define DRV_NAME "gpio_creg"
struct hsdk_creg_gpio { - uint32_t *regs; + u32 *regs; + u8 shift; + u8 activate; + u8 deactivate; + u8 bit_per_gpio; };
static int hsdk_creg_gpio_set_value(struct udevice *dev, unsigned oft, int val) { struct hsdk_creg_gpio *hcg = dev_get_priv(dev); - uint32_t reg = readl(hcg->regs); - uint32_t cmd = val ? GPIO_DEACTIVATE : GPIO_ACTIVATE; + u8 reg_shift = oft * hcg->bit_per_gpio + hcg->shift; + u32 reg = readl(hcg->regs);
- reg &= ~(GPIO_PIN_MASK << (oft * BIT_PER_GPIO)); - reg |= (cmd << (oft * BIT_PER_GPIO)); + reg &= ~(GENMASK(hcg->bit_per_gpio - 1, 0) << reg_shift); + reg |= ((val ? hcg->deactivate : hcg->activate) << reg_shift);
writel(reg, hcg->regs);
@@ -51,7 +50,9 @@ static int hsdk_creg_gpio_direction_output(struct udevice *dev, unsigned oft,
static int hsdk_creg_gpio_direction_input(struct udevice *dev, unsigned oft) { - pr_err("hsdk-creg-gpio can't be used as input!\n"); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + pr_err("%s can't be used as input!\n", uc_priv->bank_name);
return -ENOTSUPP; } @@ -59,10 +60,11 @@ static int hsdk_creg_gpio_direction_input(struct udevice *dev, unsigned oft) static int hsdk_creg_gpio_get_value(struct udevice *dev, unsigned int oft) { struct hsdk_creg_gpio *hcg = dev_get_priv(dev); - uint32_t val = readl(hcg->regs); + u32 val = readl(hcg->regs);
- val = (val >> (oft * BIT_PER_GPIO)) & GPIO_PIN_MASK; - return (val == GPIO_DEACTIVATE) ? 1 : 0; + val >>= oft * hcg->bit_per_gpio + hcg->shift; + val &= GENMASK(hcg->bit_per_gpio - 1, 0); + return (val == hcg->deactivate) ? 1 : 0; }
static const struct dm_gpio_ops hsdk_creg_gpio_ops = { @@ -76,17 +78,74 @@ static int hsdk_creg_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct hsdk_creg_gpio *hcg = dev_get_priv(dev); + u32 shift, bit_per_gpio, activate, deactivate, gpio_count; + const u8 *defaults;
- hcg->regs = (uint32_t *)devfdt_get_addr_ptr(dev); - - uc_priv->gpio_count = dev_read_u32_default(dev, "gpio-count", 1); - if (uc_priv->gpio_count > HSDK_CREG_MAX_GPIO) - uc_priv->gpio_count = HSDK_CREG_MAX_GPIO; + hcg->regs = (u32 *)devfdt_get_addr_ptr(dev); + gpio_count = dev_read_u32_default(dev, "gpio-count", 1); + shift = dev_read_u32_default(dev, "gpio-first-shift", 0); + bit_per_gpio = dev_read_u32_default(dev, "gpio-bit-per-line", 1); + activate = dev_read_u32_default(dev, "gpio-activate-val", 1); + deactivate = dev_read_u32_default(dev, "gpio-deactivate-val", 0); + defaults = dev_read_u8_array_ptr(dev, "gpio-default-val", gpio_count);
uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name"); if (!uc_priv->bank_name) uc_priv->bank_name = dev_read_name(dev);
+ if (!bit_per_gpio) { + pr_err("%s: 'gpio-bit-per-line' can't be 0\n", + uc_priv->bank_name); + + return -EINVAL; + } + + if (!gpio_count) { + pr_err("%s: 'gpio-count' can't be 0\n", + uc_priv->bank_name); + + return -EINVAL; + } + + if ((gpio_count * bit_per_gpio + shift) > 32) { + pr_err("%s: u32 io register overflow: try to use %u bits\n", + uc_priv->bank_name, gpio_count * bit_per_gpio + shift); + + return -EINVAL; + } + + if (GENMASK(31, bit_per_gpio) & activate) { + pr_err("%s: 'gpio-activate-val' can't be more than %lu\n", + uc_priv->bank_name, GENMASK(bit_per_gpio - 1, 0)); + + return -EINVAL; + } + + if (GENMASK(31, bit_per_gpio) & deactivate) { + pr_err("%s: 'gpio-deactivate-val' can't be more than %lu\n", + uc_priv->bank_name, GENMASK(bit_per_gpio - 1, 0)); + + return -EINVAL; + } + + if (activate == deactivate) { + pr_err("%s: 'gpio-deactivate-val' and 'gpio-activate-val' can't be equal\n", + uc_priv->bank_name); + + return -EINVAL; + } + + hcg->shift = (u8)shift; + hcg->bit_per_gpio = (u8)bit_per_gpio; + hcg->activate = (u8)activate; + hcg->deactivate = (u8)deactivate; + uc_priv->gpio_count = gpio_count; + + /* Setup default GPIO value if we have "gpio-default-val" array */ + if (defaults) + for (u8 i = 0; i < gpio_count; i++) + hsdk_creg_gpio_set_value(dev, i, defaults[i]); + pr_debug("%s GPIO [0x%p] controller with %d gpios probed\n", uc_priv->bank_name, hcg->regs, uc_priv->gpio_count);
@@ -94,12 +153,12 @@ static int hsdk_creg_gpio_probe(struct udevice *dev) }
static const struct udevice_id hsdk_creg_gpio_ids[] = { - { .compatible = "snps,hsdk-creg-gpio" }, + { .compatible = "snps,creg-gpio" }, { } };
U_BOOT_DRIVER(gpio_hsdk_creg) = { - .name = "gpio_hsdk_creg", + .name = DRV_NAME, .id = UCLASS_GPIO, .ops = &hsdk_creg_gpio_ops, .probe = hsdk_creg_gpio_probe,

Signed-off-by: Eugeniy Paltsev Eugeniy.Paltsev@synopsys.com --- MAINTAINERS | 1 + doc/device-tree-bindings/gpio/snps,creg-gpio.txt | 43 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 doc/device-tree-bindings/gpio/snps,creg-gpio.txt
diff --git a/MAINTAINERS b/MAINTAINERS index 642c448093..0ea730e33c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -74,6 +74,7 @@ ARC HSDK CREG GPIO M: Eugeniy Paltsev Eugeniy.Paltsev@synopsys.com S: Maintained L: uboot-snps-arc@synopsys.com +F: doc/device-tree-bindings/gpio/snps,creg-gpio.txt F: drivers/gpio/hsdk-creg-gpio.c
ARM diff --git a/doc/device-tree-bindings/gpio/snps,creg-gpio.txt b/doc/device-tree-bindings/gpio/snps,creg-gpio.txt new file mode 100644 index 0000000000..46ceb65c53 --- /dev/null +++ b/doc/device-tree-bindings/gpio/snps,creg-gpio.txt @@ -0,0 +1,43 @@ +GPIO via CREG (control registers) driver + +31 9 7 5 0 < bit number +| | | | | +[ not used | gpio-1 | gpio-0 | <-shift-> ] < 32 bit register + ^ ^ + | | + write 0x2 == set output to "1" (activate) + write 0x3 == set output to "0" (deactivate) + +Required properties: +- compatible : "snps,creg-gpio" +- reg : Exactly one register range with length 0x4. +- #gpio-cells : Should be one - the pin number. +- gpio-controller : Marks the device node as a GPIO controller. +- gpio-count: Number of GPIO pins. +- gpio-bit-per-line: Number of bits per gpio line (see picture). +- gpio-first-shift: Shift (in bits) of the first GPIO field in register + (see picture). +- gpio-activate-val: Value should be set in corresponding field to set + output to "1" (see picture). Applied to all GPIO ports. +- gpio-deactivate-val: Value should be set in corresponding field to set + output to "0" (see picture). Applied to all GPIO ports. + +Optional properties: +- gpio-bank-name: name of bank (as default driver name is used is used) +- gpio-default-val: array of default output values (must me 0 or 1) + +Example (see picture): + +gpio: gpio@f00014b0 { + compatible = "snps,creg-gpio"; + reg = <0xf00014b0 0x4>; + gpio-controller; + #gpio-cells = <1>; + gpio-bank-name = "hsdk-spi-cs"; + gpio-count = <2>; + gpio-first-shift = <5>; + gpio-bit-per-line = <2>; + gpio-activate-val = <2>; + gpio-deactivate-val = <3>; + gpio-default-val = <1 1>; +};
participants (1)
-
Eugeniy Paltsev