Message ID | 20201011024831.3868571-4-daniel@0x0f.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add GPIO support for MStar/SigmaStar ARMv7 | expand |
Hi Daniel, thanks for your patch! Some comments below, we need some work but keep at it. On Sun, Oct 11, 2020 at 4:48 AM Daniel Palmer <daniel@0x0f.com> wrote: > This adds a driver that supports the GPIO block found in > MStar/SigmaStar ARMv7 SoCs. > > The controller seems to support 128 lines but where they > are wired up differs between chips and no currently known > chip uses anywhere near 128 lines so there needs to be some > per-chip data to collect together what lines actually have > physical pins attached and map the right names to them. > > The core peripherals seem to use the same lines on the > currently known chips but the lines used for the sensor > interface, lcd controller etc pins seem to be totally > different between the infinity and mercury chips > > The code tries to collect all of the re-usable names, > offsets etc together so that it's easy to build the extra > per-chip data for other chips in the future. > > So far this only supports the MSC313 and MSC313E chips. > > Support for the SSC8336N (mercury5) is trivial to add once > all of the lines have been mapped out. > > Signed-off-by: Daniel Palmer <daniel@0x0f.com> (...) > +config GPIO_MSC313 > + bool "MStar MSC313 GPIO support" > + default y if ARCH_MSTARV7 > + depends on ARCH_MSTARV7 > + select GPIO_GENERIC Selecting GPIO_GENERIC, that is good. But you're not using it, because you can't. This chip does not have the bits lined up nicely in one register, instead there seems to be something like one register per line, right? So skip GPIO_GENERIC. > +#define MSC313_GPIO_IN BIT(0) > +#define MSC313_GPIO_OUT BIT(4) > +#define MSC313_GPIO_OEN BIT(5) > + > +#define MSC313_GPIO_BITSTOSAVE (MSC313_GPIO_OUT | MSC313_GPIO_OEN) Some comment here telling us why these need saving and not others. > +#define FUART_NAMES \ > + MSC313_PINNAME_FUART_RX, \ > + MSC313_PINNAME_FUART_TX, \ > + MSC313_PINNAME_FUART_CTS, \ > + MSC313_PINNAME_FUART_RTS > + > +#define OFF_FUART_RX 0x50 > +#define OFF_FUART_TX 0x54 > +#define OFF_FUART_CTS 0x58 > +#define OFF_FUART_RTS 0x5c > + > +#define FUART_OFFSETS \ > + OFF_FUART_RX, \ > + OFF_FUART_TX, \ > + OFF_FUART_CTS, \ > + OFF_FUART_RTS This looks a bit strange. The GPIO driver should not really have to know about any other use cases for pins than GPIO. But I guess it is intuitive for the driver. > +#define SD_NAMES \ > + MSC313_PINNAME_SD_CLK, \ > + MSC313_PINNAME_SD_CMD, \ > + MSC313_PINNAME_SD_D0, \ > + MSC313_PINNAME_SD_D1, \ > + MSC313_PINNAME_SD_D2, \ > + MSC313_PINNAME_SD_D3 > + > +#define OFF_SD_CLK 0x140 > +#define OFF_SD_CMD 0x144 > +#define OFF_SD_D0 0x148 > +#define OFF_SD_D1 0x14cchild_to_parent_hwirq > +#define OFF_SD_D2 0x150 > +#define OFF_SD_D3 0x154 > + > +#define SD_OFFSETS \ > + OFF_SD_CLK, \ > + OFF_SD_CMD, \ > + OFF_SD_D0, \ > + OFF_SD_D1, \ > + OFF_SD_D2, \ > + OFF_SD_D3 > + > +#define I2C1_NAMES \ > + MSC313_PINNAME_I2C1_SCL, \ > + MSC313_PINNAME_I2C1_SCA > + > +#define OFF_I2C1_SCL 0x188 > +#define OFF_I2C1_SCA 0x18c > + > +#define I2C1_OFFSETS \ > + OFF_I2C1_SCL, \ > + OFF_I2C1_SCA > + > +#define SPI0_NAMES \ > + MSC313_PINNAME_SPI0_CZ, \ > + MSC313_PINNAME_SPI0_CK, \ > + MSC313_PINNAME_SPI0_DI, \ > + MSC313_PINNAME_SPI0_DO > + > +#define OFF_SPI0_CZ 0x1c0 > +#define OFF_SPI0_CK 0x1c4 > +#define OFF_SPI0_DI 0x1c8 > +#define OFF_SPI0_DO 0x1cc > + > +#define SPI0_OFFSETS \ > + OFF_SPI0_CZ, \ > + OFF_SPI0_CK, \ > + OFF_SPI0_DI, \ > + OFF_SPI0_DO Same with all these. I suppose it is the offsets of stuff that would be there unless we were using it for GPIO. > +static int msc313_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) > +{ > + struct msc313_gpio *gpio = gpiochip_get_data(chip); > +> + > + return gpio->irqs[offset]; > +} Please do not use custom IRQ handling like this. As there seems to be one IRQ per line, look into using select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY See for example in gpio-ixp4xx.c how we deal with hiearchical GPIO IRQs. > + gpiochip->to_irq = msc313_gpio_to_irq; > + gpiochip->base = -1; > + gpiochip->ngpio = gpio->gpio_data->num; > + gpiochip->names = gpio->gpio_data->names; > + > + for (i = 0; i < gpiochip->ngpio; i++) > + gpio->irqs[i] = of_irq_get_byname(pdev->dev.of_node, gpio->gpio_data->names[i]); Use hierarchical generic GPIO IRQs for these. Assign ->fwnode, ->parent_domain, ->child_to_parent_hwirq, and probably also ->handler on the struct gpio_irq_chip *. Skip assigning gpiochip->to_irq, the generic code will handle that. Again see gpio-ixp4xx.c for an example. Yours, Linus Walleij
Hi Linus On Sat, 17 Oct 2020 at 01:56, Linus Walleij <linus.walleij@linaro.org> wrote: > (...) > > > +config GPIO_MSC313 > > + bool "MStar MSC313 GPIO support" > > + default y if ARCH_MSTARV7 > > + depends on ARCH_MSTARV7 > > + select GPIO_GENERIC > > Selecting GPIO_GENERIC, that is good. > But you're not using it, because you can't. > This chip does not have the bits lined up nicely > in one register, instead there seems to be something > like one register per line, right? > So skip GPIO_GENERIC. Well spotted. Copy/paste fail on my side :). > > +#define MSC313_GPIO_IN BIT(0) > > +#define MSC313_GPIO_OUT BIT(4) > > +#define MSC313_GPIO_OEN BIT(5) > > + > > +#define MSC313_GPIO_BITSTOSAVE (MSC313_GPIO_OUT | MSC313_GPIO_OEN) > > Some comment here telling us why these need saving and > not others. There is a comment near to the save function that explains it I think. When the hardware goes into low power mode with the CPU turned off the register contents are lost and those two bits are the only ones that are writable from what I can tell. I'll add an extra comment above that line. > > +#define FUART_NAMES \ > > + MSC313_PINNAME_FUART_RX, \ > > + MSC313_PINNAME_FUART_TX, \ > > + MSC313_PINNAME_FUART_CTS, \ > > + MSC313_PINNAME_FUART_RTS > > + > > +#define OFF_FUART_RX 0x50 > > +#define OFF_FUART_TX 0x54 > > +#define OFF_FUART_CTS 0x58 > > +#define OFF_FUART_RTS 0x5c > > + > > +#define FUART_OFFSETS \ > > + OFF_FUART_RX, \ > > + OFF_FUART_TX, \ > > + OFF_FUART_CTS, \ > > + OFF_FUART_RTS > > This looks a bit strange. The GPIO driver should not really > have to know about any other use cases for pins than > GPIO. But I guess it is intuitive for the driver. > <snip> > > Same with all these. I suppose it is the offsets of stuff > that would be there unless we were using it for GPIO. The pad FUART_RX can't move but the function FUART_RX can. If the function FUART_RX (or another function) isn't on the pad/pin FUART_RX it's connected to the GPIO block. Even more confusingly some of the other chips (SSD201/SSD202) have pads called GPIO1, GPIO2 etc that only have GPIO functionality but the offsets of the registers to control the GPIO on those pads might not have a relation to the name. GPIO1 isn't gpio_base + (1 * 4) and instead some random address. Basically using the pad name as the name of the GPIO made sense because it's fixed and the pad name and offset are the same with all of the chips I've seen so far. > > +static int msc313_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) > > +{ > > + struct msc313_gpio *gpio = gpiochip_get_data(chip); > > +> + > > > + return gpio->irqs[offset]; > > +} > > Please do not use custom IRQ handling like this. > As there seems to be one IRQ per line, look into using > > select GPIOLIB_IRQCHIP > select IRQ_DOMAIN_HIERARCHY > > See for example in gpio-ixp4xx.c how we deal with > hiearchical GPIO IRQs. <snip> > Use hierarchical generic GPIO IRQs for these. > > Assign ->fwnode, ->parent_domain, ->child_to_parent_hwirq, > and probably also ->handler on the struct gpio_irq_chip *. > > Skip assigning gpiochip->to_irq, the generic code will > handle that. > > Again see gpio-ixp4xx.c for an example. I'll look into this. I don't have datasheets so I'm working from some crusty header files from the vendor kernel but there isn't one irq per line from what I can tell. There seems to have been 4 spare lines on an interrupt controller so they wired GPIOs to them. Thank you for the comments. I'll send a v2 in a few days. Thanks, Daniel
Hi Linus, Sorry to pester you again... On Sat, 17 Oct 2020 at 01:56, Linus Walleij <linus.walleij@linaro.org> wrote: > > + gpiochip->to_irq = msc313_gpio_to_irq; > > + gpiochip->base = -1; > > + gpiochip->ngpio = gpio->gpio_data->num; > > + gpiochip->names = gpio->gpio_data->names; > > + > > + for (i = 0; i < gpiochip->ngpio; i++) > > + gpio->irqs[i] = of_irq_get_byname(pdev->dev.of_node, gpio->gpio_data->names[i]); > > Use hierarchical generic GPIO IRQs for these. > > Assign ->fwnode, ->parent_domain, ->child_to_parent_hwirq, > and probably also ->handler on the struct gpio_irq_chip *. > > Skip assigning gpiochip->to_irq, the generic code will > handle that. > > Again see gpio-ixp4xx.c for an example. I sent a v2 with this conversion already and it looks a lot better. Based on Andy Shevchenko's comments[0] I'll be sending a v3 that fixes up all style and other issues he found. Before I do that I have a question that maybe you could help me with: Andy noted a few times that I have this driver as a built in driver and not a module. The gpio-ixp4xx.c driver is also a built in driver. Is there a reason why it's ok there but not this driver? I've actually changed it to allow building as a module already but I don't want to push a v3 if something like the interrupt handling means it should actually be a built in and I'm just missing something. Thanks, Daniel 0 - https://lore.kernel.org/linux-gpio/CAHp75Vf5iUzKp32CqBbv_5MRo8q8CyBPsBcgzKsww6BFtGJwUA@mail.gmail.com/
On Wed, Oct 21, 2020 at 1:07 PM Daniel Palmer <daniel@0x0f.com> wrote: > Sorry to pester you again... Don't worry. I'm more worried that my replies are slow. > Before I do that I have a question that maybe you could help me with: > Andy noted a few times that I have this driver as a built in driver > and not a module. > The gpio-ixp4xx.c driver is also a built in driver. Is there a reason > why it's ok there but not this driver? Not that I know of. There is a lot of push for modularization right now because Android (and other distributions) likes it, so if your SoC could be used by Android or Fedora or Debian etc it is generally a good idea to modularize. These distributions use the generic ARM (etc) kernel and try to load as many drivers as possible as modules. It is not always possible because some GPIOs might be needed very early, such as on-chip GPIO. So you better make sure that the platform can get to userspace also without this driver compiled in, otherwise it *MUST* be bool so people don't get ammunition to shoot themselves in the foot and configure a non-bootable kernel just because they could modularize this driver. If your SoC is only used by OpenWrt (like ixp4xx) then it is fine to just use bool because that distribution is always built with an image for a specific hardware, whereas distributions are generic. So it actually depends a bit on the usecase of the SoC. Yours, Linus Walleij
On Thu, Nov 05, 2020 at 10:21:27AM +0100, Linus Walleij wrote: > If your SoC is only used by OpenWrt (like ixp4xx) then it is fine > to just use bool because that distribution is always built with an > image for a specific hardware, whereas distributions are generic. Speaking for myself (since I have a few now), I'm not running OpenWRT on mine but my own distro, and I guess most users will run either Buildroot or their own distro. It's unlikely that we'll see very generic distros there given the limited storage you'd typically have in an SPI NOR (16-32 MB) and the small RAM (64MB) which tends to discourage anyone from booting a regular distro over other storage anyway. Thus my guess is that most users will keep building their own kernels. But this just emphasizes your points :-) Just my two cents, Willy
On Thu, Nov 5, 2020 at 10:31 AM Willy Tarreau <w@1wt.eu> wrote: > On Thu, Nov 05, 2020 at 10:21:27AM +0100, Linus Walleij wrote: > > If your SoC is only used by OpenWrt (like ixp4xx) then it is fine > > to just use bool because that distribution is always built with an > > image for a specific hardware, whereas distributions are generic. > > Speaking for myself (since I have a few now), I'm not running OpenWRT > on mine but my own distro, and I guess most users will run either > Buildroot or their own distro. It's unlikely that we'll see very > generic distros there given the limited storage you'd typically have > in an SPI NOR (16-32 MB) and the small RAM (64MB) which tends to > discourage anyone from booting a regular distro over other storage > anyway. > > Thus my guess is that most users will keep building their own kernels. > > But this just emphasizes your points :-) I think that is a good argument to keep this as bool. Yours, Linus Walleij
Hi Linus, Thanks for all of the comments. On Thu, 5 Nov 2020 at 18:42, Linus Walleij <linus.walleij@linaro.org> wrote: > > On Thu, Nov 5, 2020 at 10:31 AM Willy Tarreau <w@1wt.eu> wrote: > > On Thu, Nov 05, 2020 at 10:21:27AM +0100, Linus Walleij wrote: > > > > If your SoC is only used by OpenWrt (like ixp4xx) then it is fine > > > to just use bool because that distribution is always built with an > > > image for a specific hardware, whereas distributions are generic. > > .. snip .. >> It's unlikely that we'll see very > > generic distros there given the limited storage you'd typically have > > in an SPI NOR (16-32 MB) and the small RAM (64MB) which tends to > > discourage anyone from booting a regular distro over other storage > > anyway. > > > > Thus my guess is that most users will keep building their own kernels. > > > > But this just emphasizes your points :-) > > I think that is a good argument to keep this as bool. Thanks. I did change it to a tristate for v3 but I'll change it back. Just a heads up: There is another GPIO driver for this chip (same functionality, totally different register layout for no reason) that'll look pretty similar to this that'll follow soon. It might be similar enough that people confuse the two series as the same thing. Thanks, Daniel
diff --git a/MAINTAINERS b/MAINTAINERS index ec5b49b9955f..d20e8935dd4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2158,6 +2158,7 @@ F: Documentation/devicetree/bindings/arm/mstar/* F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml F: arch/arm/boot/dts/mstar-* F: arch/arm/mach-mstar/ +F: drivers/gpio/gpio-msc313.c F: include/dt-bindings/gpio/msc313-gpio.h ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8030fd91a3cc..d85226cb2a07 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -712,6 +712,15 @@ config GPIO_AMD_FCH Note: This driver doesn't registers itself automatically, as it needs to be provided with platform specific configuration. (See eg. CONFIG_PCENGINES_APU2.) + +config GPIO_MSC313 + bool "MStar MSC313 GPIO support" + default y if ARCH_MSTARV7 + depends on ARCH_MSTARV7 + select GPIO_GENERIC + help + Say Y here to support GPIO on MStar MSC313 and later SoCs. + endmenu menu "Port-mapped I/O GPIO drivers" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4f9abff4f2dc..7c675b502cc4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o +obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c new file mode 100644 index 000000000000..6bdab77674ae --- /dev/null +++ b/drivers/gpio/gpio-msc313.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Daniel Palmer<daniel@thingy.jp> + */ + +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/gpio/driver.h> + +#include <dt-bindings/gpio/msc313-gpio.h> + +#define DRIVER_NAME "gpio-msc313" + +#define MSC313_GPIO_IN BIT(0) +#define MSC313_GPIO_OUT BIT(4) +#define MSC313_GPIO_OEN BIT(5) + +#define MSC313_GPIO_BITSTOSAVE (MSC313_GPIO_OUT | MSC313_GPIO_OEN) + +#define FUART_NAMES \ + MSC313_PINNAME_FUART_RX, \ + MSC313_PINNAME_FUART_TX, \ + MSC313_PINNAME_FUART_CTS, \ + MSC313_PINNAME_FUART_RTS + +#define OFF_FUART_RX 0x50 +#define OFF_FUART_TX 0x54 +#define OFF_FUART_CTS 0x58 +#define OFF_FUART_RTS 0x5c + +#define FUART_OFFSETS \ + OFF_FUART_RX, \ + OFF_FUART_TX, \ + OFF_FUART_CTS, \ + OFF_FUART_RTS + +#define SR_NAMES \ + MSC313_PINNAME_SR_IO2, \ + MSC313_PINNAME_SR_IO3, \ + MSC313_PINNAME_SR_IO4, \ + MSC313_PINNAME_SR_IO5, \ + MSC313_PINNAME_SR_IO6, \ + MSC313_PINNAME_SR_IO7, \ + MSC313_PINNAME_SR_IO8, \ + MSC313_PINNAME_SR_IO9, \ + MSC313_PINNAME_SR_IO10, \ + MSC313_PINNAME_SR_IO11, \ + MSC313_PINNAME_SR_IO12, \ + MSC313_PINNAME_SR_IO13, \ + MSC313_PINNAME_SR_IO14, \ + MSC313_PINNAME_SR_IO15, \ + MSC313_PINNAME_SR_IO16, \ + MSC313_PINNAME_SR_IO17 + +#define OFF_SR_IO2 0x88 +#define OFF_SR_IO3 0x8c +#define OFF_SR_IO4 0x90 +#define OFF_SR_IO5 0x94 +#define OFF_SR_IO6 0x98 +#define OFF_SR_IO7 0x9c +#define OFF_SR_IO8 0xa0 +#define OFF_SR_IO9 0xa4 +#define OFF_SR_IO10 0xa8 +#define OFF_SR_IO11 0xac +#define OFF_SR_IO12 0xb0 +#define OFF_SR_IO13 0xb4 +#define OFF_SR_IO14 0xb8 +#define OFF_SR_IO15 0xbc +#define OFF_SR_IO16 0xc0 +#define OFF_SR_IO17 0xc4 + +#define SR_OFFSETS \ + OFF_SR_IO2, \ + OFF_SR_IO3, \ + OFF_SR_IO4, \ + OFF_SR_IO5, \ + OFF_SR_IO6, \ + OFF_SR_IO7, \ + OFF_SR_IO8, \ + OFF_SR_IO9, \ + OFF_SR_IO10, \ + OFF_SR_IO11, \ + OFF_SR_IO12, \ + OFF_SR_IO13, \ + OFF_SR_IO14, \ + OFF_SR_IO15, \ + OFF_SR_IO16, \ + OFF_SR_IO17 + +#define SD_NAMES \ + MSC313_PINNAME_SD_CLK, \ + MSC313_PINNAME_SD_CMD, \ + MSC313_PINNAME_SD_D0, \ + MSC313_PINNAME_SD_D1, \ + MSC313_PINNAME_SD_D2, \ + MSC313_PINNAME_SD_D3 + +#define OFF_SD_CLK 0x140 +#define OFF_SD_CMD 0x144 +#define OFF_SD_D0 0x148 +#define OFF_SD_D1 0x14c +#define OFF_SD_D2 0x150 +#define OFF_SD_D3 0x154 + +#define SD_OFFSETS \ + OFF_SD_CLK, \ + OFF_SD_CMD, \ + OFF_SD_D0, \ + OFF_SD_D1, \ + OFF_SD_D2, \ + OFF_SD_D3 + +#define I2C1_NAMES \ + MSC313_PINNAME_I2C1_SCL, \ + MSC313_PINNAME_I2C1_SCA + +#define OFF_I2C1_SCL 0x188 +#define OFF_I2C1_SCA 0x18c + +#define I2C1_OFFSETS \ + OFF_I2C1_SCL, \ + OFF_I2C1_SCA + +#define SPI0_NAMES \ + MSC313_PINNAME_SPI0_CZ, \ + MSC313_PINNAME_SPI0_CK, \ + MSC313_PINNAME_SPI0_DI, \ + MSC313_PINNAME_SPI0_DO + +#define OFF_SPI0_CZ 0x1c0 +#define OFF_SPI0_CK 0x1c4 +#define OFF_SPI0_DI 0x1c8 +#define OFF_SPI0_DO 0x1cc + +#define SPI0_OFFSETS \ + OFF_SPI0_CZ, \ + OFF_SPI0_CK, \ + OFF_SPI0_DI, \ + OFF_SPI0_DO + +struct msc313_gpio_data { + const char * const *names; + const unsigned int *offsets; + const unsigned int num; +}; + +#define MSC313_GPIO_CHIPDATA(_chip) \ +static const struct msc313_gpio_data _chip##_data = { \ + .names = _chip##_names, \ + .offsets = _chip##_offsets, \ + .num = ARRAY_SIZE(_chip##_offsets), \ +} + +#ifdef CONFIG_MACH_INFINITY +static const char * const msc313_names[] = { + FUART_NAMES, + SR_NAMES, + SD_NAMES, + I2C1_NAMES, + SPI0_NAMES, +}; + +static const unsigned int msc313_offsets[] = { + FUART_OFFSETS, + SR_OFFSETS, + SD_OFFSETS, + I2C1_OFFSETS, + SPI0_OFFSETS, +}; + +MSC313_GPIO_CHIPDATA(msc313); +#endif + +struct msc313_gpio { + void __iomem *base; + const struct msc313_gpio_data *gpio_data; + int *irqs; + u8 *saved; +}; + +static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); + + if (value) + gpioreg |= MSC313_GPIO_OUT; + else + gpioreg &= ~MSC313_GPIO_OUT; + + writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); +} + +static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + + return readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]) + & MSC313_GPIO_IN; +} + +static int msc313_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); + + gpioreg |= MSC313_GPIO_OEN; + writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + + return 0; +} + +static int msc313_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); + + gpioreg &= ~MSC313_GPIO_OEN; + if (value) + gpioreg |= MSC313_GPIO_OUT; + else + gpioreg &= ~MSC313_GPIO_OUT; + writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + + return 0; +} + +static int msc313_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + + return gpio->irqs[offset]; +} + +static int msc313_gpio_probe(struct platform_device *pdev) +{ + int i, ret; + const struct msc313_gpio_data *match_data; + struct msc313_gpio *gpio; + struct resource *res; + struct gpio_chip *gpiochip; + + match_data = of_device_get_match_data(&pdev->dev); + if (!match_data) + return -EINVAL; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->gpio_data = match_data; + + gpio->irqs = devm_kzalloc(&pdev->dev, gpio->gpio_data->num * sizeof(*gpio->irqs), GFP_KERNEL); + if (!gpio->irqs) + return -ENOMEM; + + gpio->saved = devm_kzalloc(&pdev->dev, gpio->gpio_data->num * sizeof(*gpio->saved), GFP_KERNEL); + if (!gpio->saved) + return -ENOMEM; + + platform_set_drvdata(pdev, gpio); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpio->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); + + gpiochip = devm_kzalloc(&pdev->dev, sizeof(*gpiochip), GFP_KERNEL); + if (!gpiochip) + return -ENOMEM; + + gpiochip->label = DRIVER_NAME; + gpiochip->parent = &pdev->dev; + gpiochip->request = gpiochip_generic_request; + gpiochip->free = gpiochip_generic_free; + gpiochip->direction_input = msc313_gpio_direction_input; + gpiochip->direction_output = msc313_gpio_direction_output; + gpiochip->get = msc313_gpio_get; + gpiochip->set = msc313_gpio_set; + gpiochip->to_irq = msc313_gpio_to_irq; + gpiochip->base = -1; + gpiochip->ngpio = gpio->gpio_data->num; + gpiochip->names = gpio->gpio_data->names; + + for (i = 0; i < gpiochip->ngpio; i++) + gpio->irqs[i] = of_irq_get_byname(pdev->dev.of_node, gpio->gpio_data->names[i]); + + ret = gpiochip_add_data(gpiochip, gpio); + return ret; +} + +static const struct of_device_id msc313_gpio_of_match[] = { +#ifdef CONFIG_MACH_INFINITY + { + .compatible = "mstar,msc313-gpio", + .data = &msc313_data, + }, +#endif + { } +}; + +/* The GPIO controller loses the state of the registers when the + * SoC goes into "DRAM self-refresh low power" mode so we need to + * save the direction and value before suspending and put it back + * when resuming. + */ + +static int __maybe_unused msc313_gpio_suspend(struct device *dev) +{ + struct msc313_gpio *gpio = dev_get_drvdata(dev); + int i; + + for (i = 0; i < gpio->gpio_data->num; i++) + gpio->saved[i] = readb_relaxed(gpio->base + gpio->gpio_data->offsets[i]) & MSC313_GPIO_BITSTOSAVE; + + return 0; +} + +static int __maybe_unused msc313_gpio_resume(struct device *dev) +{ + struct msc313_gpio *gpio = dev_get_drvdata(dev); + int i; + + for (i = 0; i < gpio->gpio_data->num; i++) + writeb_relaxed(gpio->saved[i], gpio->base + gpio->gpio_data->offsets[i]); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume); + +static struct platform_driver msc313_gpio_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = msc313_gpio_of_match, + .pm = &msc313_gpio_ops, + }, + .probe = msc313_gpio_probe, +}; + +builtin_platform_driver(msc313_gpio_driver);
This adds a driver that supports the GPIO block found in MStar/SigmaStar ARMv7 SoCs. The controller seems to support 128 lines but where they are wired up differs between chips and no currently known chip uses anywhere near 128 lines so there needs to be some per-chip data to collect together what lines actually have physical pins attached and map the right names to them. The core peripherals seem to use the same lines on the currently known chips but the lines used for the sensor interface, lcd controller etc pins seem to be totally different between the infinity and mercury chips The code tries to collect all of the re-usable names, offsets etc together so that it's easy to build the extra per-chip data for other chips in the future. So far this only supports the MSC313 and MSC313E chips. Support for the SSC8336N (mercury5) is trivial to add once all of the lines have been mapped out. Signed-off-by: Daniel Palmer <daniel@0x0f.com> --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-msc313.c | 341 +++++++++++++++++++++++++++++++++++++ 4 files changed, 352 insertions(+) create mode 100644 drivers/gpio/gpio-msc313.c