brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · 3c03681 Raw
458 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * twl6030-irq.c - TWL6030 irq support4 *5 * Copyright (C) 2005-2009 Texas Instruments, Inc.6 *7 * Modifications to defer interrupt handling to a kernel thread:8 * Copyright (C) 2006 MontaVista Software, Inc.9 *10 * Based on tlv320aic23.c:11 * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>12 *13 * Code cleanup and modifications to IRQ handler.14 * by syed khasim <x0khasim@ti.com>15 *16 * TWL6030 specific code and IRQ handling changes by17 * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>18 * Balaji T K <balajitk@ti.com>19 */20 21#include <linux/export.h>22#include <linux/interrupt.h>23#include <linux/irq.h>24#include <linux/kthread.h>25#include <linux/mfd/twl.h>26#include <linux/platform_device.h>27#include <linux/suspend.h>28#include <linux/of.h>29#include <linux/irqdomain.h>30#include <linux/of_device.h>31 32#include "twl-core.h"33 34/*35 * TWL6030 (unlike its predecessors, which had two level interrupt handling)36 * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.37 * It exposes status bits saying who has raised an interrupt. There are38 * three mask registers that corresponds to these status registers, that39 * enables/disables these interrupts.40 *41 * We set up IRQs starting at a platform-specified base. An interrupt map table,42 * specifies mapping between interrupt number and the associated module.43 */44#define TWL6030_NR_IRQS    2045 46static int twl6030_interrupt_mapping[24] = {47	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/48	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/49	PWR_INTR_OFFSET,	/* Bit 2	BAT_VLOW		*/50	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/51	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/52	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/53	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/54	SMPSLDO_INTR_OFFSET,	/* Bit 7	VMMC_SHORT		*/55 56	SMPSLDO_INTR_OFFSET,	/* Bit 8	VUSIM_SHORT		*/57	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/58	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/59	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/60	RSV_INTR_OFFSET,	/* Bit 12	Reserved		*/61	MADC_INTR_OFFSET,	/* Bit 13	GPADC_RT_EOC		*/62	MADC_INTR_OFFSET,	/* Bit 14	GPADC_SW_EOC		*/63	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/64 65	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/66	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/67	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/68	USB_PRES_INTR_OFFSET,	/* Bit 19	VBUS			*/69	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/70	CHARGERFAULT_INTR_OFFSET,	/* Bit 21	EXT_CHRG	*/71	CHARGERFAULT_INTR_OFFSET,	/* Bit 22	INT_CHRG	*/72	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/73};74 75static int twl6032_interrupt_mapping[24] = {76	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/77	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/78	PWR_INTR_OFFSET,	/* Bit 2	SYS_VLOW		*/79	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/80	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/81	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/82	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/83	PWR_INTR_OFFSET,	/* Bit 7	SPDURATION		*/84 85	PWR_INTR_OFFSET,	/* Bit 8	WATCHDOG		*/86	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/87	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/88	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/89	MADC_INTR_OFFSET,	/* Bit 12	GPADC_RT_EOC		*/90	MADC_INTR_OFFSET,	/* Bit 13	GPADC_SW_EOC		*/91	GASGAUGE_INTR_OFFSET,	/* Bit 14	CC_EOC			*/92	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/93 94	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/95	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/96	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/97	USB_PRES_INTR_OFFSET,	/* Bit 19	VBUS			*/98	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/99	CHARGERFAULT_INTR_OFFSET,	/* Bit 21	EXT_CHRG	*/100	CHARGERFAULT_INTR_OFFSET,	/* Bit 22	INT_CHRG	*/101	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/102};103 104/*----------------------------------------------------------------------*/105 106struct twl6030_irq {107	unsigned int		irq_base;108	int			twl_irq;109	bool			irq_wake_enabled;110	atomic_t		wakeirqs;111	struct notifier_block	pm_nb;112	struct irq_chip		irq_chip;113	struct irq_domain	*irq_domain;114	const int		*irq_mapping_tbl;115};116 117static struct twl6030_irq *twl6030_irq;118 119static int twl6030_irq_pm_notifier(struct notifier_block *notifier,120				   unsigned long pm_event, void *unused)121{122	int chained_wakeups;123	struct twl6030_irq *pdata = container_of(notifier, struct twl6030_irq,124						  pm_nb);125 126	switch (pm_event) {127	case PM_SUSPEND_PREPARE:128		chained_wakeups = atomic_read(&pdata->wakeirqs);129 130		if (chained_wakeups && !pdata->irq_wake_enabled) {131			if (enable_irq_wake(pdata->twl_irq))132				pr_err("twl6030 IRQ wake enable failed\n");133			else134				pdata->irq_wake_enabled = true;135		} else if (!chained_wakeups && pdata->irq_wake_enabled) {136			disable_irq_wake(pdata->twl_irq);137			pdata->irq_wake_enabled = false;138		}139 140		disable_irq(pdata->twl_irq);141		break;142 143	case PM_POST_SUSPEND:144		enable_irq(pdata->twl_irq);145		break;146 147	default:148		break;149	}150 151	return NOTIFY_DONE;152}153 154/*155* Threaded irq handler for the twl6030 interrupt.156* We query the interrupt controller in the twl6030 to determine157* which module is generating the interrupt request and call158* handle_nested_irq for that module.159*/160static irqreturn_t twl6030_irq_thread(int irq, void *data)161{162	int i, ret;163	union {164		u8 bytes[4];165		__le32 int_sts;166	} sts;167	u32 int_sts; /* sts.int_sts converted to CPU endianness */168	struct twl6030_irq *pdata = data;169 170	/* read INT_STS_A, B and C in one shot using a burst read */171	ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3);172	if (ret) {173		pr_warn("twl6030_irq: I2C error %d reading PIH ISR\n", ret);174		return IRQ_HANDLED;175	}176 177	sts.bytes[3] = 0; /* Only 24 bits are valid*/178 179	/*180	 * Since VBUS status bit is not reliable for VBUS disconnect181	 * use CHARGER VBUS detection status bit instead.182	 */183	if (sts.bytes[2] & 0x10)184		sts.bytes[2] |= 0x08;185 186	int_sts = le32_to_cpu(sts.int_sts);187	for (i = 0; int_sts; int_sts >>= 1, i++)188		if (int_sts & 0x1) {189			int module_irq =190				irq_find_mapping(pdata->irq_domain,191						 pdata->irq_mapping_tbl[i]);192			if (module_irq)193				handle_nested_irq(module_irq);194			else195				pr_err("twl6030_irq: Unmapped PIH ISR %u detected\n",196				       i);197			pr_debug("twl6030_irq: PIH ISR %u, virq%u\n",198				 i, module_irq);199		}200 201	/*202	 * NOTE:203	 * Simulation confirms that documentation is wrong w.r.t the204	 * interrupt status clear operation. A single *byte* write to205	 * any one of STS_A to STS_C register results in all three206	 * STS registers being reset. Since it does not matter which207	 * value is written, all three registers are cleared on a208	 * single byte write, so we just use 0x0 to clear.209	 */210	ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);211	if (ret)212		pr_warn("twl6030_irq: I2C error in clearing PIH ISR\n");213 214	return IRQ_HANDLED;215}216 217/*----------------------------------------------------------------------*/218 219static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)220{221	struct twl6030_irq *pdata = irq_data_get_irq_chip_data(d);222 223	if (on)224		atomic_inc(&pdata->wakeirqs);225	else226		atomic_dec(&pdata->wakeirqs);227 228	return 0;229}230 231int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)232{233	int ret;234	u8 unmask_value;235 236	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,237			REG_INT_STS_A + offset);238	unmask_value &= (~(bit_mask));239	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,240			REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */241	return ret;242}243EXPORT_SYMBOL(twl6030_interrupt_unmask);244 245int twl6030_interrupt_mask(u8 bit_mask, u8 offset)246{247	int ret;248	u8 mask_value;249 250	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,251			REG_INT_STS_A + offset);252	mask_value |= (bit_mask);253	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,254			REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */255	return ret;256}257EXPORT_SYMBOL(twl6030_interrupt_mask);258 259int twl6030_mmc_card_detect_config(void)260{261	int ret;262	u8 reg_val = 0;263 264	/* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */265	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,266						REG_INT_MSK_LINE_B);267	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,268						REG_INT_MSK_STS_B);269	/*270	 * Initially Configuring MMC_CTRL for receiving interrupts &271	 * Card status on TWL6030 for MMC1272	 */273	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);274	if (ret < 0) {275		pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);276		return ret;277	}278	reg_val &= ~VMMC_AUTO_OFF;279	reg_val |= SW_FC;280	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);281	if (ret < 0) {282		pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);283		return ret;284	}285 286	/* Configuring PullUp-PullDown register */287	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,288						TWL6030_CFG_INPUT_PUPD3);289	if (ret < 0) {290		pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",291									ret);292		return ret;293	}294	reg_val &= ~(MMC_PU | MMC_PD);295	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,296						TWL6030_CFG_INPUT_PUPD3);297	if (ret < 0) {298		pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",299									ret);300		return ret;301	}302 303	return irq_find_mapping(twl6030_irq->irq_domain,304				 MMCDETECT_INTR_OFFSET);305}306EXPORT_SYMBOL(twl6030_mmc_card_detect_config);307 308int twl6030_mmc_card_detect(struct device *dev, int slot)309{310	int ret = -EIO;311	u8 read_reg = 0;312	struct platform_device *pdev = to_platform_device(dev);313 314	if (pdev->id) {315		/* TWL6030 provide's Card detect support for316		 * only MMC1 controller.317		 */318		pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__);319		return ret;320	}321	/*322	 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1323	 * 0 - Card not present ,1 - Card present324	 */325	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,326						TWL6030_MMCCTRL);327	if (ret >= 0)328		ret = read_reg & STS_MMC;329	return ret;330}331EXPORT_SYMBOL(twl6030_mmc_card_detect);332 333static int twl6030_irq_map(struct irq_domain *d, unsigned int virq,334			      irq_hw_number_t hwirq)335{336	struct twl6030_irq *pdata = d->host_data;337 338	irq_set_chip_data(virq, pdata);339	irq_set_chip_and_handler(virq,  &pdata->irq_chip, handle_simple_irq);340	irq_set_nested_thread(virq, true);341	irq_set_parent(virq, pdata->twl_irq);342	irq_set_noprobe(virq);343 344	return 0;345}346 347static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)348{349	irq_set_chip_and_handler(virq, NULL, NULL);350	irq_set_chip_data(virq, NULL);351}352 353static const struct irq_domain_ops twl6030_irq_domain_ops = {354	.map	= twl6030_irq_map,355	.unmap	= twl6030_irq_unmap,356	.xlate	= irq_domain_xlate_onetwocell,357};358 359static const struct of_device_id twl6030_of_match[] __maybe_unused = {360	{.compatible = "ti,twl6030", &twl6030_interrupt_mapping},361	{.compatible = "ti,twl6032", &twl6032_interrupt_mapping},362	{ },363};364 365int twl6030_init_irq(struct device *dev, int irq_num)366{367	struct			device_node *node = dev->of_node;368	int			nr_irqs;369	int			status;370	u8			mask[3];371	const struct of_device_id *of_id;372 373	of_id = of_match_device(twl6030_of_match, dev);374	if (!of_id || !of_id->data) {375		dev_err(dev, "Unknown TWL device model\n");376		return -EINVAL;377	}378 379	nr_irqs = TWL6030_NR_IRQS;380 381	twl6030_irq = devm_kzalloc(dev, sizeof(*twl6030_irq), GFP_KERNEL);382	if (!twl6030_irq)383		return -ENOMEM;384 385	mask[0] = 0xFF;386	mask[1] = 0xFF;387	mask[2] = 0xFF;388 389	/* mask all int lines */390	status = twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);391	/* mask all int sts */392	status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);393	/* clear INT_STS_A,B,C */394	status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);395 396	if (status < 0) {397		dev_err(dev, "I2C err writing TWL_MODULE_PIH: %d\n", status);398		return status;399	}400 401	/*402	 * install an irq handler for each of the modules;403	 * clone dummy irq_chip since PIH can't *do* anything404	 */405	twl6030_irq->irq_chip = dummy_irq_chip;406	twl6030_irq->irq_chip.name = "twl6030";407	twl6030_irq->irq_chip.irq_set_type = NULL;408	twl6030_irq->irq_chip.irq_set_wake = twl6030_irq_set_wake;409 410	twl6030_irq->pm_nb.notifier_call = twl6030_irq_pm_notifier;411	atomic_set(&twl6030_irq->wakeirqs, 0);412	twl6030_irq->irq_mapping_tbl = of_id->data;413 414	twl6030_irq->irq_domain =415		irq_domain_add_linear(node, nr_irqs,416				      &twl6030_irq_domain_ops, twl6030_irq);417	if (!twl6030_irq->irq_domain) {418		dev_err(dev, "Can't add irq_domain\n");419		return -ENOMEM;420	}421 422	dev_info(dev, "PIH (irq %d) nested IRQs\n", irq_num);423 424	/* install an irq handler to demultiplex the TWL6030 interrupt */425	status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread,426				      IRQF_ONESHOT, "TWL6030-PIH", twl6030_irq);427	if (status < 0) {428		dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);429		goto fail_irq;430	}431 432	twl6030_irq->twl_irq = irq_num;433	register_pm_notifier(&twl6030_irq->pm_nb);434	return 0;435 436fail_irq:437	irq_domain_remove(twl6030_irq->irq_domain);438	return status;439}440 441void twl6030_exit_irq(void)442{443	if (twl6030_irq && twl6030_irq->twl_irq) {444		unregister_pm_notifier(&twl6030_irq->pm_nb);445		free_irq(twl6030_irq->twl_irq, NULL);446		/*447		 * TODO: IRQ domain and allocated nested IRQ descriptors448		 * should be freed somehow here. Now It can't be done, because449		 * child devices will not be deleted during removing of450		 * TWL Core driver and they will still contain allocated451		 * virt IRQs in their Resources tables.452		 * The same prevents us from using devm_request_threaded_irq()453		 * in this module.454		 */455	}456}457 458