blob: 1d466eeb5d88691feea3568ff085d7758aa177bc [file] [log] [blame]
/*
* 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, &param);
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, &reg);
irq_sts = reg;
zx234290->read(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
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, &reg);
irq_mask = reg;
zx234290->read(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, &reg);
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);
//reg &= ~(0x1<<7);
//zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
#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);
reg = irq_sts & 0xFF;
irq_sts >>= 8;
if (reg)
zx234290->write(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
#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);
reg_mask = reg;
zx234290->read(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, &reg);
reg_mask |= reg << 8;
/* take ldo6 & ldo8 error as buck error */
zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, &reg);
if (reg)
reg_mask |= BIT(ZX234290_INT_BUCK_FAUL);
zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, &reg);
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);
reg = zx234290->irq_mask >> 8 & 0xFF;
zx234290->write(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, &reg);
reg = (zx234290->irq_mask & BIT(ZX234290_INT_BUCK_FAUL)) ? 0xFF : 0;
zx234290->write(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, &reg);
reg = (zx234290->irq_mask & BIT(ZX234290_INT_LDO_FAUL)) ? 0xFF : 0;
zx234290->write(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, &reg);
}
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, &reg);
//zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
zx234290->read(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
//zx234290->write(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
/*write bit7 to be 0, clear pmu INT*/
zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
reg &= ~(0x1<<7);
zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
/* 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;
}