| /* |
| * zx234290-irq.c -- Interrupt controller support for ZX234290 PMICs |
| * |
| * Copyright 2016 ZTE Inc. |
| * |
| * Author: yuxiang<yu.xiang5@zte.com.cn> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| * |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/bug.h> |
| #include <linux/device.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/gpio.h> |
| #include <linux/mfd/zx234290.h> |
| #include <mach/pcu.h> |
| #include <mach/gpio.h> |
| #include <linux/sched.h> |
| #include <linux/semaphore.h> |
| #include <linux/kthread.h> |
| |
| #define INT_LOW_LEVEL /* yuxiang */ |
| #define ZX234290_INT_DEBUG |
| #ifdef ZX234290_INT_DEBUG |
| unsigned long int_irq_times = 0; |
| unsigned long int_thread_times = 0; |
| #endif |
| struct semaphore zx234290_sem; |
| |
| #define ZX234290_WAKELOCK 1 |
| #ifdef ZX234290_WAKELOCK |
| #include <linux/wakelock.h> |
| static struct wake_lock zx234290_wake_lock; |
| #endif |
| |
| #ifdef ZX234290_PWR_FAUL_PROCESS |
| extern int zx234290_regulator_error_irq_request(struct zx234290 *zx234290); |
| #endif |
| |
| static inline int irq_to_zx234290_irq(struct zx234290 *zx234290, |
| int irq) |
| { |
| return irq - zx234290->irq_base; |
| } |
| |
| /* |
| * This is a threaded IRQ handler so can access I2C/SPI. Since the |
| * IRQ handler explicitly clears the IRQ it handles the IRQ line |
| * will be reasserted and the physical IRQ will be handled again if |
| * another interrupt is asserted while we run - in the normal course |
| * of events this is a rare occurrence so we save I2C/SPI reads. We're |
| * also assuming that it's rare to get lots of interrupts firing |
| * simultaneously so try to minimise I/O. |
| */ |
| static int zx234290_irq(void *irq_data) |
| { |
| struct zx234290 *zx234290 = irq_data; |
| u32 irq_sts; |
| u32 irq_mask; |
| u8 buck_sts=0, buck_mask=0; |
| u8 ldo_sts=0, ldo_mask=0; |
| u8 reg; |
| int i; |
| int irq = zx234290->chip_irq; |
| |
| const struct sched_param param = { |
| .sched_priority = 31, |
| }; |
| sched_setscheduler(current, SCHED_FIFO, ¶m); |
| |
| while (!kthread_should_stop()) { |
| down(&zx234290_sem); |
| #ifdef ZX234290_WAKELOCK |
| wake_lock(&zx234290_wake_lock); |
| #endif |
| |
| #ifdef ZX234290_INT_DEBUG |
| int_thread_times ++; |
| #endif |
| //printk(KERN_INFO "zx234290 irq handler:irq=%d.\n", irq); |
| |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| irq_sts = reg; |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTB, 1, ®); |
| irq_sts |= reg << 8; |
| |
| // printk(KERN_INFO "zx234290 irq handler:irq=%d, INTR_A=0x%02x, INTR_B=0x%02x\n", irq, irq_sts&0xFF, irq_sts>>8); |
| |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTA_MASK, 1, ®); |
| irq_mask = reg; |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, ®); |
| irq_mask |= reg << 8; |
| |
| irq_sts &= ~irq_mask; /* */ |
| |
| #ifdef ZX234290_PWR_FAUL_PROCESS |
| //++ |
| zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_FAULT_STATUS, 1, &buck_sts); |
| zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_FAULT_STATUS, 1, &ldo_sts); |
| zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, &buck_mask); |
| zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, &ldo_mask); |
| |
| buck_sts &= ~buck_mask; |
| ldo_sts &= ~ldo_mask; |
| buck_sts &=~(1<<ZX234290_LDO_RSTERR_LSH);//clear rst flag |
| if(buck_sts ) |
| irq_sts |= (1 << ZX234290_INT_BUCK_FAUL); |
| else |
| irq_sts &= ~(1 << ZX234290_INT_BUCK_FAUL); |
| |
| if(ldo_sts ) |
| irq_sts |= (1 << ZX234290_INT_LDO_FAUL); |
| else |
| irq_sts &= ~(1 << ZX234290_INT_LDO_FAUL); |
| |
| // if(buck_sts || ldo_sts) |
| // printk(KERN_INFO "zx234290 irq handler:buck_sts=0x%02x, ldo_sts=0x%02x, irq_sts=0x%x\n", buck_sts, ldo_sts, irq_sts); |
| #endif |
| |
| if (!irq_sts) |
| { |
| |
| #ifdef ZX234290_WAKELOCK |
| wake_unlock(&zx234290_wake_lock); |
| #endif |
| |
| #ifdef INT_LOW_LEVEL |
| pcu_clr_irq_pending(irq); |
| enable_irq(irq);//yx |
| #endif |
| //return IRQ_NONE; |
| continue; |
| } |
| |
| for (i = 0; i < zx234290->irq_num; i++) { |
| if (!(irq_sts & (1 << i))) |
| continue; |
| |
| handle_nested_irq(zx234290->irq_base + i); |
| } |
| /*write bit7 to be 0, clear pmu INT*/ |
| //zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| //reg &= ~(0x1<<7); |
| //zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| #if 0 |
| /* Write the STS register back to clear IRQs we handled */ |
| reg = irq_sts & 0xFF; |
| irq_sts >>= 8; |
| if (reg) |
| zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| reg = irq_sts & 0xFF; |
| irq_sts >>= 8; |
| if (reg) |
| zx234290->write(zx234290, ZX234290_REG_ADDR_INTB, 1, ®); |
| #endif |
| |
| #ifdef ZX234290_WAKELOCK |
| wake_unlock(&zx234290_wake_lock); |
| #endif |
| |
| |
| #ifdef INT_LOW_LEVEL |
| pcu_clr_irq_pending(irq); |
| enable_irq(irq);//yx |
| #endif |
| } |
| return 0; |
| } |
| |
| static void zx234290_irq_lock(struct irq_data *data) |
| { |
| struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data); |
| |
| mutex_lock(&zx234290->irq_lock); |
| } |
| |
| static void zx234290_irq_sync_unlock(struct irq_data *data) |
| { |
| struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data); |
| u32 reg_mask; |
| u8 reg, reg2; |
| |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTA_MASK, 1, ®); |
| reg_mask = reg; |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, ®); |
| reg_mask |= reg << 8; |
| /* take ldo6 & ldo8 error as buck error */ |
| zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, ®); |
| if (reg) |
| reg_mask |= BIT(ZX234290_INT_BUCK_FAUL); |
| zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, ®); |
| if (reg) |
| reg_mask |= BIT(ZX234290_INT_LDO_FAUL); |
| |
| if (zx234290->irq_mask != reg_mask) { |
| reg = zx234290->irq_mask & 0xFC; |
| zx234290->write(zx234290, ZX234290_REG_ADDR_INTA_MASK, 1, ®); |
| |
| reg = zx234290->irq_mask >> 8 & 0xFF; |
| zx234290->write(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, ®); |
| |
| reg = (zx234290->irq_mask & BIT(ZX234290_INT_BUCK_FAUL)) ? 0xFF : 0; |
| zx234290->write(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, ®); |
| |
| reg = (zx234290->irq_mask & BIT(ZX234290_INT_LDO_FAUL)) ? 0xFF : 0; |
| zx234290->write(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, ®); |
| } |
| |
| mutex_unlock(&zx234290->irq_lock); |
| } |
| |
| static void zx234290_irq_enable(struct irq_data *data) |
| { |
| struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data); |
| |
| zx234290->irq_mask &= ~(1 << irq_to_zx234290_irq(zx234290, data->irq)); |
| } |
| |
| static void zx234290_irq_disable(struct irq_data *data) |
| { |
| struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data); |
| |
| zx234290->irq_mask |= (1 << irq_to_zx234290_irq(zx234290, data->irq)); |
| } |
| |
| static struct irq_chip zx234290_irq_chip = { |
| .name = "zx234290", |
| .irq_bus_lock = zx234290_irq_lock, |
| .irq_bus_sync_unlock = zx234290_irq_sync_unlock, |
| .irq_disable = zx234290_irq_disable, |
| .irq_enable = zx234290_irq_enable, |
| }; |
| static irqreturn_t irq_primary_handler(int irq, void *dev_id) |
| { |
| #ifdef INT_LOW_LEVEL |
| disable_irq_nosync(irq);//yx |
| #endif |
| //pcu_int_clear(PCU_EX0_INT); //xzg |
| pcu_clr_irq_pending(irq); |
| up(&zx234290_sem); |
| #ifdef ZX234290_INT_DEBUG |
| int_irq_times ++; |
| #endif |
| |
| //return IRQ_WAKE_THREAD; |
| return IRQ_HANDLED; |
| } |
| |
| int zx234290_irq_init(struct zx234290 *zx234290, int irq, |
| struct zx234290_board *pdata) |
| { |
| int ret, cur_irq; |
| /* int flags = IRQF_ONESHOT; */ |
| u8 reg; |
| |
| #ifdef ZX234290_WAKELOCK |
| wake_lock_init(&zx234290_wake_lock, WAKE_LOCK_SUSPEND, "zx234290"); |
| #endif |
| |
| if (!irq) { |
| dev_warn(zx234290->dev, "No interrupt support, no core IRQ\n"); |
| return 0; |
| } |
| |
| if (!pdata || !pdata->irq_base) { |
| dev_warn(zx234290->dev, "No interrupt support, no IRQ base\n"); |
| return 0; |
| } |
| |
| sema_init(&zx234290_sem, 0); |
| /* Clear unattended interrupts */ |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| //zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTB, 1, ®); |
| //zx234290->write(zx234290, ZX234290_REG_ADDR_INTB, 1, ®); |
| |
| /*write bit7 to be 0, clear pmu INT*/ |
| zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| reg &= ~(0x1<<7); |
| zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, ®); |
| |
| /* Mask top level interrupts */ |
| zx234290->irq_mask = 0xFFFF; |
| |
| mutex_init(&zx234290->irq_lock); |
| zx234290->chip_irq = irq; /* irq */ |
| zx234290->irq_base = pdata->irq_base; /* irq_base 32+49 */ |
| |
| zx234290->irq_num = ZX234290_NUM_IRQ; |
| |
| /* Register with genirq */ |
| for (cur_irq = zx234290->irq_base; |
| cur_irq < zx234290->irq_num + zx234290->irq_base; |
| cur_irq++) { |
| irq_set_chip_data(cur_irq, zx234290); |
| irq_set_chip_and_handler(cur_irq, &zx234290_irq_chip, |
| handle_edge_irq); |
| irq_set_nested_thread(cur_irq, 1); |
| /* ARM needs us to explicitly flag the IRQ as valid |
| * and will set them noprobe when we do so. */ |
| #ifdef CONFIG_ARM |
| set_irq_flags(cur_irq, IRQF_VALID); |
| #else |
| irq_set_noprobe(cur_irq); |
| #endif |
| } |
| |
| #ifdef ZX234290_PWR_FAUL_PROCESS |
| zx234290_regulator_error_irq_request(zx234290); |
| #endif |
| //unsigned int gpio_num = irq_to_gpio(irq);//by yuxiang |
| //#define GPIO_PMU_INT ZX29_GPIO_50 |
| /* GPIO reuse*/ |
| zx29_gpio_config(pdata->irq_gpio_num, pdata->irq_gpio_func); |
| #ifdef INT_LOW_LEVEL |
| zx29_gpio_set_inttype(pdata->irq_gpio_num, IRQ_TYPE_LEVEL_LOW); |
| #else |
| zx29_gpio_set_inttype(pdata->irq_gpio_num, IRQ_TYPE_EDGE_FALLING); |
| #endif |
| zx29_gpio_pd_pu_set(pdata->irq_gpio_num, 0); |
| //pcu_int_clear(PCU_EX0_INT); //by yuxiang |
| pcu_clr_irq_pending(irq); |
| //irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);//by yuxiang |
| //ret = request_threaded_irq(irq, irq_primary_handler, zx234290_irq, IRQF_ONESHOT, |
| // "zx234290", zx234290); |
| //µÈµ½pmuËùÓÐÄÚ²¿ÖжÏ×¢²áºó£¬ÔÙʹÄÜpmuÖÐ¶Ï |
| /* irq_set_status_flags(irq, IRQ_NOAUTOEN); */ |
| ret = request_irq(irq, irq_primary_handler, IRQF_NO_THREAD, |
| "zx234290", zx234290); |
| if (ret != 0) |
| dev_err(zx234290->dev, "Failed to request IRQ: %d\n", ret); |
| #ifndef CONFIG_ARCH_ZX297520V3_CAP |
| irq_set_irq_wake(irq, 1); |
| #endif |
| kthread_run(zx234290_irq, zx234290, "irq/%d-%s", irq, "zx234290"); |
| return ret; |
| } |
| |
| int zx234290_irq_exit(struct zx234290 *zx234290) |
| { |
| free_irq(zx234290->chip_irq, zx234290); |
| |
| #ifdef ZX234290_WAKELOCK |
| wake_lock_destroy(&zx234290_wake_lock); |
| #endif |
| |
| return 0; |
| } |