brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 245a069 Raw
346 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.4 * Copyright (C) 2018-2024 Linaro Ltd.5 */6 7/* DOC: IPA Interrupts8 *9 * The IPA has an interrupt line distinct from the interrupt used by the GSI10 * code.  Whereas GSI interrupts are generally related to channel events (like11 * transfer completions), IPA interrupts are related to other events related12 * to the IPA.  Some of the IPA interrupts come from a microcontroller13 * embedded in the IPA.  Each IPA interrupt type can be both masked and14 * acknowledged independent of the others.15 *16 * Two of the IPA interrupts are initiated by the microcontroller.  A third17 * can be generated to signal the need for a wakeup/resume when an IPA18 * endpoint has been suspended.  There are other IPA events, but at this19 * time only these three are supported.20 */21 22#include <linux/interrupt.h>23#include <linux/platform_device.h>24#include <linux/pm_runtime.h>25#include <linux/pm_wakeirq.h>26#include <linux/types.h>27 28#include "ipa.h"29#include "ipa_endpoint.h"30#include "ipa_interrupt.h"31#include "ipa_power.h"32#include "ipa_reg.h"33#include "ipa_uc.h"34 35/**36 * struct ipa_interrupt - IPA interrupt information37 * @ipa:		IPA pointer38 * @irq:		Linux IRQ number used for IPA interrupts39 * @enabled:		Mask indicating which interrupts are enabled40 * @suspend_enabled:	Bitmap of endpoints with the SUSPEND interrupt enabled41 */42struct ipa_interrupt {43	struct ipa *ipa;44	u32 irq;45	u32 enabled;46	unsigned long *suspend_enabled;47};48 49/* Clear the suspend interrupt for all endpoints that signaled it */50static void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt)51{52	struct ipa *ipa = interrupt->ipa;53	u32 unit_count;54	u32 unit;55 56	unit_count = DIV_ROUND_UP(ipa->endpoint_count, 32);57	for (unit = 0; unit < unit_count; unit++) {58		const struct reg *reg;59		u32 val;60 61		reg = ipa_reg(ipa, IRQ_SUSPEND_INFO);62		val = ioread32(ipa->reg_virt + reg_n_offset(reg, unit));63 64		/* SUSPEND interrupt status isn't cleared on IPA version 3.0 */65		if (!val || ipa->version == IPA_VERSION_3_0)66			continue;67 68		reg = ipa_reg(ipa, IRQ_SUSPEND_CLR);69		iowrite32(val, ipa->reg_virt + reg_n_offset(reg, unit));70	}71}72 73/* Process a particular interrupt type that has been received */74static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id)75{76	struct ipa *ipa = interrupt->ipa;77	const struct reg *reg;78	u32 mask = BIT(irq_id);79	u32 offset;80 81	reg = ipa_reg(ipa, IPA_IRQ_CLR);82	offset = reg_offset(reg);83 84	switch (irq_id) {85	case IPA_IRQ_UC_0:86	case IPA_IRQ_UC_1:87		/* For microcontroller interrupts, clear the interrupt right88		 * away, "to avoid clearing unhandled interrupts."89		 */90		iowrite32(mask, ipa->reg_virt + offset);91		ipa_uc_interrupt_handler(ipa, irq_id);92		break;93 94	case IPA_IRQ_TX_SUSPEND:95		/* Clearing the SUSPEND_TX interrupt also clears the96		 * register that tells us which suspended endpoint(s)97		 * caused the interrupt, so defer clearing until after98		 * the handler has been called.99		 */100		ipa_interrupt_suspend_clear_all(interrupt);101		fallthrough;102 103	default:	/* Silently ignore (and clear) any other condition */104		iowrite32(mask, ipa->reg_virt + offset);105		break;106	}107}108 109/* IPA IRQ handler is threaded */110static irqreturn_t ipa_isr_thread(int irq, void *dev_id)111{112	struct ipa_interrupt *interrupt = dev_id;113	struct ipa *ipa = interrupt->ipa;114	u32 enabled = interrupt->enabled;115	struct device *dev = ipa->dev;116	const struct reg *reg;117	u32 pending;118	u32 offset;119	u32 mask;120	int ret;121 122	ret = pm_runtime_get_sync(dev);123	if (WARN_ON(ret < 0))124		goto out_power_put;125 126	/* The status register indicates which conditions are present,127	 * including conditions whose interrupt is not enabled.  Handle128	 * only the enabled ones.129	 */130	reg = ipa_reg(ipa, IPA_IRQ_STTS);131	offset = reg_offset(reg);132	pending = ioread32(ipa->reg_virt + offset);133	while ((mask = pending & enabled)) {134		do {135			u32 irq_id = __ffs(mask);136 137			mask ^= BIT(irq_id);138 139			ipa_interrupt_process(interrupt, irq_id);140		} while (mask);141		pending = ioread32(ipa->reg_virt + offset);142	}143 144	/* If any disabled interrupts are pending, clear them */145	if (pending) {146		dev_dbg(dev, "clearing disabled IPA interrupts 0x%08x\n",147			pending);148		reg = ipa_reg(ipa, IPA_IRQ_CLR);149		iowrite32(pending, ipa->reg_virt + reg_offset(reg));150	}151out_power_put:152	pm_runtime_mark_last_busy(dev);153	(void)pm_runtime_put_autosuspend(dev);154 155	return IRQ_HANDLED;156}157 158static void ipa_interrupt_enabled_update(struct ipa *ipa)159{160	const struct reg *reg = ipa_reg(ipa, IPA_IRQ_EN);161 162	iowrite32(ipa->interrupt->enabled, ipa->reg_virt + reg_offset(reg));163}164 165/* Enable an IPA interrupt type */166void ipa_interrupt_enable(struct ipa *ipa, enum ipa_irq_id ipa_irq)167{168	/* Update the IPA interrupt mask to enable it */169	ipa->interrupt->enabled |= BIT(ipa_irq);170	ipa_interrupt_enabled_update(ipa);171}172 173/* Disable an IPA interrupt type */174void ipa_interrupt_disable(struct ipa *ipa, enum ipa_irq_id ipa_irq)175{176	/* Update the IPA interrupt mask to disable it */177	ipa->interrupt->enabled &= ~BIT(ipa_irq);178	ipa_interrupt_enabled_update(ipa);179}180 181void ipa_interrupt_irq_disable(struct ipa *ipa)182{183	disable_irq(ipa->interrupt->irq);184}185 186void ipa_interrupt_irq_enable(struct ipa *ipa)187{188	enable_irq(ipa->interrupt->irq);189}190 191/* Common function used to enable/disable TX_SUSPEND for an endpoint */192static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,193					  u32 endpoint_id, bool enable)194{195	struct ipa *ipa = interrupt->ipa;196	u32 mask = BIT(endpoint_id % 32);197	u32 unit = endpoint_id / 32;198	const struct reg *reg;199	unsigned long weight;200	u32 offset;201	u32 val;202 203	WARN_ON(!test_bit(endpoint_id, ipa->available));204 205	/* IPA version 3.0 does not support TX_SUSPEND interrupt control */206	if (ipa->version == IPA_VERSION_3_0)207		return;208 209	weight = bitmap_weight(interrupt->suspend_enabled, ipa->endpoint_count);210	if (weight == 1 && !enable)211		ipa_interrupt_disable(ipa, IPA_IRQ_TX_SUSPEND);212 213	reg = ipa_reg(ipa, IRQ_SUSPEND_EN);214	offset = reg_n_offset(reg, unit);215	val = ioread32(ipa->reg_virt + offset);216 217	if (enable)218		val |= mask;219	else220		val &= ~mask;221	__change_bit(endpoint_id, interrupt->suspend_enabled);222 223	iowrite32(val, ipa->reg_virt + offset);224 225	if (!weight && enable)226		ipa_interrupt_enable(ipa, IPA_IRQ_TX_SUSPEND);227}228 229/* Enable TX_SUSPEND for an endpoint */230void231ipa_interrupt_suspend_enable(struct ipa_interrupt *interrupt, u32 endpoint_id)232{233	ipa_interrupt_suspend_control(interrupt, endpoint_id, true);234}235 236/* Disable TX_SUSPEND for an endpoint */237void238ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id)239{240	ipa_interrupt_suspend_control(interrupt, endpoint_id, false);241}242 243/* Simulate arrival of an IPA TX_SUSPEND interrupt */244void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt)245{246	ipa_interrupt_process(interrupt, IPA_IRQ_TX_SUSPEND);247}248 249/* Configure the IPA interrupt framework */250int ipa_interrupt_config(struct ipa *ipa)251{252	struct ipa_interrupt *interrupt = ipa->interrupt;253	unsigned int irq = interrupt->irq;254	struct device *dev = ipa->dev;255	const struct reg *reg;256	int ret;257 258	interrupt->ipa = ipa;259 260	/* Initially all IPA interrupt types are disabled */261	interrupt->enabled = 0;262	interrupt->suspend_enabled = bitmap_zalloc(ipa->endpoint_count,263						   GFP_KERNEL);264	if (!interrupt->suspend_enabled) {265		ret = -ENOMEM;266		goto err_kfree;267	}268 269	/* Disable IPA interrupt types */270	reg = ipa_reg(ipa, IPA_IRQ_EN);271	iowrite32(0, ipa->reg_virt + reg_offset(reg));272 273	ret = request_threaded_irq(irq, NULL, ipa_isr_thread, IRQF_ONESHOT,274				   "ipa", interrupt);275	if (ret) {276		dev_err(dev, "error %d requesting \"ipa\" IRQ\n", ret);277		goto err_free_bitmap;278	}279 280	ret = device_init_wakeup(dev, true);281	if (ret) {282		dev_err(dev, "error %d enabling wakeup\n", ret);283		goto err_free_irq;284	}285 286	ret = dev_pm_set_wake_irq(dev, irq);287	if (ret) {288		dev_err(dev, "error %d registering \"ipa\" IRQ as wakeirq\n",289			ret);290		goto err_disable_wakeup;291	}292 293	ipa->interrupt = interrupt;294 295	return 0;296 297err_disable_wakeup:298	(void)device_init_wakeup(dev, false);299err_free_irq:300	free_irq(interrupt->irq, interrupt);301err_free_bitmap:302	bitmap_free(interrupt->suspend_enabled);303err_kfree:304	kfree(interrupt);305 306	return ret;307}308 309/* Inverse of ipa_interrupt_config() */310void ipa_interrupt_deconfig(struct ipa *ipa)311{312	struct ipa_interrupt *interrupt = ipa->interrupt;313	struct device *dev = ipa->dev;314 315	ipa->interrupt = NULL;316 317	dev_pm_clear_wake_irq(dev);318	(void)device_init_wakeup(dev, false);319	free_irq(interrupt->irq, interrupt);320	bitmap_free(interrupt->suspend_enabled);321}322 323/* Initialize the IPA interrupt structure */324struct ipa_interrupt *ipa_interrupt_init(struct platform_device *pdev)325{326	struct ipa_interrupt *interrupt;327	int irq;328 329	irq = platform_get_irq_byname(pdev, "ipa");330	if (irq <= 0)331		return ERR_PTR(irq ? : -EINVAL);332 333	interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL);334	if (!interrupt)335		return ERR_PTR(-ENOMEM);336	interrupt->irq = irq;337 338	return interrupt;339}340 341/* Inverse of ipa_interrupt_init() */342void ipa_interrupt_exit(struct ipa_interrupt *interrupt)343{344	kfree(interrupt);345}346