diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/mfd/rt5033_irq.c | |
Diffstat (limited to 'drivers/mfd/rt5033_irq.c')
| -rw-r--r-- | drivers/mfd/rt5033_irq.c | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/drivers/mfd/rt5033_irq.c b/drivers/mfd/rt5033_irq.c new file mode 100644 index 00000000..a02b1003 --- /dev/null +++ b/drivers/mfd/rt5033_irq.c @@ -0,0 +1,466 @@ +/* drivers/mfd/rt5033_irq.c + * RT5033 Multifunction Device Driver + * Charger / Buck / LDOs / FlashLED + * + * Copyright (C) 2013 Richtek Technology Corp. + * Author: Patrick Chang <patrick_chang@richtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ +#include <linux/mfd/rt5033.h> +#include <linux/mfd/rt5033_irq.h> +#include <linux/gpio.h> +#include <linux/device.h> + +#define ALIAS_NAME "rt5033_mfd_irq" + +#define RT5033_CHG_IRQ 0x60 +#define RT5033_LED_IRQ 0x66 +#define RT5033_PMIC_IRQ 0x68 +#define RT5033_OFF_EVENT 0x6B + +#define RT5033_CHG_IRQ_CTRL 0x63 +#define RT5033_CHG_IRQ1_CTRL RT5033_CHG_IRQ_CTRL +#define RT5033_CHG_IRQ2_CTRL (RT5033_CHG_IRQ_CTRL + 1) +#define RT5033_CHG_IRQ3_CTRL (RT5033_CHG_IRQ_CTRL + 2) +#define RT5033_LED_IRQ_CTRL 0x67 +#define RT5033_PMIC_IRQ_CTRL 0x69 + +#define IRQ_NAME(irq) [irq] = irq##_NAME +static const char *rt5033_irq_names[] = { + IRQ_NAME(RT5033_ADPBAD_IRQ), + IRQ_NAME(RT5033_PPBATLV_IRQ), + IRQ_NAME(RT5033_CHTMRFI_IRQ), + IRQ_NAME(RT5033_VINOVPI_IRQ), + IRQ_NAME(RT5033_TSDI_IRQ), + IRQ_NAME(RT5033_CHMIVRI_IRQ), + IRQ_NAME(RT5033_CHTREGI_IRQ), + IRQ_NAME(RT5033_IEOC_IRQ), + IRQ_NAME(RT5033_CHRCHGI_IRQ), + IRQ_NAME(RT5033_CHTERMI_IRQ), + IRQ_NAME(RT5033_CHBATOVI_IRQ), + IRQ_NAME(RT5033_CHRVPI_IRQ), + IRQ_NAME(RT5033_BSTLOWVI_IRQ), + IRQ_NAME(RT5033_BSTOLI_IRQ), + IRQ_NAME(RT5033_BSTVMIDOVP_IRQ), + IRQ_NAME(RT5033_VF_L_IRQ), + IRQ_NAME(RT5033_LEDCS2_SHORT_IRQ), + IRQ_NAME(RT5033_LEDCS1_SHORT_IRQ), + IRQ_NAME(RT5033_BUCK_OCP_IRQ), + IRQ_NAME(RT5033_BUCK_LV_IRQ), + IRQ_NAME(RT5033_SAFE_LDO_LV_IRQ), + IRQ_NAME(RT5033_LDO_LV_IRQ), + IRQ_NAME(RT5033_OT_IRQ), + IRQ_NAME(RT5033_VDDA_UV_IRQ), +}; + +const char *rt5033_get_irq_name_by_index(int index) +{ + return rt5033_irq_names[index]; +} +EXPORT_SYMBOL(rt5033_get_irq_name_by_index); + +enum RT5033_IRQ_OFFSET { + RT5033_CHG_IRQ_OFFSET = 0, + RT5033_CHG_IRQ1_OFFSET = 0, + RT5033_CHG_IRQ2_OFFSET, + RT5033_CHG_IRQ3_OFFSET, + RT5033_LED_IRQ_OFFSET, + RT5033_PMIC_IRQ_OFFSET, +}; + +struct rt5033_irq_data { + int mask; + int offset; +}; + +static const u8 rt5033_mask_reg[] = { + RT5033_CHG_IRQ1_CTRL, + RT5033_CHG_IRQ2_CTRL, + RT5033_CHG_IRQ3_CTRL, + RT5033_LED_IRQ_CTRL, + RT5033_PMIC_IRQ_CTRL, +}; + +#define DECLARE_IRQ_CTRL(idx, _offset, _mask) \ + [(idx)] = { .offset = (_offset), .mask = (_mask) } + +static const struct rt5033_irq_data rt5033_irqs[] = { + DECLARE_IRQ_CTRL(RT5033_ADPBAD_IRQ, 0, 1 << 0), + DECLARE_IRQ_CTRL(RT5033_PPBATLV_IRQ, 0, 1 << 4), + DECLARE_IRQ_CTRL(RT5033_CHTERMI_IRQ, 0, 1 << 5), + DECLARE_IRQ_CTRL(RT5033_VINOVPI_IRQ, 0, 1 << 6), + DECLARE_IRQ_CTRL(RT5033_TSDI_IRQ, 0, 1 << 7), + DECLARE_IRQ_CTRL(RT5033_CHMIVRI_IRQ, 1, 1 << 0), + DECLARE_IRQ_CTRL(RT5033_CHTREGI_IRQ, 1, 1 << 1), + DECLARE_IRQ_CTRL(RT5033_CHTMRFI_IRQ, 1, 1 << 2), + DECLARE_IRQ_CTRL(RT5033_CHRCHGI_IRQ, 1, 1 << 3), + DECLARE_IRQ_CTRL(RT5033_IEOC_IRQ, 1, 1 << 4), + DECLARE_IRQ_CTRL(RT5033_CHBATOVI_IRQ, 1, 1 << 5), + DECLARE_IRQ_CTRL(RT5033_CHRVPI_IRQ, 1, 1 << 7), + + DECLARE_IRQ_CTRL(RT5033_BSTLOWVI_IRQ, 2, 1 << 5), + DECLARE_IRQ_CTRL(RT5033_BSTOLI_IRQ, 2, 1 << 6), + DECLARE_IRQ_CTRL(RT5033_BSTVMIDOVP_IRQ, 2, 1 << 7), + + DECLARE_IRQ_CTRL(RT5033_VF_L_IRQ, 3, 1 << 3), + DECLARE_IRQ_CTRL(RT5033_LEDCS2_SHORT_IRQ, 3, 1 << 6), + DECLARE_IRQ_CTRL(RT5033_LEDCS1_SHORT_IRQ, 3, 1 << 7), + + DECLARE_IRQ_CTRL(RT5033_BUCK_OCP_IRQ, 4, 1 << 2), + DECLARE_IRQ_CTRL(RT5033_BUCK_LV_IRQ, 4, 1 << 3), + DECLARE_IRQ_CTRL(RT5033_SAFE_LDO_LV_IRQ, 4, 1 << 4), + DECLARE_IRQ_CTRL(RT5033_LDO_LV_IRQ, 4, 1 << 5), + DECLARE_IRQ_CTRL(RT5033_OT_IRQ, 4, 1 << 6), + DECLARE_IRQ_CTRL(RT5033_VDDA_UV_IRQ, 4, 1 << 7), +}; + + +static void rt5033_irq_lock(struct irq_data *data) +{ + rt5033_mfd_chip_t *chip = irq_get_chip_data(data->irq); + mutex_lock(&chip->irq_lock); +} + +static void rt5033_irq_sync_unlock(struct irq_data *data) +{ + rt5033_mfd_chip_t *chip = irq_get_chip_data(data->irq); + rt5033_block_write_device(chip->i2c_client, + RT5033_CHG_IRQ_CTRL, + RT5033_CHG_IRQ_REGS_NR, + chip->irq_masks_cache + + RT5033_CHG_IRQ_OFFSET); + + rt5033_block_write_device(chip->i2c_client, + RT5033_LED_IRQ_CTRL, + RT5033_LED_IRQ_REGS_NR, + chip->irq_masks_cache + + RT5033_LED_IRQ_OFFSET); + + rt5033_block_write_device(chip->i2c_client, + RT5033_PMIC_IRQ_CTRL, + RT5033_PMIC_IRQ_REGS_NR, + chip->irq_masks_cache + + RT5033_PMIC_IRQ_OFFSET); + + mutex_unlock(&chip->irq_lock); +} + + static const inline struct rt5033_irq_data * +irq_to_rt5033_irq(rt5033_mfd_chip_t *chip, int irq) +{ + return &rt5033_irqs[irq - chip->irq_base]; +} + +static void rt5033_irq_mask(struct irq_data *data) +{ + rt5033_mfd_chip_t *chip = irq_get_chip_data(data->irq); + const struct rt5033_irq_data *irq_data = irq_to_rt5033_irq(chip, + data->irq); + + chip->irq_masks_cache[irq_data->offset] |= irq_data->mask; +} + + +static void rt5033_irq_unmask(struct irq_data *data) +{ + rt5033_mfd_chip_t *chip = irq_get_chip_data(data->irq); + const struct rt5033_irq_data *irq_data = irq_to_rt5033_irq(chip, + data->irq); + + chip->irq_masks_cache[irq_data->offset] &= ~irq_data->mask; +} + +static struct irq_chip rt5033_irq_chip = { + .name = "rt5033", + .irq_bus_lock = rt5033_irq_lock, + .irq_bus_sync_unlock = rt5033_irq_sync_unlock, + .irq_mask = rt5033_irq_mask, + .irq_unmask = rt5033_irq_unmask, +}; + +rt5033_irq_status_t *rt5033_get_irq_status(rt5033_mfd_chip_t *mfd_chip, + rt5033_irq_status_sel_t sel) +{ + + int index; + + switch(sel) { + case RT5033_PREV_STATUS: + index = mfd_chip->irq_status_index ^ 0x01; + break; + case RT5033_NOW_STATUS: + default: + index = mfd_chip->irq_status_index; + } + + return &mfd_chip->irq_status[index]; +} +EXPORT_SYMBOL(rt5033_get_irq_status); + +static int rt5033_read_irq_status(rt5033_mfd_chip_t *chip) +{ + int ret; + struct i2c_client *iic = chip->i2c_client; + rt5033_irq_status_t *now_irq_status; + + now_irq_status = rt5033_get_irq_status(chip, RT5033_NOW_STATUS); + +#ifdef CONFIG_CHARGER_RT5033 + ret = rt5033_block_read_device(iic, RT5033_CHG_IRQ, + sizeof(now_irq_status->chg_irq_status), + now_irq_status->chg_irq_status); + if (ret < 0) { + dev_err(chip->dev, + "Failed on reading CHG irq status\n"); + return ret; + } + + printk("charger irq = 0x%x 0x%x 0x%x\n", (int)now_irq_status->chg_irq_status[0], + (int)now_irq_status->chg_irq_status[1], + now_irq_status->chg_irq_status[2]); +#endif +#ifdef CONFIG_FLED_RT5033 + ret = rt5033_block_read_device(iic, RT5033_LED_IRQ, + sizeof(now_irq_status->fled_irq_status), + now_irq_status->fled_irq_status); + if (ret < 0) { + dev_err(chip->dev, + "Failed on reading FlashLED irq status\n"); + return ret; + } + printk("fled irq = 0x%x\n", (int)now_irq_status->fled_irq_status[0]); +#endif /* CONFIG_FLED_RT5033 */ + +#ifdef CONFIG_REGULATOR_RT5033 + ret = rt5033_block_read_device(iic, RT5033_PMIC_IRQ, + sizeof(now_irq_status->pmic_irq_status), + now_irq_status->pmic_irq_status); + if (ret < 0) { + dev_err(chip->dev, + "Failed on reading PMIC irq status\n"); + return ret; + } + printk("regulator irq = 0x%x\n", (int)now_irq_status->pmic_irq_status[0]); +#endif /* CONFIG_REGULATOR_RT5033 */ + + return 0; +} + +static void rt5033_irq_work(rt5033_mfd_chip_t *chip) +{ + int ret; + int i; + rt5033_irq_status_t *status; + pr_info("%s : handle IRQ event\n", __func__); + ret = rt5033_read_irq_status(chip); + if (ret < 0) { + pr_err("%s :Error : can't read irq status (%d)\n", + __func__, ret); + return; + } + status = rt5033_get_irq_status(chip, RT5033_NOW_STATUS); + + for (i = 0; i < RT5033_IRQ_REGS_NR; i++) + status->regs[i] &= ~chip->irq_masks_cache[i]; + + for (i = 0; i < RT5033_IRQS_NR; i++) { + if (status->regs[rt5033_irqs[i].offset] & rt5033_irqs[i].mask) { + pr_info("%s : Trigger IRQ %s, irq : %d \n", + __func__, rt5033_get_irq_name_by_index(i), + chip->irq_base + i); + + handle_nested_irq(chip->irq_base + i); + } + } + + chip->irq_status_index ^= 0x01; // exchange irq index; +} + +static irqreturn_t rt5033_irq_handler(int irq, void *data) +{ + rt5033_mfd_chip_t *chip = data; + + + printk("RT5033 IRQ triggered\n"); + wake_lock_timeout(&chip->irq_wake_lock, msecs_to_jiffies(500)); + mutex_lock(&chip->suspend_flag_lock); + if (chip->suspend_flag) { + printk("I2C host controller might not be ready," + " pend this IRQ event...\n"); + chip->pending_irq = true; + } else + rt5033_irq_work(chip); + mutex_unlock(&chip->suspend_flag_lock);; + return IRQ_HANDLED; +} + +static int rt5033_irq_ctrl_regs[] = { + RT5033_CHG_IRQ1_CTRL, + RT5033_CHG_IRQ2_CTRL, + RT5033_CHG_IRQ3_CTRL, + RT5033_LED_IRQ_CTRL, + RT5033_PMIC_IRQ_CTRL, +}; + +static uint8_t rt5033_irqs_ctrl_mask_all_val[] = { + 0xf1, + 0xbf, + 0xf0, + 0xc8, + 0xfc, +}; + +static int rt5033_mask_all_irqs(struct i2c_client *iic) +{ + int rc; + int i; + + rt5033_mfd_chip_t *chip = i2c_get_clientdata(iic); + + for (i = 0; i < ARRAY_SIZE(rt5033_irq_ctrl_regs); i++) { + rc = rt5033_reg_write(iic, rt5033_irq_ctrl_regs[i], + rt5033_irqs_ctrl_mask_all_val[i]); + chip->irq_masks_cache[i] = rt5033_irqs_ctrl_mask_all_val[i]; + if (rc < 0) { + pr_info("Error : can't write reg[0x%x] = 0x%x\n", + rt5033_irq_ctrl_regs[i], + rt5033_irqs_ctrl_mask_all_val[i]); + return rc; + } + } + + return 0; +} + +static int rt5033_irq_init_read(rt5033_mfd_chip_t *chip) +{ + int ret; + ret = rt5033_read_irq_status(chip); + chip->irq_status_index ^= 0x01; + + return ret; +} + +int rt5033_init_irq(rt5033_mfd_chip_t *chip) +{ + int i, ret, curr_irq; + ret = rt5033_mask_all_irqs(chip->i2c_client); + + if (ret < 0) { + pr_err("%s : Can't mask all irqs(%d)\n", __func__, ret); + goto err_mask_all_irqs; + } + + rt5033_irq_init_read(chip); + mutex_init(&chip->irq_lock); + + /* Register with genirq */ + for (i = 0; i < RT5033_IRQS_NR; i++) { + curr_irq = i + chip->irq_base; + irq_set_chip_data(curr_irq, chip); + irq_set_chip_and_handler(curr_irq, &rt5033_irq_chip, + handle_simple_irq); + irq_set_nested_thread(curr_irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(curr_irq, IRQF_VALID); +#else + irq_set_noprobe(curr_irq); +#endif + } + + ret = gpio_request(chip->pdata->irq_gpio, "rt5033_mfd_irq"); + if (ret < 0) { + pr_err("%s : Request GPIO %d failed\n", + __func__, (int)chip->pdata->irq_gpio); + goto err_gpio_request; + } + + ret = gpio_direction_input(chip->pdata->irq_gpio); + if (ret < 0) { + pr_err("Set GPIO direction to input : failed\n"); + goto err_set_gpio_input; + } + + chip->irq = gpio_to_irq(chip->pdata->irq_gpio); + ret = request_threaded_irq(chip->irq, NULL, rt5033_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND, + "rt5033", chip); + if (ret < 0) { + pr_err("%s : Failed : request IRQ (%d)\n", __func__, ret); + goto err_request_irq; + + } + + ret = enable_irq_wake(chip->irq); + if (ret < 0) { + pr_info("%s : enable_irq_wake(%d) failed for (%d)\n", + __func__, chip->irq, ret); + } + + + return ret; +err_request_irq: +err_set_gpio_input: + gpio_free(chip->pdata->irq_gpio); +err_gpio_request: + for (curr_irq = chip->irq_base; + curr_irq < chip->irq_base + RT5033_IRQS_NR; + curr_irq++) { +#ifdef CONFIG_ARM + set_irq_flags(curr_irq, 0); +#endif + irq_set_chip_and_handler(curr_irq, NULL, NULL); + irq_set_chip_data(curr_irq, NULL); + } + + mutex_destroy(&chip->irq_lock); +err_mask_all_irqs: + return ret; + +} + +int rt5033_exit_irq(rt5033_mfd_chip_t *chip) +{ + int curr_irq; + + for (curr_irq = chip->irq_base; + curr_irq < chip->irq_base + RT5033_IRQS_NR; + curr_irq++) { +#ifdef CONFIG_ARM + set_irq_flags(curr_irq, 0); +#endif + irq_set_chip_and_handler(curr_irq, NULL, NULL); + irq_set_chip_data(curr_irq, NULL); + } + + if (chip->irq) + free_irq(chip->irq, chip); + mutex_destroy(&chip->irq_lock); + return 0; +} +#ifdef CONFIG_PM +int rt5033_irq_suspend(rt5033_mfd_chip_t *chip) +{ + mutex_lock(&chip->suspend_flag_lock); + chip->suspend_flag = true; + mutex_unlock(&chip->suspend_flag_lock); + return 0; +} +int rt5033_irq_resume(rt5033_mfd_chip_t *chip) +{ + mutex_lock(&chip->suspend_flag_lock); + if (chip->pending_irq) { + pr_info("%s : there is pending IRQ event\n", + __func__); + rt5033_irq_work(chip); + chip->pending_irq = false; + } + chip->suspend_flag = false; + mutex_unlock(&chip->suspend_flag_lock); + return 0; +} +#endif /* CONFIG_PM */ |
