Re: [RFC, PATCH 3/4] SoC base drivers: ASIC3 driver

From: Andrew Morton
Date: Tue May 01 2007 - 02:58:22 EST


On Tue, 1 May 2007 08:09:48 +0300 Paul Sokolovsky <pmiscml@xxxxxxxxx> wrote:

> Hello linux-kernel,
>
> Note: This driver depends on ds1wm.h header, recently submitted, and which by now should be in -mm tree.
> -----
>
> asic3_base: SoC base driver for ASIC3 chip.
>
> Signed-off-by: Paul Sokolovsky <pmiscml@xxxxxxxxx>
>
> ...
>
> +
> +struct asic3_data
> +{

struct asic3_data {

> + void *mapping;
> + unsigned int bus_shift;
> + int irq_base;
> + int irq_nr;
> +
> + u16 irq_bothedge[4];
> + struct device *dev;
> +
> + struct platform_device *mmc_dev;
> +};
> +
> +static spinlock_t asic3_gpio_lock;

DEFINE_SPINLOCK(), please - it's better to do it at compile-time.

> +static int asic3_remove(struct platform_device *dev);
> +
> +static inline unsigned long asic3_address(struct device *dev,
> + unsigned int reg)
> +{
> + struct asic3_data *adata;
> +
> + adata = (struct asic3_data *)dev->driver_data;
> +
> + return (unsigned long)adata->mapping + (reg >> (2 - adata->bus_shift));
> +}
> +
> +void asic3_write_register(struct device *dev, unsigned int reg, u32 value)
> +{
> + __raw_writew(value, asic3_address(dev, reg));
> +}
> +EXPORT_SYMBOL(asic3_write_register);
> +
> +u32 asic3_read_register(struct device *dev, unsigned int reg)
> +{
> + return __raw_readw(asic3_address(dev, reg));
> +}
> +EXPORT_SYMBOL(asic3_read_register);
> +
> +static inline void __asic3_write_register(struct asic3_data *asic,
> + unsigned int reg, u32 value)
> +{
> + __raw_writew(value, (unsigned long)asic->mapping
> + + (reg >> (2 - asic->bus_shift)));
> +}
> +
> +static inline u32 __asic3_read_register(struct asic3_data *asic,
> + unsigned int reg)
> +{
> + return __raw_readw((unsigned long)asic->mapping
> + + (reg >> (2 - asic->bus_shift)));
> +}

Why __raw_*() here?

How come we're using the io.h functions here, but [patch 2/4] open-coded it?

> +#define ASIC3_GPIO_FN(get_fn_name, set_fn_name, REG) \
> +u32 get_fn_name(struct device *dev) \
> +{ \
> + return asic3_read_register(dev, REG); \
> +} \
> +EXPORT_SYMBOL(get_fn_name); \
> + \
> +void set_fn_name(struct device *dev, u32 bits, u32 val) \
> +{ \
> + unsigned long flags; \
> + \
> + spin_lock_irqsave(&asic3_gpio_lock, flags); \
> + val |= (asic3_read_register(dev, REG) & ~bits); \
> + asic3_write_register(dev, REG, val); \
> + spin_unlock_irqrestore(&asic3_gpio_lock, flags); \
> +} \
> +EXPORT_SYMBOL(set_fn_name);
> +
> +#define ASIC3_GPIO_REGISTER(ACTION, action, fn, FN) \
> + ASIC3_GPIO_FN (asic3_get_gpio_ ## action ## _ ## fn , \
> + asic3_set_gpio_ ## action ## _ ## fn , \
> + _IPAQ_ASIC3_GPIO_ ## FN ## _Base \
> + + _IPAQ_ASIC3_GPIO_ ## ACTION )
> +
> +#define ASIC3_GPIO_FUNCTIONS(fn, FN) \
> + ASIC3_GPIO_REGISTER (Direction, dir, fn, FN) \
> + ASIC3_GPIO_REGISTER (Out, out, fn, FN) \
> + ASIC3_GPIO_REGISTER (SleepMask, sleepmask, fn, FN) \
> + ASIC3_GPIO_REGISTER (SleepOut, sleepout, fn, FN) \
> + ASIC3_GPIO_REGISTER (BattFaultOut, battfaultout, fn, FN) \
> + ASIC3_GPIO_REGISTER (AltFunction, alt_fn, fn, FN) \
> + ASIC3_GPIO_REGISTER (SleepConf, sleepconf, fn, FN) \
> + ASIC3_GPIO_REGISTER (Status, status, fn, FN)
> +
> +ASIC3_GPIO_FUNCTIONS(a, A)
> +ASIC3_GPIO_FUNCTIONS(b, B)
> +ASIC3_GPIO_FUNCTIONS(c, C)
> +ASIC3_GPIO_FUNCTIONS(d, D)

Ho hum, fair enough.

Was it deliberate that get_fn_name() and set_fn_name() are given global
scope? I guess so, given that they're exported to modules.

Please remove the space between the function or macro name and the "("
(whole patchset).

> +int asic3_gpio_get_value(struct device *dev, unsigned gpio)
> +{
> + u32 mask = ASIC3_GPIO_bit(gpio);
> + printk("%s(%d)\n", __FUNCTION__, gpio);
> + switch (gpio >> 4) {
> + case _IPAQ_ASIC3_GPIO_BANK_A:
> + return asic3_get_gpio_status_a(dev) & mask;
> + case _IPAQ_ASIC3_GPIO_BANK_B:
> + return asic3_get_gpio_status_b(dev) & mask;
> + case _IPAQ_ASIC3_GPIO_BANK_C:
> + return asic3_get_gpio_status_c(dev) & mask;
> + case _IPAQ_ASIC3_GPIO_BANK_D:
> + return asic3_get_gpio_status_d(dev) & mask;
> + }
> +
> + printk(KERN_ERR "%s: invalid GPIO value 0x%x", __FUNCTION__, gpio);
> + return 0;
> +}
> +EXPORT_SYMBOL(asic3_gpio_get_value);
> +
> +void asic3_gpio_set_value(struct device *dev, unsigned gpio, int val)
> +{
> + u32 mask = ASIC3_GPIO_bit(gpio);
> + u32 bitval = 0;
> + if (val) bitval = mask;
> + printk("%s(%d, %d)\n", __FUNCTION__, gpio, val);
> +
> + switch (gpio >> 4) {
> + case _IPAQ_ASIC3_GPIO_BANK_A:
> + asic3_set_gpio_out_a(dev, mask, bitval);
> + return;
> + case _IPAQ_ASIC3_GPIO_BANK_B:
> + asic3_set_gpio_out_b(dev, mask, bitval);
> + return;
> + case _IPAQ_ASIC3_GPIO_BANK_C:
> + asic3_set_gpio_out_c(dev, mask, bitval);
> + return;
> + case _IPAQ_ASIC3_GPIO_BANK_D:
> + asic3_set_gpio_out_d(dev, mask, bitval);
> + return;
> + }
> +
> + printk(KERN_ERR "%s: invalid GPIO value 0x%x", __FUNCTION__, gpio);
> +}
> +EXPORT_SYMBOL(asic3_gpio_set_value);

I assume all these debugging printks won't last long.

> +int asic3_irq_base(struct device *dev)
> +{
> + struct asic3_data *asic = dev->driver_data;
> +
> + return asic->irq_base;
> +}
> +EXPORT_SYMBOL(asic3_irq_base);
> +
> +void asic3_set_led(struct device *dev, int led_num, int duty_time,
> + int cycle_time, int timebase)
> +{
> + struct asic3_data *asic = dev->driver_data;
> + unsigned int led_base;
> +
> + /* it's a macro thing: see #define _IPAQ_ASIC_LED_0_Base for why you
> + * can't substitute led_num in the macros below...
> + */
> +
> + switch (led_num) {
> + case 0:
> + led_base = _IPAQ_ASIC3_LED_0_Base;
> + break;
> + case 1:
> + led_base = _IPAQ_ASIC3_LED_1_Base;
> + break;
> + case 2:
> + led_base = _IPAQ_ASIC3_LED_2_Base;
> + break;
> + default:
> + printk(KERN_ERR "%s: invalid led number %d", __FUNCTION__,
> + led_num);
> + return;
> + }
> +
> + __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_TimeBase,
> + timebase | LED_EN);
> + __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_PeriodTime,
> + cycle_time);
> + __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_DutyTime,
> + 0);
> + udelay(20); /* asic voodoo - possibly need a whole duty cycle? */
> + __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_DutyTime,
> + duty_time);
> +}
> +
> +EXPORT_SYMBOL(asic3_set_led);

Remove the line before EXPORT_SYMBOL().

> +void asic3_set_clock_sel(struct device *dev, u32 bits, u32 val)
> +{
> + struct asic3_data *asic = dev->driver_data;
> + unsigned long flags;
> + u32 v;
> +
> + spin_lock_irqsave(&asic3_gpio_lock, flags);
> + v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL));
> + v = (v & ~bits) | val;
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), v);
> + spin_unlock_irqrestore(&asic3_gpio_lock, flags);
> +}
> +EXPORT_SYMBOL(asic3_set_clock_sel);
> +
> +void asic3_set_clock_cdex(struct device *dev, u32 bits, u32 val)
> +{
> + struct asic3_data *asic = dev->driver_data;
> + unsigned long flags;
> + u32 v;
> +
> + spin_lock_irqsave(&asic3_gpio_lock, flags);
> + v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX));
> + v = (v & ~bits) | val;
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), v);
> + spin_unlock_irqrestore(&asic3_gpio_lock, flags);
> +}
> +EXPORT_SYMBOL(asic3_set_clock_cdex);
> +
> +static int asic3_clock_cdex_enable(struct clk *clk, int enable)
> +{
> + struct asic3_data *asic = (struct asic3_data *)clk->parent->ctrlbit;
> + unsigned long flags, val;
> +
> + local_irq_save(flags);
> +
> + val = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX));
> + if (enable)
> + val |= clk->ctrlbit;
> + else
> + val &= ~clk->ctrlbit;
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), val);
> +
> + local_irq_restore(flags);
> +
> + return 0;
> +}

How come asic3_clock_cdex_enable() uses local_irq_save() but the
similar-looking functions above use spin_lock_irqsave()?

> +
> +#define MAX_ASIC_ISR_LOOPS 20
> +#define _IPAQ_ASIC3_GPIO_Base_INCR \
> + (_IPAQ_ASIC3_GPIO_B_Base - _IPAQ_ASIC3_GPIO_A_Base)
> +
> +static inline void asic3_irq_flip_edge(struct asic3_data *asic,
> + u32 base, int bit)
> +{
> + u16 edge = __asic3_read_register(asic,
> + base + _IPAQ_ASIC3_GPIO_EdgeTrigger);
> + edge ^= bit;
> + __asic3_write_register(asic,
> + base + _IPAQ_ASIC3_GPIO_EdgeTrigger, edge);
> +}

This function doesn't need the spinlock?

> +static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc)
> +{
> + int iter;
> + struct asic3_data *asic;
> +
> + /* Acknowledge the parrent (i.e. CPU's) IRQ */
> + desc->chip->ack(irq);
> +
> + asic = desc->handler_data;
> +
> + /* printk( KERN_NOTICE "asic3_irq_demux: irq=%d\n", irq ); */
> + for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) {
> + u32 status;
> + int bank;
> +
> + status = __asic3_read_register(asic,
> + IPAQ_ASIC3_OFFSET(INTR, PIntStat));
> + /* Check all ten register bits */
> + if ((status & 0x3ff) == 0)
> + break;
> +
> + /* Handle GPIO IRQs */
> + for (bank = 0; bank < 4; bank++) {
> + if (status & (1 << bank)) {
> + unsigned long base, i, istat;
> +
> + base = _IPAQ_ASIC3_GPIO_A_Base
> + + bank * _IPAQ_ASIC3_GPIO_Base_INCR;
> + istat = __asic3_read_register(asic,
> + base + _IPAQ_ASIC3_GPIO_IntStatus);
> + /* IntStatus is write 0 to clear */
> + /* XXX could miss interrupts! */
> + __asic3_write_register(asic,
> + base + _IPAQ_ASIC3_GPIO_IntStatus, 0);

And neither does this?

> + for (i = 0; i < 16; i++) {

I hope the magical 16 is meaningful to those who are familiar with the
hardware.

> + int bit = (1 << i);
> + unsigned int irqnr;
> + if (!(istat & bit))
> + continue;
> +
> + irqnr = asic->irq_base
> + + (16 * bank) + i;
> + desc = irq_desc + irqnr;
> + desc->handle_irq(irqnr, desc);
> + if (asic->irq_bothedge[bank] & bit) {
> + asic3_irq_flip_edge(asic, base,
> + bit);
> + }
> + }
> + }
> + }
> +
> + /* Handle remaining IRQs in the status register */
> + {
> + int i;
> +
> + for (i = ASIC3_LED0_IRQ; i <= ASIC3_OWM_IRQ; i++) {
> + /* They start at bit 4 and go up */
> + if (status & (1 << (i - ASIC3_LED0_IRQ + 4))) {
> + desc = irq_desc + asic->irq_base + i;
> + desc->handle_irq(asic->irq_base + i,
> + desc);
> + }
> + }
> + }
> +
> + }
> +
> + if (iter >= MAX_ASIC_ISR_LOOPS)
> + printk(KERN_ERR "%s: interrupt processing overrun\n",
> + __FUNCTION__);
> +}
> +
> +static inline int asic3_irq_to_bank(struct asic3_data *asic, int irq)
> +{
> + int n;
> +
> + n = (irq - asic->irq_base) >> 4;
> +
> + return (n * (_IPAQ_ASIC3_GPIO_B_Base - _IPAQ_ASIC3_GPIO_A_Base));
> +}
> +
> +static inline int asic3_irq_to_index(struct asic3_data *asic, int irq)
> +{
> + return (irq - asic->irq_base) & 15;
> +}
> +
> +static void asic3_mask_gpio_irq(unsigned int irq)
> +{
> + struct asic3_data *asic = get_irq_chip_data(irq);
> + u32 val, bank, index;
> + unsigned long flags;
> +
> + bank = asic3_irq_to_bank(asic, irq);
> + index = asic3_irq_to_index(asic, irq);
> +
> + spin_lock_irqsave(&asic3_gpio_lock, flags);
> + val = __asic3_read_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask);
> + val |= 1 << index;
> + __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask, val);
> + spin_unlock_irqrestore(&asic3_gpio_lock, flags);
> +}

Locked.

> +static void asic3_mask_irq(unsigned int irq)
> +{
> + struct asic3_data *asic = get_irq_chip_data(irq);
> + int regval;
> +
> + if (irq < ASIC3_NR_GPIO_IRQS) {
> + printk(KERN_ERR "asic3_base: gpio mask attempt, irq %d\n",
> + irq);
> + return;
> + }
> +
> + regval = __asic3_read_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask);
> +
> + switch (irq - asic->irq_base) {
> + case ASIC3_LED0_IRQ:
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
> + regval & ~ASIC3_INTMASK_MASK0);
> + break;
> + case ASIC3_LED1_IRQ:
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
> + regval & ~ASIC3_INTMASK_MASK1);
> + break;
> + case ASIC3_LED2_IRQ:
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
> + regval & ~ASIC3_INTMASK_MASK2);
> + break;
> + case ASIC3_SPI_IRQ:
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
> + regval & ~ASIC3_INTMASK_MASK3);
> + break;
> + case ASIC3_SMBUS_IRQ:
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
> + regval & ~ASIC3_INTMASK_MASK4);
> + break;
> + case ASIC3_OWM_IRQ:
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
> + regval & ~ASIC3_INTMASK_MASK5);
> + break;
> + default:
> + printk(KERN_ERR "asic3_base: bad non-gpio irq %d\n", irq);
> + break;
> + }
> +}

Not locked!

Please add a comment to asic3_gpio_lock identifying what resource(s) it
protects.

> +static void asic3_unmask_gpio_irq(unsigned int irq)

sticky space bar.

> +{
> + struct asic3_data *asic = get_irq_chip_data(irq);
> + u32 val, bank, index;
> + unsigned long flags;
> +
> + bank = asic3_irq_to_bank(asic, irq);
> + index = asic3_irq_to_index(asic, irq);
> +
> + spin_lock_irqsave(&asic3_gpio_lock, flags);
> + val = __asic3_read_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask);
> + val &= ~(1 << index);
> + __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask, val);
> + spin_unlock_irqrestore(&asic3_gpio_lock, flags);
> +}
>
> ...
>
> +static int asic3_gpio_irq_type(unsigned int irq, unsigned int type)
> +{
> + struct asic3_data *asic = get_irq_chip_data(irq);
> + u32 bank, index;
> + unsigned long flags;
> + u16 trigger, level, edge, bit;
> +
> + bank = asic3_irq_to_bank(asic, irq);
> + index = asic3_irq_to_index(asic, irq);
> + bit = 1<<index;
> +
> + spin_lock_irqsave(&asic3_gpio_lock, flags);
> + level = __asic3_read_register(asic,
> + bank + _IPAQ_ASIC3_GPIO_LevelTrigger);
> + edge = __asic3_read_register(asic,
> + bank + _IPAQ_ASIC3_GPIO_EdgeTrigger);
> + trigger = __asic3_read_register(asic,
> + bank + _IPAQ_ASIC3_GPIO_TriggerType);
> + asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit;
> +
> + if (type == IRQT_RISING) {
> + trigger |= bit;
> + edge |= bit;
> + } else if (type == IRQT_FALLING) {
> + trigger |= bit;
> + edge &= ~bit;
> + } else if (type == IRQT_BOTHEDGE) {
> + trigger |= bit;
> + if (asic3_gpio_get_value(asic->dev, irq - asic->irq_base))
> + edge &= ~bit;
> + else
> + edge |= bit;
> + asic->irq_bothedge[(irq - asic->irq_base) >> 4] |= bit;
> + } else if (type == IRQT_LOW) {
> + trigger &= ~bit;
> + level &= ~bit;
> + } else if (type == IRQT_HIGH) {
> + trigger &= ~bit;
> + level |= bit;
> + } else {
> + /*
> + * if type == IRQT_NOEDGE, we should mask interrupts, but
> + * be careful to not unmask them if mask was also called.
> + * Probably need internal state for mask.
> + */
> + printk(KERN_NOTICE "asic3: irq type not changed.\n");
> + }
> + __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_LevelTrigger,
> + level);
> + __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_EdgeTrigger,
> + edge);
> + __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_TriggerType,
> + trigger);
> + spin_unlock_irqrestore(&asic3_gpio_lock, flags);
> + return 0;
> +}

Locking here looks good.

> +static struct irq_chip asic3_gpio_irq_chip = {
> + .name = "ASIC3-GPIO",
> + .ack = asic3_mask_gpio_irq,
> + .mask = asic3_mask_gpio_irq,
> + .unmask = asic3_unmask_gpio_irq,
> + .set_type = asic3_gpio_irq_type,
> +};
> +
> +static struct irq_chip asic3_irq_chip = {
> + .name = "ASIC3",
> + .ack = asic3_mask_irq,
> + .mask = asic3_mask_irq,
> + .unmask = asic3_unmask_irq,
> +};
> +
> +static void asic3_release(struct device *dev)
> +{
> + struct platform_device *sdev = to_platform_device(dev);
> +
> + kfree(sdev->resource);
> + kfree(sdev);
> +}
> +
> +int asic3_register_mmc(struct device *dev)
> +{
> + struct platform_device *sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
> + struct tmio_mmc_hwconfig *mmc_config = kmalloc(sizeof(*mmc_config),
> + GFP_KERNEL);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct asic3_data *asic = dev->driver_data;
> + struct asic3_platform_data *asic3_pdata = dev->platform_data;
> + struct resource *res;
> + int rc;
> +
> + if (sdev == NULL || mmc_config == NULL)
> + return -ENOMEM;

That'll leak *sdev if *mmc_config==NULL.

> + if (asic3_pdata->tmio_mmc_hwconfig) {
> + memcpy(mmc_config, asic3_pdata->tmio_mmc_hwconfig,
> + sizeof(*mmc_config));
> + } else {
> + memset(mmc_config, 0, sizeof(*mmc_config));
> + }
> + mmc_config->address_shift = asic->bus_shift;
> +
> + sdev->id = -1;
> + sdev->name = "asic3_mmc";
> + sdev->dev.parent = dev;
> + sdev->num_resources = 2;
> + sdev->dev.platform_data = mmc_config;
> + sdev->dev.release = asic3_release;
> +
> + res = kzalloc(sdev->num_resources * sizeof(struct resource),
> + GFP_KERNEL);
> + if (res == NULL) {
> + kfree(sdev);
> + kfree(mmc_config);
> + return -ENOMEM;
> + }
> + sdev->resource = res;
> +
> + res[0].start = pdev->resource[2].start;
> + res[0].end = pdev->resource[2].end;
> + res[0].flags = IORESOURCE_MEM;
> + res[1].start = res[1].end = pdev->resource[3].start;
> + res[1].flags = IORESOURCE_IRQ;
> +
> + rc = platform_device_register(sdev);
> + if (rc) {
> + printk(KERN_ERR "asic3_base: "
> + "Could not register asic3_mmc device\n");
> + kfree(res);
> + kfree(sdev);

kfree(mmc_config); ?

> + return rc;
> + }
> +
> + asic->mmc_dev = sdev;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(asic3_register_mmc);
> +
> +int asic3_unregister_mmc(struct device *dev)
> +{
> + struct asic3_data *asic = dev->driver_data;
> + platform_device_unregister(asic->mmc_dev);
> + asic->mmc_dev = 0;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(asic3_unregister_mmc);
> +
>
> ...
>
> + for (i = 0 ; i < ASIC3_NR_IRQS ; i++) {

Use
for (i = 0; i < ASIC3_NR_IRQS; i++) {

> + for (i = 0 ; i < ASIC3_NR_IRQS ; i++) {

Ditto (check all patches) (soon we'll have a script to do this) (hopefully)

> + int irq = i + asic->irq_base;
> + set_irq_flags(irq, 0);
> + set_irq_handler (irq, NULL);
> + set_irq_chip (irq, NULL);
> + set_irq_chip_data(irq, NULL);
> + }
> +
> + set_irq_chained_handler(asic->irq_nr, NULL);
> + }
> +
> + if (asic->mmc_dev)
> + asic3_unregister_mmc(&pdev->dev);
> +
> + for (i = 0; i < ARRAY_SIZE(asic3_clocks); i++)
> + clk_unregister(&asic3_clocks[i]);
> + clk_unregister(&clk_g);
> +
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), 0);
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask), 0);
> +
> + iounmap(asic->mapping);
> +
> + kfree(asic);
> +
> + return 0;
> +}
>
> ...
>
> +static int asic3_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> + struct asic3_data *asic = platform_get_drvdata(pdev);
> + suspend_cdex = __asic3_read_register(asic,
> + _IPAQ_ASIC3_CLOCK_Base + _IPAQ_ASIC3_CLOCK_CDEX);
> + /* The LEDs are still active during suspend */
> + __asic3_write_register(asic,
> + _IPAQ_ASIC3_CLOCK_Base + _IPAQ_ASIC3_CLOCK_CDEX,
> + suspend_cdex & ASIC3_SUSPEND_CDEX_MASK);
> + return 0;
> +}
> +
> +static int asic3_resume(struct platform_device *pdev)
> +{
> + struct asic3_data *asic = platform_get_drvdata(pdev);
> + unsigned short intmask;
> +
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX),
> + suspend_cdex);
> +
> + if (asic->irq_nr != -1) {
> + /* Toggle the interrupt mask to try to get ASIC3 to show
> + * the CPU an interrupt edge. For more details see the
> + * kernel-discuss thread around 13 June 2005 with the
> + * subject "asic3 suspend / resume". */
> + intmask = __asic3_read_register(asic,
> + IPAQ_ASIC3_OFFSET(INTR, IntMask));
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask),
> + intmask & ~ASIC3_INTMASK_GINTMASK);
> + mdelay(1);
> + __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask),
> + intmask | ASIC3_INTMASK_GINTMASK);
> + }
> +
> + return 0;
> +}
> +
> +static struct platform_driver asic3_device_driver = {
> + .driver = {
> + .name = "asic3",
> + },
> + .probe = asic3_probe,
> + .remove = asic3_remove,

Should .remove be __devexit_p()?

> + .suspend = asic3_suspend,
> + .resume = asic3_resume,
> + .shutdown = asic3_shutdown,
> +};

Does this driver have a Kconfig dependency upon CONFIG_PM?

If not, you should support CONFIG_PM=n. The typical way of doing that is

#ifdef CONFIG_PM
static int asic3_suspend(struct platform_device *pdev, pm_message_t state)
{
...
}

static int asic3_resume(struct platform_device *pdev)
{
...
}
#else
#define asic3_suspend NULL
#define asic3_resume NULL
#endif

> +static int __init asic3_base_init(void)
> +{
> + int retval = 0;
> + retval = platform_driver_register(&asic3_device_driver);
> + return retval;
> +}
> +
> +static void __exit asic3_base_exit(void)
> +{
> + platform_driver_unregister(&asic3_device_driver);
> +}
> +
> +#ifdef MODULE
> +module_init(asic3_base_init);
> +#else /* start early for dependencies */
> +subsys_initcall(asic3_base_init);
> +#endif

hm, I'd expect that subsys_initcall() from within a module will do the
right thing, in which case the ifdef isn't needed.

I certainly hope that's the case.

> +module_exit(asic3_base_exit);
>
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Phil Blundell <pb@xxxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Core driver for HTC ASIC3");
> +MODULE_SUPPORTED_DEVICE("asic3");
> diff --git a/include/linux/soc/asic3_base.h b/include/linux/soc/asic3_base.h
> new file mode 100644
> index 0000000..f17acda
> --- /dev/null
> +++ b/include/linux/soc/asic3_base.h
> @@ -0,0 +1,100 @@
> +#include <asm/types.h>
> +
> +/* Private API - for ASIC3 devices internal use only */
> +#define HDR_IPAQ_ASIC3_ACTION(ACTION,action,fn,FN) \
> +u32 asic3_get_gpio_ ## action ## _ ## fn (struct device *dev); \
> +void asic3_set_gpio_ ## action ## _ ## fn (struct device *dev, u32 bits, u32 val);
> +
> +#define HDR_IPAQ_ASIC3_FN(fn,FN) \
> + HDR_IPAQ_ASIC3_ACTION ( MASK,mask,fn,FN) \
> + HDR_IPAQ_ASIC3_ACTION ( DIR, dir, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( OUT, out, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( LEVELTRI, trigtype, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( RISING, rising, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( LEVEL, triglevel, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( SLEEP_MASK, sleepmask, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( SLEEP_OUT, sleepout, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( BATT_FAULT_OUT, battfaultout, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( INT_STATUS, intstatus, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( ALT_FUNCTION, alt_fn, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( SLEEP_CONF, sleepconf, fn, FN) \
> + HDR_IPAQ_ASIC3_ACTION ( STATUS, status, fn, FN)

s/ (/(/g

> +struct tmio_mmc_hwconfig;
> +
> +struct asic3_platform_data
> +{

struct asic3_platform_data {

(review whole patchset)

> + struct {
> + u32 dir;
> + u32 init;
> + u32 sleep_mask;
> + u32 sleep_out;
> + u32 batt_fault_out;
> + u32 sleep_conf;
> + u32 alt_function;
> + } gpio_a, gpio_b, gpio_c, gpio_d;
> +
> + int irq_base;
> + unsigned int bus_shift;
> +
> + struct platform_device **child_platform_devs;
> + int num_child_platform_devs;
> +
> + struct tmio_mmc_hwconfig *tmio_mmc_hwconfig;
> +};
> diff --git a/include/linux/soc/tmio_mmc.h b/include/linux/soc/tmio_mmc.h
> new file mode 100644
> index 0000000..b8c407c
> --- /dev/null
> +++ b/include/linux/soc/tmio_mmc.h
> @@ -0,0 +1,17 @@
> +#include <linux/platform_device.h>
> +
> +#define MMC_CLOCK_DISABLED 0
> +#define MMC_CLOCK_ENABLED 1
> +
> +#define TMIO_WP_ALWAYS_RW ((void*)-1)
> +
> +struct tmio_mmc_hwconfig {
> + void (*hwinit)(struct platform_device *sdev);
> + void (*set_mmc_clock)(struct platform_device *sdev, int state);
> +
> + /* NULL - use ASIC3 signal,
> + TMIO_WP_ALWAYS_RW - assume always R/W (e.g. miniSD)
> + otherwise - machine-specific handler */
> + int (*mmc_get_ro)(struct platform_device *pdev);
> + short address_shift;
> +};

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