lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CACRpkdZ67KoaMWUzpPm5g7yvQ-_q9zxuS+4wcXP0u18MW=XovA@mail.gmail.com>
Date:	Fri, 16 May 2014 19:33:44 +0200
From:	Linus Walleij <linus.walleij@...aro.org>
To:	"Zhu, Lejun" <lejun.zhu@...ux.intel.com>
Cc:	Alexandre Courbot <gnurou@...il.com>,
	"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
	"linux-gpio@...r.kernel.org" <linux-gpio@...r.kernel.org>,
	bin.yang@...el.com, Darren Hart <dvhart@...ux.intel.com>,
	"Holmberg, Hans" <hans.holmberg@...el.com>,
	Mika Westerberg <mika.westerberg@...ux.intel.com>,
	Mathias Nyman <mathias.nyman@...ux.intel.com>
Subject: Re: [PATCH] gpio: Add support for Intel SoC PMIC (Crystal Cove)

On Wed, May 14, 2014 at 5:44 PM, Zhu, Lejun <lejun.zhu@...ux.intel.com> wrote:

> Devices based on Intel SoC products such as Baytrail have a Power
> Management IC. In the PMIC there are subsystems for voltage regulation,
> A/D conversion, GPIO and PWMs. The PMIC in Baytrail-T platform is called
> Crystal Cove.
>
> This patch adds support for the GPIO function in Crystal Cove.
>
> Signed-off-by: Yang, Bin <bin.yang@...el.com>
> Signed-off-by: Zhu, Lejun <lejun.zhu@...ux.intel.com>

(...)

> +config GPIO_INTEL_SOC_PMIC
> +       bool "GPIO on Intel SoC PMIC"
> +       depends on INTEL_SOC_PMIC

select GPIOLIB_IRQCHIP

and use the infrastructure from commit
1425052097b53de841e064dc190a9009480c208c
"gpio: add IRQ chip helpers in gpiolib"

Some fixes for sleeping chips have been merged and are available
on the "devel" branch in the GPIO tree, so you may need to base
your development on that.

Adding some kerneldoc to the below struct will maybe make you
realize whether you actually need it or not.

> +struct crystalcove_gpio {
> +       struct mutex            buslock; /* irq_bus_lock */
> +       struct gpio_chip        chip;
> +       int                     irq;
> +       int                     irq_base;

You should not have hardcoded IRQ bases around. Use an irqdomain
to map IRQs, and even better: use the one provided in
chip->irqdomain by GPIOLIB_IRQCHIP. (It's a requirement.)

> +       int                     update;
> +       int                     trigger_type;
> +       int                     irq_mask;

Should this be a bool since you just set it to 0 or 1?

> +};
> +static struct crystalcove_gpio gpio_info;
> +
> +static void __crystalcove_irq_mask(int gpio, int mask)

I don't really like __doubleunderscore specifiers, can you use a
unique name or something instead.

> +{
> +       u8 mirqs0 = gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0;
> +       int offset = gpio < 8 ? gpio : gpio - 8;

Meh.
unsigned int offset = gpio%8;

> +
> +       if (mask)
> +               intel_soc_pmic_setb(mirqs0, 1 << offset);

Use
#include <linux/bitops.h>

intel_soc_pmic_setb(mirqs0, BIT(offset));

I really like the BIT() macros.

> +       else
> +               intel_soc_pmic_clearb(mirqs0, 1 << offset);

Dito.

> +static void __crystalcove_irq_type(int gpio, int type)
> +{
> +       u8 ctli = gpio < 8 ? GPIO0P0CTLI + gpio : GPIO1P0CTLI + (gpio - 8);

Do it like the first time instead, this is hard to read.

> +       type &= IRQ_TYPE_EDGE_BOTH;
> +       intel_soc_pmic_clearb(ctli, CTLI_INTCNT_BE);
> +
> +       if (type == IRQ_TYPE_EDGE_BOTH)
> +               intel_soc_pmic_setb(ctli, CTLI_INTCNT_BE);
> +       else if (type == IRQ_TYPE_EDGE_RISING)
> +               intel_soc_pmic_setb(ctli, CTLI_INTCNT_PE);
> +       else if (type & IRQ_TYPE_EDGE_FALLING)
> +               intel_soc_pmic_setb(ctli, CTLI_INTCNT_NE);
> +}
> +
> +static int crystalcove_gpio_direction_input(struct gpio_chip *chip,
> +                                           unsigned gpio)
> +{
> +       u8 ctlo = gpio < 8 ? GPIO0P0CTLO + gpio : GPIO1P0CTLO + (gpio - 8);

Dito.

> +
> +       intel_soc_pmic_writeb(ctlo, CTLO_INPUT_DEF);
> +       return 0;
> +}
> +
> +static int crystalcove_gpio_direction_output(struct gpio_chip *chip,
> +                                            unsigned gpio, int value)
> +{
> +       u8 ctlo = gpio < 8 ? GPIO0P0CTLO + gpio : GPIO1P0CTLO + (gpio - 8);

Dito. (etc for all)

> +
> +       intel_soc_pmic_writeb(ctlo, CTLO_OUTPUT_DEF | value);
> +       return 0;
> +}
> +
> +static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
> +{
> +       u8 ctli = gpio < 8 ? GPIO0P0CTLI + gpio : GPIO1P0CTLI + (gpio - 8);
> +
> +       return intel_soc_pmic_readb(ctli) & 0x1;
> +}
> +
> +static void crystalcove_gpio_set(struct gpio_chip *chip,
> +                                unsigned gpio, int value)
> +{
> +       u8 ctlo = gpio < 8 ? GPIO0P0CTLO + gpio : GPIO1P0CTLO + (gpio - 8);
> +
> +       if (value)
> +               intel_soc_pmic_setb(ctlo, 1);
> +       else
> +               intel_soc_pmic_clearb(ctlo, 1);
> +}
> +
> +static int crystalcove_irq_type(struct irq_data *data, unsigned type)
> +{
> +       struct crystalcove_gpio *cg = irq_data_get_irq_chip_data(data);
> +
> +       cg->trigger_type = type;
> +       cg->update |= UPDATE_TYPE;
> +
> +       return 0;
> +}
> +
> +static int crystalcove_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
> +{
> +       struct crystalcove_gpio *cg =
> +               container_of(chip, struct crystalcove_gpio, chip);
> +
> +       return cg->irq_base + gpio;
> +}

Nope. Use the irqdomain in chip->irqdomain.

> +static void crystalcove_bus_lock(struct irq_data *data)
> +{
> +       struct crystalcove_gpio *cg = irq_data_get_irq_chip_data(data);

This and all IRQ function callbacks needs to use a construct like
this:

struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct crystalcove_gpio *cg = container_of(gc, struct crystalcove_gpio, chip);

> +
> +       mutex_lock(&cg->buslock);
> +}
> +
> +static void crystalcove_bus_sync_unlock(struct irq_data *data)
> +{

The same here and for each irq function, as the irqchip helpers pass
struct gpio_chip* as irq_chip_data.

> +       struct crystalcove_gpio *cg = irq_data_get_irq_chip_data(data);
> +       int gpio = data->irq - cg->irq_base;
> +
> +       if (cg->update & UPDATE_TYPE)
> +               __crystalcove_irq_type(gpio, cg->trigger_type);
> +       if (cg->update & UPDATE_MASK)
> +               __crystalcove_irq_mask(gpio, cg->irq_mask);
> +       cg->update = 0;
> +
> +       mutex_unlock(&cg->buslock);
> +}
> +
> +static void crystalcove_irq_unmask(struct irq_data *data)
> +{
> +       struct crystalcove_gpio *cg = irq_data_get_irq_chip_data(data);
> +
> +       cg->irq_mask = 0;
> +       cg->update |= UPDATE_MASK;
> +}
> +
> +static void crystalcove_irq_mask(struct irq_data *data)
> +{
> +       struct crystalcove_gpio *cg = irq_data_get_irq_chip_data(data);
> +
> +       cg->irq_mask = 1;
> +       cg->update |= UPDATE_MASK;
> +}
> +
> +static struct irq_chip crystalcove_irqchip = {
> +       .name                   = "PMIC-GPIO",
> +       .irq_mask               = crystalcove_irq_mask,
> +       .irq_unmask             = crystalcove_irq_unmask,
> +       .irq_set_type           = crystalcove_irq_type,
> +       .irq_bus_lock           = crystalcove_bus_lock,
> +       .irq_bus_sync_unlock    = crystalcove_bus_sync_unlock,
> +};
> +
> +static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
> +{
> +       struct crystalcove_gpio *cg = data;

If you also use gpiochip_set_chained_irqchip() you need something like
this here:

struct gpio_chip *gc = data;
then  the container_of() construction.

> +       int pending;
> +       int gpio;
> +
> +       pending = intel_soc_pmic_readb(GPIO0IRQ) & 0xff;
> +       pending |= (intel_soc_pmic_readb(GPIO1IRQ) & 0xff) << 8;
> +       intel_soc_pmic_writeb(GPIO0IRQ, pending & 0xff);
> +       intel_soc_pmic_writeb(GPIO1IRQ, (pending >> 8) & 0xff);
> +
> +       local_irq_disable();
> +       for (gpio = 0; gpio < cg->chip.ngpio; gpio++)
> +               if (pending & (1 << gpio))
> +                       generic_handle_irq(cg->irq_base + gpio);
> +       local_irq_enable();
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void crystalcove_gpio_dbg_show(struct seq_file *s,
> +                                     struct gpio_chip *chip)
> +{
> +       struct crystalcove_gpio *cg =
> +               container_of(chip, struct crystalcove_gpio, chip);
> +       int gpio, offset;
> +       u8 ctlo, ctli, mirqs0, mirqsx, irq;
> +
> +       for (gpio = 0; gpio < cg->chip.ngpio; gpio++) {
> +               offset = gpio < 8 ? gpio : gpio - 8;
> +               ctlo = intel_soc_pmic_readb(
> +                       (gpio < 8 ? GPIO0P0CTLO : GPIO1P0CTLO) + offset);
> +               ctli = intel_soc_pmic_readb(
> +                       (gpio < 8 ? GPIO0P0CTLI : GPIO1P0CTLI) + offset);
> +               mirqs0 = intel_soc_pmic_readb(
> +                       gpio < 8 ? MGPIO0IRQS0 : MGPIO1IRQS0);
> +               mirqsx = intel_soc_pmic_readb(
> +                       gpio < 8 ? MGPIO0IRQSX : MGPIO1IRQSX);
> +               irq = intel_soc_pmic_readb(
> +                       gpio < 8 ? GPIO0IRQ : GPIO1IRQ);
> +               seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s %s\n",
> +                          gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ",
> +                          ctli & 0x1 ? "hi" : "lo",
> +                          ctli & CTLI_INTCNT_NE ? "fall" : "    ",
> +                          ctli & CTLI_INTCNT_PE ? "rise" : "    ",
> +                          ctlo,
> +                          mirqs0 & (1 << offset) ? "s0 mask  " : "s0 unmask",
> +                          mirqsx & (1 << offset) ? "sx mask  " : "sx unmask",
> +                          irq & (1 << offset) ? "pending" : "       ");
> +       }
> +}

Looks helpful!

> +static int crystalcove_gpio_probe(struct platform_device *pdev)
> +{
> +       int irq = platform_get_irq(pdev, 0);
> +       struct crystalcove_gpio *cg = &gpio_info;
> +       int retval;
> +       int i;
> +       struct device *dev = intel_soc_pmic_dev();
> +
> +       mutex_init(&cg->buslock);
> +       cg->chip.label = "intel_crystalcove";
> +       cg->chip.direction_input = crystalcove_gpio_direction_input;
> +       cg->chip.direction_output = crystalcove_gpio_direction_output;
> +       cg->chip.get = crystalcove_gpio_get;
> +       cg->chip.set = crystalcove_gpio_set;
> +       cg->chip.to_irq = crystalcove_gpio_to_irq;

You don't define to_irq when using GPIOLIB_IRQCHIP.

> +       cg->chip.base = -1;
> +       cg->chip.ngpio = NUM_GPIO;
> +       cg->chip.can_sleep = 1;

true. Set it to true. This is a bool.

> +       cg->chip.dev = dev;
> +       cg->chip.dbg_show = crystalcove_gpio_dbg_show;
> +
> +       retval = gpiochip_add(&cg->chip);
> +       if (retval) {
> +               dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval);
> +               goto out;
> +       }

Skip from here:

> +       cg->irq_base = irq_alloc_descs(-1, 0, NUM_GPIO, 0);
> +       if (cg->irq_base < 0) {
> +               retval = cg->irq_base;
> +               cg->irq_base = 0;
> +               dev_warn(&pdev->dev, "irq_alloc_descs error: %d\n", retval);
> +               goto out_remove_gpio;
> +       }
> +
> +       for (i = 0; i < NUM_GPIO; i++) {
> +               irq_set_chip_data(i + cg->irq_base, cg);
> +               irq_set_chip_and_handler_name(i + cg->irq_base,
> +                                             &crystalcove_irqchip,
> +                                             handle_simple_irq, "demux");
> +       }

To here, replace with:

gpiochip_irqchip_add(&cg->chip, &crystalcove_irqchip, cg->irq_base,
handle_simple_irq, IRQ_TYPE_NONE);

> +       retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler,
> +                                     IRQF_ONESHOT, "crystalcove_gpio", cg);
> +
> +       if (retval) {
> +               dev_warn(&pdev->dev, "request irq failed: %d\n", retval);
> +               goto out_free_desc;
> +       }

Maybe use
gpiochip_set_chained_irqchip() here.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ