Re: [PATCH] gpio: winbond: add driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Maciej!

Thanks for your patch!

On Thu, Dec 14, 2017 at 11:05 PM, Maciej S. Szmigiero
<mail@xxxxxxxxxxxxxxxxxxxxx> wrote:

> This commit adds GPIO driver for Winbond Super I/Os.
>
> Currently, only W83627UHG model (also known as Nuvoton NCT6627UD) is
> supported but in the future a support for other Winbond models, too, can
> be added to the driver.
>
> A module parameter "gpios" sets a bitmask of GPIO ports to enable (bit 0 is
> GPIO1, bit 1 is GPIO2, etc.).
> One should be careful which ports one tinkers with since some might be
> managed by the firmware (for functions like powering on and off, sleeping,
> BIOS recovery, etc.) and some of GPIO port pins are physically shared with
> other devices included in the Super I/O chip.
>
> Signed-off-by: Maciej S. Szmigiero <mail@xxxxxxxxxxxxxxxxxxxxx>

As this is a ioport-based driver I'd like William Breathitt Gray to
help reviewing it. He knows this stuff.

Add a license tag at the top of the file like this:

// SPDX-License-Identifier: GPL-2.0

It is part of our new dance.

> +#include <linux/errno.h>
> +#include <linux/gpio.h>

Only:

#include <linux/driver.h>

> +static int gpiobase = -1; /* dynamic */

Don't make this configurable.

> +static uint8_t gpios;
> +static uint8_t ppgpios;
> +static uint8_t odgpios;

Just use u8 unless you can tell me why I have to
switch it everywhere. Use u8, u16, u32 everywhere in place
of these, change globally.

> +static bool pledgpio;
> +static bool beepgpio;
> +static bool i2cgpio;
> +
> +static int winbond_sio_enter(uint16_t base)

And u16 as argument.

> +static void winbond_sio_reg_bclear(uint16_t base, uint8_t reg, uint8_t bit)
> +{
> +       uint8_t val;
> +
> +       val = winbond_sio_reg_read(base, reg);
> +       val &= ~BIT(bit);
> +       winbond_sio_reg_write(base, reg, val);

This kind of construction make me feel like we should have
an ioport regmap. But no requirement for this driver.

> +struct winbond_gpio_info {
> +       uint8_t dev;
> +       uint8_t ioreg;
> +       uint8_t invreg;
> +       uint8_t datareg;
> +};

Add kerneldoc to this struct please.

> +static const struct winbond_gpio_info winbond_gpio_infos[6] = {
> +       { .dev = WB_SIO_DEV_GPIO12, .ioreg = WB_SIO_GPIO12_REG_IO1,
> +         .invreg = WB_SIO_GPIO12_REG_INV1,
> +         .datareg = WB_SIO_GPIO12_REG_DATA1 },

Please break these up a bit:

{
    .dev = WB_SIO_DEV_GPIO12,
    .ioreg = WB_SIO_GPIO12_REG_IO1,
    .invreg = WB_SIO_GPIO12_REG_INV1,
    .datareg = WB_SIO_GPIO12_REG_DATA1,
},


> +/* returns whether changing a pin is allowed */
> +static bool winbond_gpio_get_info(unsigned int gpio_num,
> +                                 const struct winbond_gpio_info **info)
> +{
> +       bool allow_changing = true;
> +       unsigned int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(winbond_gpio_infos); i++) {
> +               if (!(gpios & BIT(i)))
> +                       continue;
> +
> +               if (gpio_num < 8)
> +                       break;
> +
> +               gpio_num -= 8;
> +       }
> +

Add a comment here explaining why we warn on this.

> +       if (WARN_ON(i >= ARRAY_SIZE(winbond_gpio_infos)))
> +               i = 0;
> +
> +       *info = &winbond_gpio_infos[i];
> +

Add comment here explaining what is going on.

> +       if (i == 1) {
> +               if (gpio_num == 0 && !pledgpio)
> +                       allow_changing = false;
> +               else if (gpio_num == 1 && !beepgpio)
> +                       allow_changing = false;
> +               else if ((gpio_num == 5 || gpio_num == 6) && !i2cgpio)
> +                       allow_changing = false;
> +       }
> +
> +       return allow_changing;
> +}

(...)

> +static struct gpio_chip winbond_gpio_chip = {
> +       .label                  = WB_GPIO_DRIVER_NAME,
> +       .owner                  = THIS_MODULE,
> +       .can_sleep              = true,

Why can this ioport driver sleep?

> +static int winbond_gpio_probe(struct platform_device *pdev)
> +{
> +       uint16_t *base = dev_get_platdata(&pdev->dev);
> +       unsigned int i;
> +
> +       if (base == NULL)
> +               return -EINVAL;
> +

Add a comment explaining how the GPIO lines are detected here.

> +       winbond_gpio_chip.ngpio = 0;
> +       for (i = 0; i < 5; i++)
> +               if (gpios & BIT(i))
> +                       winbond_gpio_chip.ngpio += 8;
> +
> +       if (gpios & BIT(5))
> +               winbond_gpio_chip.ngpio += 5;
> +
> +       winbond_gpio_chip.base = gpiobase;
> +       winbond_gpio_chip.parent = &pdev->dev;
> +
> +       return devm_gpiochip_add_data(&pdev->dev, &winbond_gpio_chip, base);
> +}
> +
> +static void winbond_gpio_warn_conflict(unsigned int idx, const char *otherdev)
> +{
> +       pr_warn(WB_GPIO_DRIVER_NAME
> +               ": enabled GPIO%u share pins with active %s\n", idx + 1,
> +               otherdev);
> +}

I don't understand this otherdev business, is it pin multiplexing?

> +static void winbond_gpio_configure_common(uint16_t base, unsigned int idx,
> +                                         uint8_t dev, uint8_t enable_reg,
> +                                         uint8_t enable_bit,
> +                                         uint8_t output_reg,
> +                                         uint8_t output_pp_bit)
> +{
> +       winbond_sio_select_logical(base, dev);
> +
> +       winbond_sio_reg_bset(base, enable_reg, enable_bit);
> +
> +       if (ppgpios & BIT(idx))
> +               winbond_sio_reg_bset(base, output_reg,
> +                                    output_pp_bit);
> +       else if (odgpios & BIT(idx))
> +               winbond_sio_reg_bclear(base, output_reg,
> +                                      output_pp_bit);
> +       else
> +               pr_notice(WB_GPIO_DRIVER_NAME ": GPIO%u pins are %s\n", idx + 1,
> +                         winbond_sio_reg_btest(base, output_reg,
> +                                               output_pp_bit) ?
> +                         "push-pull" :
> +                         "open drain");

If your hardware supports open drain then implement .set_config() for
it in the gpio_chip so you can use the hardware open drain with the driver.

> +static void winbond_gpio_configure_0(uint16_t base)

This is an awkward name for a function. Give it some semantically
reasonable name please.

winbond_gpio_configure_uartb()?

> +{
> +       uint8_t val;
> +
> +       if (!(gpios & BIT(0)))
> +               return;
> +
> +       winbond_sio_select_logical(base, WB_SIO_DEV_UARTB);
> +       if (winbond_sio_reg_btest(base, WB_SIO_UARTB_REG_ENABLE,
> +                                 WB_SIO_UARTB_ENABLE_ON))
> +               winbond_gpio_warn_conflict(0, "UARTB");
> +
> +       val = winbond_sio_reg_read(base, WB_SIO_REG_GPIO1_MF);
> +       if ((val & WB_SIO_REG_G1MF_FS) != WB_SIO_REG_G1MF_FS_GPIO1) {
> +               pr_warn(WB_GPIO_DRIVER_NAME
> +                       ": GPIO1 pins were connected to something else (%.2x), fixing\n",
> +                       (unsigned int)val);
> +
> +               val &= ~WB_SIO_REG_G1MF_FS;
> +               val |= WB_SIO_REG_G1MF_FS_GPIO1;
> +
> +               winbond_sio_reg_write(base, WB_SIO_REG_GPIO1_MF, val);
> +       }
> +
> +       winbond_gpio_configure_common(base, 0, WB_SIO_DEV_GPIO12,
> +                                     WB_SIO_GPIO12_REG_ENABLE,
> +                                     WB_SIO_GPIO12_ENABLE_1,
> +                                     WB_SIO_REG_GPIO1_MF,
> +                                     WB_SIO_REG_G1MF_G1PP);
> +}
> +
> +static void winbond_gpio_configure_1(uint16_t base)

Same here. 0, 1? I don't get it.

winbond_gpio_configure_i2cgpio()?

> +{
> +       if (!(gpios & BIT(1)))
> +               return;
> +
> +       i2cgpio = !winbond_sio_reg_btest(base, WB_SIO_REG_I2C_PS,
> +                                        WB_SIO_REG_I2CPS_I2CFS);
> +       if (!i2cgpio)
> +               pr_warn(WB_GPIO_DRIVER_NAME
> +                       ": disabling GPIO2.5 and GPIO2.6 as I2C is enabled\n");
> +
> +       winbond_gpio_configure_common(base, 1, WB_SIO_DEV_GPIO12,
> +                                     WB_SIO_GPIO12_REG_ENABLE,
> +                                     WB_SIO_GPIO12_ENABLE_2,
> +                                     WB_SIO_REG_GPIO1_MF,
> +                                     WB_SIO_REG_G1MF_G2PP);
> +}
> +
> +static void winbond_gpio_configure_2(uint16_t base)

Etc.

> +{
> +       if (!(gpios & BIT(2)))
> +               return;
> +
> +       winbond_sio_select_logical(base, WB_SIO_DEV_UARTC);
> +       if (winbond_sio_reg_btest(base, WB_SIO_UARTC_REG_ENABLE,
> +                                 WB_SIO_UARTC_ENABLE_ON))
> +               winbond_gpio_warn_conflict(2, "UARTC");
> +
> +       winbond_gpio_configure_common(base, 2, WB_SIO_DEV_GPIO34,
> +                                     WB_SIO_GPIO34_REG_ENABLE,
> +                                     WB_SIO_GPIO34_ENABLE_3,
> +                                     WB_SIO_REG_OVTGPIO3456,
> +                                     WB_SIO_REG_OG3456_G3PP);
> +}
> +
> +static void winbond_gpio_configure_3(uint16_t base)

Etc.

> +{
> +       if (!(gpios & BIT(3)))
> +               return;
> +
> +       winbond_sio_select_logical(base, WB_SIO_DEV_UARTD);
> +       if (winbond_sio_reg_btest(base, WB_SIO_UARTD_REG_ENABLE,
> +                                 WB_SIO_UARTD_ENABLE_ON))
> +               winbond_gpio_warn_conflict(3, "UARTD");
> +
> +       winbond_gpio_configure_common(base, 3, WB_SIO_DEV_GPIO34,
> +                                     WB_SIO_GPIO34_REG_ENABLE,
> +                                     WB_SIO_GPIO34_ENABLE_4,
> +                                     WB_SIO_REG_OVTGPIO3456,
> +                                     WB_SIO_REG_OG3456_G4PP);
> +}
> +
> +static void winbond_gpio_configure_4(uint16_t base)

Etc.

We are starting to see code duplication. It needs to be made into
some kind of table.

> +{
> +       if (!(gpios & BIT(4)))
> +               return;
> +
> +       winbond_sio_select_logical(base, WB_SIO_DEV_UARTE);
> +       if (winbond_sio_reg_btest(base, WB_SIO_UARTE_REG_ENABLE,
> +                                 WB_SIO_UARTE_ENABLE_ON))
> +               winbond_gpio_warn_conflict(4, "UARTE");
> +
> +       winbond_gpio_configure_common(base, 4, WB_SIO_DEV_WDGPIO56,
> +                                     WB_SIO_WDGPIO56_REG_ENABLE,
> +                                     WB_SIO_WDGPIO56_ENABLE_5,
> +                                     WB_SIO_REG_OVTGPIO3456,
> +                                     WB_SIO_REG_OG3456_G5PP);
> +}
> +
> +static void winbond_gpio_configure_5(uint16_t base)

Yeah... a table :)

> +{
> +       if (!(gpios & BIT(5)))
> +               return;
> +
> +       if (winbond_sio_reg_btest(base, WB_SIO_REG_GLOBAL_OPT,
> +                                 WB_SIO_REG_GO_ENFDC)) {
> +               pr_warn(WB_GPIO_DRIVER_NAME
> +                       ": disabling GPIO6 as FDC is enabled\n");
> +
> +               gpios &= ~BIT(5);
> +               return;
> +       }
> +
> +       winbond_gpio_configure_common(base, 5, WB_SIO_DEV_WDGPIO56,
> +                                     WB_SIO_WDGPIO56_REG_ENABLE,
> +                                     WB_SIO_WDGPIO56_ENABLE_6,
> +                                     WB_SIO_REG_OVTGPIO3456,
> +                                     WB_SIO_REG_OG3456_G6PP);
> +}
> +
> +static int winbond_gpio_configure(uint16_t base)
> +{
> +       winbond_gpio_configure_0(base);
> +       winbond_gpio_configure_1(base);
> +       winbond_gpio_configure_2(base);
> +       winbond_gpio_configure_3(base);
> +       winbond_gpio_configure_4(base);
> +       winbond_gpio_configure_5(base);

So figure out some way to not have 6 similar functions but parameterize
the functions somehow with some struct or so?

> +       if (!(gpios & (BIT(5 + 1) - 1))) {
> +               pr_err(WB_GPIO_DRIVER_NAME
> +                      ": please use 'gpios' module parameter to select some active GPIO ports to enable\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static struct platform_device *winbond_gpio_pdev;

Hm I guess this is needed with ISA devices.

> +static int winbond_gpio_init_one(uint16_t base)

One... GPIO device? Put in some more info in the function name.


> +module_param(gpiobase, int, 0444);
> +MODULE_PARM_DESC(gpiobase,
> +                "number of the first GPIO to register. default is -1, that is, dynamically allocated.");

Drop this. We don't support hammering down the GPIO base.
Not anymore.

> +module_param(gpios, byte, 0444);
> +MODULE_PARM_DESC(gpios,
> +                "bitmask of GPIO ports to enable (bit 0 - GPIO1, bit 1 - GPIO2, etc.");
> +
> +module_param(ppgpios, byte, 0444);
> +MODULE_PARM_DESC(ppgpios,
> +                "bitmask of GPIO ports to set to push-pull mode (bit 0 - GPIO1, bit 1 - GPIO2, etc.");
> +
> +module_param(odgpios, byte, 0444);
> +MODULE_PARM_DESC(odgpios,
> +                "bitmask of GPIO ports to set to open drain mode (bit 0 - GPIO1, bit 1 - GPIO2, etc.");
> +
> +module_param(pledgpio, bool, 0644);
> +MODULE_PARM_DESC(pledgpio,
> +                "enable changing value of GPIO2.0 bit (Power LED), default no.");
> +
> +module_param(beepgpio, bool, 0644);
> +MODULE_PARM_DESC(beepgpio,
> +                "enable changing value of GPIO2.1 bit (BEEP), default no.");

This is an awful lot of module parameters.

Why do we need so many?

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux