[PATCH 1/2] gpio: Add CNS3XXX mmio gpio support

林宏文 tommy.lin.1101 at gmail.com
Mon Aug 15 03:43:01 EDT 2011


>> +#include <asm-generic/gpio.h>
>
> Please see linux-next's arch/arm/include/asm/gpio.h
That means I have to submit to linux-next, if I following linux-next
gpio.h header file.

>> +#define GPIO_INTR_MASK_OFFSET                        0x2C
>> +#define GPIO_INTR_CLEAR_OFFSET                       0x30
>> +#define GPIO_INTR_TRIGGER_METHOD_OFFSET              0x34
>> +#define GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET  0x38
>> +#define GPIO_INTR_TRIGGER_POL_OFFSET         0x3C
>> +#define GPIO_BOUNCE_ENABLE_OFFSET            0x40
>> +#define GPIO_BOUNCE_PRESCALE_OFFSET          0x44
>
> Do you really need to put these here?  Can they go in a private header file
> maybe in arch/arm/mach-xxx/ ?
I will move these register offset macros to arch/arm/mach-cns3xxx/gpio-regs.h

>> +
>> +
>> +#define gpio_get_value                       __gpio_get_value
>> +#define gpio_set_value                       __gpio_set_value
>> +#define gpio_cansleep                        __gpio_cansleep
>> +#define gpio_to_irq                  cns3xxx_gpio_to_irq
>
> Again, please see linux-next.
>
>> +inline int cns3xxx_gpio_to_irq(unsigned gpio)
>> +{
>> +     return NR_IRQS_CNS3XXX + gpio;
>> +}
>> +EXPORT_SYMBOL(gpio_to_irq);
>
> No.  Three issues:
>
> 1. You are exporting a function you're not defining here.
> 2. The function is marked inline yet nothing in this file uses it, so
>   the inline provides no useful purpose.
> 3. Please use the standard gpiolib gpio_to_irq() and hook the translation
>   into the gpiolib .to_irq method.
I leverage the generic driver for memory mapped GPIO, which is not define
gpio_to_irq(). So I use the lazy way here. I should change the way to use
bgpio_chip and hook gpio_to_irq().

>> +static void __iomem *gpio_map(struct platform_device *pdev,
>> +             const char *name, int *err)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct resource *r;
>> +     resource_size_t start;
>> +     resource_size_t sz;
>> +     void __iomem *ret;
>> +
>> +     *err = 0;
>> +
>> +     r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
>> +     if (!r)
>> +             return NULL;
>> +
>> +     sz = resource_size(r);
>> +     start = r->start;
>> +
>> +     if (!devm_request_mem_region(dev, start, sz, r->name)) {
>> +             *err = -EBUSY;
>> +             return NULL;
>> +     }
>> +
>> +     ret = devm_ioremap(dev, start, sz);
>> +     if (!ret) {
>> +             *err = -ENOMEM;
>> +             return NULL;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int __devinit gpio_probe(struct platform_device *pdev)
>> +{
>> +     int i, nr_gpios = 0, err = 0;
>> +     struct device *dev = &pdev->dev;
>> +     struct resource *res;
>> +     void __iomem *misc_reg;
>> +     struct irq_chip_type *ct;
>> +
>> +     /* TODO Once the clk framework (clk_get() + clk_enable()) is
>> +      * implemented. Use clok framework APIs instead of these APIs.
>> +      */
>> +     cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO);
>> +     cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO);
>> +
>> +
>> +     misc_reg = gpio_map(pdev, "MISC", &err);
>> +     if (!misc_reg) {
>> +             dev_dbg(dev, "%s gpio_map \"MISC\" failure! err=%d\n",
>> +                             __func__, err);
>> +             return err;
>> +     }
>> +
>> +     /* Scan and match GPIO resources */
>> +     for (i = 0; i < nr_banks; i++) {
>> +             /* Fetech GPIO interrupt control register base address */
>> +             gc_info[i].reg_base = gpio_map(pdev, gc_info[i].name, &err);
>> +             if (!gc_info[i].reg_base) {
>> +                     dev_dbg(dev, "%s gpio_map %s failure! err=%d\n",
>> +                                     __func__, gc_info[i].name, err);
>> +                     goto err1;
>> +             }
>> +
>> +             /* Fetech GPIO interrupt ID number */
>> +             res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
>> +                             gc_info[i].name);
>> +             if (!res) {
>> +                     dev_dbg(dev, "%s platform_get_resource_byname (%s)"
>> +                                     "failed!\n", gc_info[i].name, __func__);
>> +                     err = -ENODEV;
>> +                     goto err2;
>> +             }
>> +             gc_info[i].irq = res->start;
>> +             gc_info[i].reg_gpio_dis = misc_reg + 0x14 + i * 4;
>> +
>> +             gc_info[i].gc = irq_alloc_generic_chip("GPIO", 1,
>> +                             NR_IRQS_CNS3XXX + gc_info[i].base,
>> +                             gc_info[i].reg_base, handle_level_irq);
>> +             if (!gc_info[i].gc) {
>> +                     dev_dbg(dev, "%s irq_alloc_generic_chip failed!\n",
>> +                                     __func__);
>> +                     err = -ENOMEM;
>> +                     goto err2;
>> +             }
>> +             gc_info[i].gc->private = &gc_info[i];
>> +
>> +             ct = gc_info[i].gc->chip_types;
>> +             ct->regs.mask = get_offset(GPIO_INTR_ENABLE_OFFSET);
>> +             ct->regs.ack = get_offset(GPIO_INTR_CLEAR_OFFSET);
>> +             ct->regs.type = get_offset(GPIO_INTR_TRIGGER_METHOD_OFFSET);
>> +             ct->regs.polarity = get_offset(GPIO_INTR_TRIGGER_POL_OFFSET);
>> +             ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
>> +             ct->chip.irq_ack = irq_gc_ack_set_bit;
>> +             ct->chip.irq_mask = irq_gc_mask_clr_bit;
>> +             ct->chip.irq_unmask = irq_gc_mask_set_bit;
>> +             ct->chip.irq_set_type = cns3xxx_gpio_set_irq_type;
>> +
>> +             irq_setup_generic_chip(gc_info[i].gc, IRQ_MSK(gc_info[i].ngpio),
>> +                     IRQ_GC_INIT_MASK_CACHE | IRQ_GC_INIT_NESTED_LOCK,
>> +                     IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
>> +
>> +             irq_set_chained_handler(gc_info[i].irq, gpio_irq_handler);
>> +             err = irq_set_handler_data(gc_info[i].irq, &gc_info[i]);
>> +             if (err) {
>> +                     dev_dbg(dev, "%s irq_set_handler_data fail!\n",
>> +                                     __func__);
>> +                     goto err2;
>> +             }
>> +
>> +             nr_gpios += gc_info[i].ngpio;
>> +             if (nr_gpios >= MAX_GPIO_NO)
>> +                     break;
>> +     }
>> +
>> +     return 0;
>> +
>> +err2:
>> +     if (gc_info[1].reg_base)
>> +             devm_iounmap(dev, gc_info[0].reg_base);
>
> This looks buggy.  You're unmapping the wrong reg_base.
>
>> +
>> +err1:
>> +     if (gc_info[0].reg_base)
>> +             devm_iounmap(dev, gc_info[0].reg_base);
>> +
>> +     devm_iounmap(dev, misc_reg);
>
> This is also rather inconsistent.  While you iounmap, you're not releasing
> the region which you claimed in gpio_map().
>
> It's also rather unnecessary.  One of the reasons for devm_* is that
> drivers shouldn't need to care about cleaning up the managed resources.
> The driver model will take care of that when a probe fails, or upon
> remove.  It avoids people making mistakes like the above.
I got the idea. There is no need to do any devm_iounmap here. So I can just
remove these devm_iounmap.

> However, I don't see where you're cleaning up the generic irqchip
> which was allocated (which is an unmanaged resource).
irq_remove_generic_chip didn't release the memory allocated by
irq_alloc_generic_chip,
I should release memory myself.

-- 
Best Regards,
Tommy



More information about the linux-arm-kernel mailing list