diff options
author | Rabin Vincent <rabin.vincent@stericsson.com> | 2011-03-23 12:12:25 +0530 |
---|---|---|
committer | Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> | 2011-03-29 12:17:56 +0200 |
commit | 528832e68a9511be40ef376fb16ca853b476d62a (patch) | |
tree | 1ded390501c6de14a767b970b5c5cc6c2e59f566 | |
parent | 5934afc287ab23d88c4b8851e7b05a5c30aa07d5 (diff) |
ab5500: fixes and basic irq supportu8500-android-2.3_v0.47
- Remove old kernel version compat code
- Make some functions static
- Move to irq chip bus lock
- Fix broken irq handler registration
- Fix interrupt count
- Use direct interrupt instead of via unimplemented PRCMU
- Add RTC interrupts
- Fix RTC register list
- Allow get_page to read more than 4 registers
ST-Ericsson Linux next: -
ST-Ericsson ID: WP257121
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: I590f46f1c8f83bbcb159d743a52bb4b7628c7d50
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/18936
Reviewed-by: Bibek BASU <bibek.basu@stericsson.com>
Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
-rw-r--r-- | arch/arm/mach-ux500/board-u5500.c | 9 | ||||
-rw-r--r-- | arch/arm/mach-ux500/include/mach/irqs-board-u5500.h | 2 | ||||
-rwxr-xr-x | drivers/mfd/ab5500-core.c | 334 |
3 files changed, 131 insertions, 214 deletions
diff --git a/arch/arm/mach-ux500/board-u5500.c b/arch/arm/mach-ux500/board-u5500.c index 0aff38f2777..61bf8c18f26 100644 --- a/arch/arm/mach-ux500/board-u5500.c +++ b/arch/arm/mach-ux500/board-u5500.c @@ -91,6 +91,9 @@ static pin_cfg_t u5500_pins[] = { GPIO169_SPI1_TXD | PIN_OUTPUT_LOW, GPIO170_SPI1_CLK | PIN_OUTPUT_LOW, + /* AB5500 */ + GPIO78_IRQn, + /* TOUCH_IRQ */ GPIO179_GPIO | PIN_INPUT_PULLUP, }; @@ -318,12 +321,6 @@ static struct ab5500_platform_data ab5500_plf_data = { .base = IRQ_AB5500_BASE, .count = AB5500_NR_IRQS, }, - .dev_data = { - }, - .dev_data_sz = { - }, - .init_settings = NULL, - .init_settings_sz = 0, }; static struct platform_device u5500_ab5500_device = { diff --git a/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h b/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h index 29d972c7717..48f9f382b29 100644 --- a/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h +++ b/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h @@ -7,7 +7,7 @@ #ifndef __MACH_IRQS_BOARD_U5500_H #define __MACH_IRQS_BOARD_U5500_H -#define AB5500_NR_IRQS 5 +#define AB5500_NR_IRQS (23 * 8) #define IRQ_AB5500_BASE IRQ_BOARD_START #define IRQ_AB5500_END (IRQ_AB5500_BASE + AB5500_NR_IRQS) diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c index 4ab5c9561f7..91d7e8f9e1d 100755 --- a/drivers/mfd/ab5500-core.c +++ b/drivers/mfd/ab5500-core.c @@ -18,7 +18,6 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/random.h> -#include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> @@ -48,6 +47,11 @@ #define AB5500_MASK_END (0x79) #define AB5500_CHIP_ID (0x20) +#define AB5500_IT_LATCH0_REG 0x40 +#define AB5500_IT_MASK0_REG 0x60 +#define AB5500_NUM_IRQ_REGS 23 +#define AB5500_NR_IRQS (23 * 8) + /** * struct ab5500 * @access_mutex: lock out concurrent accesses to the AB registers @@ -56,8 +60,7 @@ * @irq_base: the platform configuration irq base for subdevices * @chip_name: name of this chip variant * @chip_id: 8 bit chip ID for this chip variant - * @mask_work: a worker for writing to mask registers - * @event_lock: a lock to protect the event_mask + * @irq_lock: a lock to protect the mask * @abb_events: a local bit mask of the prcmu wakeup events * @event_mask: a local copy of the mask event registers * @last_event_mask: a copy of the last event_mask written to hardware @@ -71,15 +74,11 @@ struct ab5500 { unsigned int irq_base; char chip_name[32]; u8 chip_id; - struct work_struct mask_work; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) - struct work_struct irq_work; -#endif - spinlock_t event_lock; + struct mutex irq_lock; u32 abb_events; - u8 event_mask[AB5500_NUM_EVENT_REG]; - u8 last_event_mask[AB5500_NUM_EVENT_REG]; - u8 startup_events[AB5500_NUM_EVENT_REG]; + u8 mask[AB5500_NUM_IRQ_REGS]; + u8 oldmask[AB5500_NUM_IRQ_REGS]; + u8 startup_events[AB5500_NUM_IRQ_REGS]; bool startup_events_read; #ifdef CONFIG_DEBUG_FS unsigned int debug_bank; @@ -250,7 +249,7 @@ static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = { .nbanks = 1, .bank = (struct ab5500_i2c_ranges[]) { { - .bankid = AB5500_BANK_LED, + .bankid = AB5500_BANK_RTC, .nranges = 1, .range = (struct ab5500_reg_range[]) { { @@ -351,6 +350,8 @@ static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = { }, }; +#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit)) + /* I appologize for the resource names beeing a mix of upper case * and lower case but I want them to be exact as the documentation */ static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { @@ -382,6 +383,15 @@ static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { [AB5500_DEVID_RTC] = { .name = "ab5500-rtc", .id = AB5500_DEVID_RTC, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "RTC_Alarm", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 7), + .end = AB5500_IRQ(1, 7), + } + }, }, [AB5500_DEVID_CHARGER] = { .name = "ab5500-charger", @@ -868,17 +878,25 @@ static int get_register_page_interruptible(struct ab5500 *ab, u8 bank, if (bank >= AB5500_NUM_BANKS) return -EINVAL; - /* The hardware limit for get page is 4 */ - if (numregs > 4) - return -EINVAL; - err = mutex_lock_interruptible(&ab->access_mutex); if (err) return err; - err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, first_reg, - regvals, numregs); + while (numregs) { + /* The hardware limit for get page is 4 */ + u8 curnum = min_t(u8, numregs, 4u); + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + first_reg, regvals, curnum); + if (err) + goto out; + + numregs -= curnum; + first_reg += curnum; + regvals += curnum; + } + +out: mutex_unlock(&ab->access_mutex); return err; } @@ -917,6 +935,12 @@ static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, return err; } +static int +set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value) +{ + return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value); +} + /* * Read/write permission checking functions. */ @@ -999,15 +1023,15 @@ static bool reg_read_allowed(u8 devid, u8 bank, u8 reg) /* * The exported register access functionality. */ -int ab5500_get_chip_id(struct device *dev) +static int ab5500_get_chip_id(struct device *dev) { struct ab5500 *ab = dev_get_drvdata(dev->parent); return (int)ab->chip_id; } -int ab5500_mask_and_set_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) +static int ab5500_mask_and_set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { struct ab5500 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -1021,15 +1045,15 @@ int ab5500_mask_and_set_register_interruptible(struct device *dev, u8 bank, bitmask, bitvalues); } -int ab5500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, - u8 value) +static int ab5500_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 value) { return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, value); } -int ab5500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, - u8 *value) +static int ab5500_get_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 *value) { struct ab5500 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -1042,8 +1066,8 @@ int ab5500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, return get_register_interruptible(ab, bank, reg, value); } -int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) +static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) { struct ab5500 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -1058,7 +1082,8 @@ int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, numregs); } -int ab5500_event_registers_startup_state_get(struct device *dev, u8 *event) +static int +ab5500_event_registers_startup_state_get(struct device *dev, u8 *event) { struct ab5500 *ab; @@ -1070,7 +1095,7 @@ int ab5500_event_registers_startup_state_get(struct device *dev, u8 *event) return 0; } -int ab5500_startup_irq_enabled(struct device *dev, unsigned int irq) +static int ab5500_startup_irq_enabled(struct device *dev, unsigned int irq) { struct ab5500 *ab; bool val; @@ -1094,102 +1119,35 @@ static struct abx500_ops ab5500_ops = { .startup_irq_enabled = ab5500_startup_irq_enabled, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -static irqreturn_t ab5500_irq_handler(int irq, void *data) +static irqreturn_t ab5500_irq(int irq, void *data) { struct ab5500 *ab = data; + u8 i; /* - * Disable the IRQ and dispatch a worker to handle the - * event. Since the chip resides on I2C this is slow - * stuff and we will re-enable the interrupts once the - * worker has finished. + * TODO: use the ITMASTER registers to reduce the number of i2c reads. */ - disable_irq_nosync(irq); - schedule_work(&ab->irq_work); - return IRQ_HANDLED; -} - -static void ab5500_irq_work(struct work_struct *work) -{ - struct ab5500 *ab = container_of(work, struct ab5500, irq_work); - u8 i; - u8 *e = 0; - u8 events[AB5500_NUM_EVENT_REG]; - unsigned long flags; - - prcmu_get_abb_event_buf(&e); - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB5500_NUM_EVENT_REG; i++) - events[i] = e[i] & ~ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - local_irq_disable(); - for (i = 0; i < AB5500_NUM_EVENT_REG; i++) { - u8 bit; - u8 event_reg; - - dev_dbg(ab->dev, "IRQ Event[%d]: 0x%2x\n", - i, events[i]); - - event_reg = events[i]; - for (bit = 0; event_reg; bit++, event_reg /= 2) { - if (event_reg % 2) { - unsigned int irq; - struct irq_desc *desc; - - irq = ab->irq_base + (i * 8) + bit; - desc = irq_to_desc(irq); - if (desc->status & IRQ_DISABLED) - note_interrupt(irq, desc, IRQ_NONE); - else - desc->handle_irq(irq, desc); - } - } - } - local_irq_enable(); - /* By now the IRQ should be acked and deasserted so enable it again */ - enable_irq(ab->ab5500_irq); -} - -#else - -static irqreturn_t ab5500_irq_handler(int irq, void *data) -{ - struct ab5500 *ab = data; - u8 i; - u8 *e = 0; - u8 events[AB5500_NUM_EVENT_REG]; - - prcmu_get_abb_event_buf(&e); - - spin_lock(&ab->event_lock); - for (i = 0; i < AB5500_NUM_EVENT_REG; i++) - events[i] = e[i] & ~ab->event_mask[i]; - spin_unlock(&ab->event_lock); for (i = 0; i < AB5500_NUM_EVENT_REG; i++) { - u8 bit; - u8 event_reg; + int status; + u8 value; - dev_dbg(ab->dev, "IRQ Event[%d]: 0x%2x\n", - i, events[i]); + status = get_register_interruptible(ab, AB5500_BANK_IT, + AB5500_IT_LATCH0_REG + i, &value); + if (status < 0 || value == 0) + continue; - event_reg = events[i]; - for (bit = 0; event_reg; bit++, event_reg /= 2) { - if (event_reg % 2) { - unsigned int irq; + do { + int bit = __ffs(value); + int line = i * 8 + bit; - irq = ab->irq_base + (i * 8) + bit; - generic_handle_irq(irq); - } - } + handle_nested_irq(ab->irq_base + line); + value &= ~(1 << bit); + } while (value); } return IRQ_HANDLED; } -#endif #ifdef CONFIG_DEBUG_FS static struct ab5500_i2c_ranges debug_ranges[AB5500_NUM_BANKS] = { @@ -1854,7 +1812,7 @@ static int __init ab5500_setup(struct ab5500 *ab, if ((settings[i].bank == AB5500_BANK_IT) && (AB5500_MASK_BASE <= settings[i].reg) && (settings[i].reg <= AB5500_MASK_END)) { - ab->event_mask[settings[i].reg - AB5500_MASK_BASE] = + ab->mask[settings[i].reg - AB5500_MASK_BASE] = settings[i].setting; } } @@ -1862,95 +1820,61 @@ exit_no_setup: return err; } -static void ab5500_mask_work(struct work_struct *work) +static void ab5500_irq_mask(unsigned int irq) { - struct ab5500 *ab = container_of(work, struct ab5500, mask_work); - int i; - int err; - unsigned long flags; - u8 mask[AB5500_NUM_EVENT_REG]; - int call_prcmu_event_readout = 0; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB5500_NUM_EVENT_REG; i++) - mask[i] = ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB5500_NUM_EVENT_REG; i++) { - if (mask[i] != ab->last_event_mask[i]) { - err = mask_and_set_register_interruptible(ab, 0, - (AB5500_MASK_BASE + i), ~0, mask[i]); - if (err) { - dev_err(ab->dev, - "ab5500_mask_work failed 0x%x,0x%x\n", - (AB5500_MASK_BASE + i), mask[i]); - break; - } - - if (mask[i] == 0xFF) { - ab->abb_events &= ~BIT(i); - call_prcmu_event_readout = 1; - } else { - ab->abb_events |= BIT(i); - if (ab->last_event_mask[i] == 0xFF) - call_prcmu_event_readout = 1; - } + struct ab5500 *ab = get_irq_chip_data(irq); + int offset = irq - ab->irq_base; + int index = offset / 8; + int mask = BIT(offset % 8); - ab->last_event_mask[i] = mask[i]; - } - } - if (call_prcmu_event_readout) { - err = db5500_prcmu_config_abb_event_readout(ab->abb_events); - if (err) - dev_err(ab->dev, - "prcmu_config_abb_event_readout failed\n"); - } + ab->mask[index] |= mask; } -static void ab5500_mask(unsigned int irq) +static void ab5500_irq_unmask(unsigned int irq) { - unsigned long flags; - struct ab5500 *ab; + struct ab5500 *ab = get_irq_chip_data(irq); + int offset = irq - ab->irq_base; + int index = offset / 8; + int mask = BIT(offset % 8); - ab = get_irq_chip_data(irq); - irq -= ab->irq_base; + ab->mask[index] &= ~mask; +} - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] |= BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); +static void ab5500_irq_lock(unsigned int irq) +{ + struct ab5500 *ab = get_irq_chip_data(irq); - schedule_work(&ab->mask_work); + mutex_lock(&ab->irq_lock); } -static void ab5500_unmask(unsigned int irq) +static void ab5500_irq_sync_unlock(unsigned int irq) { - unsigned long flags; - struct ab5500 *ab; + struct ab5500 *ab = get_irq_chip_data(irq); + int i; - ab = get_irq_chip_data(irq); - irq -= ab->irq_base; + for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) { + u8 old = ab->oldmask[i]; + u8 new = ab->mask[i]; + int reg; - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] &= ~BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); + if (new == old) + continue; - schedule_work(&ab->mask_work); -} + ab->oldmask[i] = new; -static void noop(unsigned int irq) -{ + reg = AB5500_IT_MASK0_REG + i; + set_register_interruptible(ab, AB5500_BANK_IT, reg, new); + } + + mutex_unlock(&ab->irq_lock); } static struct irq_chip ab5500_irq_chip = { - .name = "ab5500-core", /* Keep the same name as the request */ - .startup = NULL, /* defaults to enable */ - .shutdown = NULL, /* defaults to disable */ - .enable = NULL, /* defaults to unmask */ - .disable = ab5500_mask, /* No default to mask in chip.c */ - .ack = noop, - .mask = ab5500_mask, - .unmask = ab5500_unmask, - .end = NULL, + .name = "ab5500", + .mask = ab5500_irq_mask, + .unmask = ab5500_irq_unmask, + .bus_lock = ab5500_irq_lock, + .bus_sync_unlock = ab5500_irq_sync_unlock, }; struct ab_family_id { @@ -1992,7 +1916,7 @@ static int __init ab5500_probe(struct platform_device *pdev) /* Initialize data structure */ mutex_init(&ab->access_mutex); - spin_lock_init(&ab->event_lock); + mutex_init(&ab->irq_lock); ab->dev = &pdev->dev; ab->irq_base = ab5500_plf_data->irq.base; @@ -2034,11 +1958,6 @@ static int __init ab5500_probe(struct platform_device *pdev) goto exit_no_setup; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) - INIT_WORK(&ab->irq_work, ab5500_irq_work); -#endif - INIT_WORK(&ab->mask_work, ab5500_mask_work); - for (i = 0; i < ab5500_plf_data->irq.count; i++) { unsigned int irq; @@ -2046,36 +1965,38 @@ static int __init ab5500_probe(struct platform_device *pdev) set_irq_chip_data(irq, ab); set_irq_chip_and_handler(irq, &ab5500_irq_chip, handle_simple_irq); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) set_irq_nested_thread(irq, 1); -#endif +#ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { dev_err(&pdev->dev, "ab5500_platform_get_resource error\n"); goto exit_no_irq; } ab->ab5500_irq = res->start; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) - /* This really unpredictable IRQ is of course sampled for entropy. */ - err = request_irq(res->start, ab5500_irq_handler, - (IRQF_DISABLED | IRQF_SAMPLE_RANDOM), "ab5500-core", ab); - if (err) { - dev_err(&pdev->dev, "ab5500_request_irq error\n"); - goto exit_no_irq; + /* Clear and mask all interrupts */ + for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) { + u8 latchreg = AB5500_IT_LATCH0_REG + i; + u8 maskreg = AB5500_IT_MASK0_REG + i; + u8 val; + + get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val); + set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff); } - /* We probably already got an irq here, but if not, - * we force a first time and save the startup events here.*/ - disable_irq_nosync(res->start); - schedule_work(&ab->irq_work); -#else - err = request_threaded_irq(res->start, ab5500_irq_handler, NULL, - IRQF_SAMPLE_RANDOM, "ab5500-core", ab); + for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) + ab->mask[i] = ab->oldmask[i] = 0xff; + + err = request_threaded_irq(res->start, NULL, ab5500_irq, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab5500-core", ab); + /* This real unpredictable IRQ is of course sampled for entropy */ rand_initialize_irq(res->start); @@ -2083,7 +2004,6 @@ static int __init ab5500_probe(struct platform_device *pdev) dev_err(&pdev->dev, "ab5500_request_irq error\n"); goto exit_no_irq; } -#endif err = abx500_register_ops(&pdev->dev, &ab5500_ops); if (err) { |