Commit d9351ea1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull IRQ chip updates from Ingo Molnar:
 "A late irqchips update:

   - New TI INTR/INTA set of drivers

   - Rewrite of the stm32mp1-exti driver as a platform driver

   - Update the IOMMU MSI mapping API to be RT friendly

   - A number of cleanups and other low impact fixes"

* 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (34 commits)
  iommu/dma-iommu: Remove iommu_dma_map_msi_msg()
  irqchip/gic-v3-mbi: Don't map the MSI page in mbi_compose_m{b, s}i_msg()
  irqchip/ls-scfg-msi: Don't map the MSI page in ls_scfg_msi_compose_msg()
  irqchip/gic-v3-its: Don't map the MSI page in its_irq_compose_msi_msg()
  irqchip/gicv2m: Don't map the MSI page in gicv2m_compose_msi_msg()
  iommu/dma-iommu: Split iommu_dma_map_msi_msg() in two parts
  genirq/msi: Add a new field in msi_desc to store an IOMMU cookie
  arm64: arch_k3: Enable interrupt controller drivers
  irqchip/ti-sci-inta: Add msi domain support
  soc: ti: Add MSI domain bus support for Interrupt Aggregator
  irqchip/ti-sci-inta: Add support for Interrupt Aggregator driver
  dt-bindings: irqchip: Introduce TISCI Interrupt Aggregator bindings
  irqchip/ti-sci-intr: Add support for Interrupt Router driver
  dt-bindings: irqchip: Introduce TISCI Interrupt router bindings
  gpio: thunderx: Use the default parent apis for {request,release}_resources
  genirq: Introduce irq_chip_{request,release}_resource_parent() apis
  firmware: ti_sci: Add helper apis to manage resources
  firmware: ti_sci: Add RM mapping table for am654
  firmware: ti_sci: Add support for IRQ management
  firmware: ti_sci: Add support for RM core ops
  ...
parents 39feaa3f fb4e0592
......@@ -24,7 +24,8 @@ relationship between the TI-SCI parent node to the child node.
Required properties:
-------------------
- compatible: should be "ti,k2g-sci"
- compatible: should be "ti,k2g-sci" for TI 66AK2G SoC
should be "ti,am654-sci" for for TI AM654 SoC
- mbox-names:
"rx" - Mailbox corresponding to receive path
"tx" - Mailbox corresponding to transmit path
......
Texas Instruments K3 Interrupt Aggregator
=========================================
The Interrupt Aggregator (INTA) provides a centralized machine
which handles the termination of system events to that they can
be coherently processed by the host(s) in the system. A maximum
of 64 events can be mapped to a single interrupt.
Interrupt Aggregator
+-----------------------------------------+
| Intmap VINT |
| +--------------+ +------------+ |
m ------>| | vint | bit | | 0 |.....|63| vint0 |
. | +--------------+ +------------+ | +------+
. | . . | | HOST |
Globalevents ------>| . . |------>| IRQ |
. | . . | | CTRL |
. | . . | +------+
n ------>| +--------------+ +------------+ |
| | vint | bit | | 0 |.....|63| vintx |
| +--------------+ +------------+ |
| |
+-----------------------------------------+
Configuration of these Intmap registers that maps global events to vint is done
by a system controller (like the Device Memory and Security Controller on K3
AM654 SoC). Driver should request the system controller to get the range
of global events and vints assigned to the requesting host. Management
of these requested resources should be handled by driver and requests
system controller to map specific global event to vint, bit pair.
Communication between the host processor running an OS and the system
controller happens through a protocol called TI System Control Interface
(TISCI protocol). For more details refer:
Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
TISCI Interrupt Aggregator Node:
-------------------------------
- compatible: Must be "ti,sci-inta".
- reg: Should contain registers location and length.
- interrupt-controller: Identifies the node as an interrupt controller
- msi-controller: Identifies the node as an MSI controller.
- interrupt-parent: phandle of irq parent.
- ti,sci: Phandle to TI-SCI compatible System controller node.
- ti,sci-dev-id: TISCI device ID of the Interrupt Aggregator.
- ti,sci-rm-range-vint: Array of TISCI subtype ids representing vints(inta
outputs) range within this INTA, assigned to the
requesting host context.
- ti,sci-rm-range-global-event: Array of TISCI subtype ids representing the
global events range reaching this IA and are assigned
to the requesting host context.
Example:
--------
main_udmass_inta: interrupt-controller@33d00000 {
compatible = "ti,sci-inta";
reg = <0x0 0x33d00000 0x0 0x100000>;
interrupt-controller;
msi-controller;
interrupt-parent = <&main_navss_intr>;
ti,sci = <&dmsc>;
ti,sci-dev-id = <179>;
ti,sci-rm-range-vint = <0x0>;
ti,sci-rm-range-global-event = <0x1>;
};
Texas Instruments K3 Interrupt Router
=====================================
The Interrupt Router (INTR) module provides a mechanism to mux M
interrupt inputs to N interrupt outputs, where all M inputs are selectable
to be driven per N output. An Interrupt Router can either handle edge triggered
or level triggered interrupts and that is fixed in hardware.
Interrupt Router
+----------------------+
| Inputs Outputs |
+-------+ | +------+ +-----+ |
| GPIO |----------->| | irq0 | | 0 | | Host IRQ
+-------+ | +------+ +-----+ | controller
| . . | +-------+
+-------+ | . . |----->| IRQ |
| INTA |----------->| . . | +-------+
+-------+ | . +-----+ |
| +------+ | N | |
| | irqM | +-----+ |
| +------+ |
| |
+----------------------+
There is one register per output (MUXCNTL_N) that controls the selection.
Configuration of these MUXCNTL_N registers is done by a system controller
(like the Device Memory and Security Controller on K3 AM654 SoC). System
controller will keep track of the used and unused registers within the Router.
Driver should request the system controller to get the range of GIC IRQs
assigned to the requesting hosts. It is the drivers responsibility to keep
track of Host IRQs.
Communication between the host processor running an OS and the system
controller happens through a protocol called TI System Control Interface
(TISCI protocol). For more details refer:
Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
TISCI Interrupt Router Node:
----------------------------
Required Properties:
- compatible: Must be "ti,sci-intr".
- ti,intr-trigger-type: Should be one of the following:
1: If intr supports edge triggered interrupts.
4: If intr supports level triggered interrupts.
- interrupt-controller: Identifies the node as an interrupt controller
- #interrupt-cells: Specifies the number of cells needed to encode an
interrupt source. The value should be 2.
First cell should contain the TISCI device ID of source
Second cell should contain the interrupt source offset
within the device.
- ti,sci: Phandle to TI-SCI compatible System controller node.
- ti,sci-dst-id: TISCI device ID of the destination IRQ controller.
- ti,sci-rm-range-girq: Array of TISCI subtype ids representing the host irqs
assigned to this interrupt router. Each subtype id
corresponds to a range of host irqs.
For more details on TISCI IRQ resource management refer:
http://downloads.ti.com/tisci/esd/latest/2_tisci_msgs/rm/rm_irq.html
Example:
--------
The following example demonstrates both interrupt router node and the consumer
node(main gpio) on the AM654 SoC:
main_intr: interrupt-controller0 {
compatible = "ti,sci-intr";
ti,intr-trigger-type = <1>;
interrupt-controller;
interrupt-parent = <&gic500>;
#interrupt-cells = <2>;
ti,sci = <&dmsc>;
ti,sci-dst-id = <56>;
ti,sci-rm-range-girq = <0x1>;
};
main_gpio0: gpio@600000 {
...
interrupt-parent = <&main_intr>;
interrupts = <57 256>, <57 257>, <57 258>,
<57 259>, <57 260>, <57 261>;
...
};
......@@ -15547,6 +15547,12 @@ F: Documentation/devicetree/bindings/reset/ti,sci-reset.txt
F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt
F: drivers/clk/keystone/sci-clk.c
F: drivers/reset/reset-ti-sci.c
F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
F: drivers/irqchip/irq-ti-sci-intr.c
F: drivers/irqchip/irq-ti-sci-inta.c
F: include/linux/soc/ti/ti_sci_inta_msi.h
F: drivers/soc/ti/ti_sci_inta_msi.c
Texas Instruments ASoC drivers
M: Peter Ujfalusi <peter.ujfalusi@ti.com>
......
......@@ -87,6 +87,11 @@ config ARCH_EXYNOS
config ARCH_K3
bool "Texas Instruments Inc. K3 multicore SoC architecture"
select PM_GENERIC_DOMAINS if PM
select MAILBOX
select TI_MESSAGE_MANAGER
select TI_SCI_PROTOCOL
select TI_SCI_INTR_IRQCHIP
select TI_SCI_INTA_IRQCHIP
help
This enables support for Texas Instruments' K3 multicore SoC
architecture.
......
This diff is collapsed.
......@@ -35,6 +35,13 @@
#define TI_SCI_MSG_QUERY_CLOCK_FREQ 0x010d
#define TI_SCI_MSG_GET_CLOCK_FREQ 0x010e
/* Resource Management Requests */
#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500
/* IRQ requests */
#define TI_SCI_MSG_SET_IRQ 0x1000
#define TI_SCI_MSG_FREE_IRQ 0x1001
/**
* struct ti_sci_msg_hdr - Generic Message Header for All messages and responses
* @type: Type of messages: One of TI_SCI_MSG* values
......@@ -461,4 +468,99 @@ struct ti_sci_msg_resp_get_clock_freq {
u64 freq_hz;
} __packed;
#define TI_SCI_IRQ_SECONDARY_HOST_INVALID 0xff
/**
* struct ti_sci_msg_req_get_resource_range - Request to get a host's assigned
* range of resources.
* @hdr: Generic Header
* @type: Unique resource assignment type
* @subtype: Resource assignment subtype within the resource type.
* @secondary_host: Host processing entity to which the resources are
* allocated. This is required only when the destination
* host id id different from ti sci interface host id,
* else TI_SCI_IRQ_SECONDARY_HOST_INVALID can be passed.
*
* Request type is TI_SCI_MSG_GET_RESOURCE_RANGE. Responded with requested
* resource range which is of type TI_SCI_MSG_GET_RESOURCE_RANGE.
*/
struct ti_sci_msg_req_get_resource_range {
struct ti_sci_msg_hdr hdr;
#define MSG_RM_RESOURCE_TYPE_MASK GENMASK(9, 0)
#define MSG_RM_RESOURCE_SUBTYPE_MASK GENMASK(5, 0)
u16 type;
u8 subtype;
u8 secondary_host;
} __packed;
/**
* struct ti_sci_msg_resp_get_resource_range - Response to resource get range.
* @hdr: Generic Header
* @range_start: Start index of the resource range.
* @range_num: Number of resources in the range.
*
* Response to request TI_SCI_MSG_GET_RESOURCE_RANGE.
*/
struct ti_sci_msg_resp_get_resource_range {
struct ti_sci_msg_hdr hdr;
u16 range_start;
u16 range_num;
} __packed;
/**
* struct ti_sci_msg_req_manage_irq - Request to configure/release the route
* between the dev and the host.
* @hdr: Generic Header
* @valid_params: Bit fields defining the validity of interrupt source
* parameters. If a bit is not set, then corresponding
* field is not valid and will not be used for route set.
* Bit field definitions:
* 0 - Valid bit for @dst_id
* 1 - Valid bit for @dst_host_irq
* 2 - Valid bit for @ia_id
* 3 - Valid bit for @vint
* 4 - Valid bit for @global_event
* 5 - Valid bit for @vint_status_bit_index
* 31 - Valid bit for @secondary_host
* @src_id: IRQ source peripheral ID.
* @src_index: IRQ source index within the peripheral
* @dst_id: IRQ Destination ID. Based on the architecture it can be
* IRQ controller or host processor ID.
* @dst_host_irq: IRQ number of the destination host IRQ controller
* @ia_id: Device ID of the interrupt aggregator in which the
* vint resides.
* @vint: Virtual interrupt number if the interrupt route
* is through an interrupt aggregator.
* @global_event: Global event that is to be mapped to interrupt
* aggregator virtual interrupt status bit.
* @vint_status_bit: Virtual interrupt status bit if the interrupt route
* utilizes an interrupt aggregator status bit.
* @secondary_host: Host ID of the IRQ destination computing entity. This is
* required only when destination host id is different
* from ti sci interface host id.
*
* Request type is TI_SCI_MSG_SET/RELEASE_IRQ.
* Response is generic ACK / NACK message.
*/
struct ti_sci_msg_req_manage_irq {
struct ti_sci_msg_hdr hdr;
#define MSG_FLAG_DST_ID_VALID TI_SCI_MSG_FLAG(0)
#define MSG_FLAG_DST_HOST_IRQ_VALID TI_SCI_MSG_FLAG(1)
#define MSG_FLAG_IA_ID_VALID TI_SCI_MSG_FLAG(2)
#define MSG_FLAG_VINT_VALID TI_SCI_MSG_FLAG(3)
#define MSG_FLAG_GLB_EVNT_VALID TI_SCI_MSG_FLAG(4)
#define MSG_FLAG_VINT_STS_BIT_VALID TI_SCI_MSG_FLAG(5)
#define MSG_FLAG_SHOST_VALID TI_SCI_MSG_FLAG(31)
u32 valid_params;
u16 src_id;
u16 src_index;
u16 dst_id;
u16 dst_host_irq;
u16 ia_id;
u16 vint;
u16 global_event;
u8 vint_status_bit;
u8 secondary_host;
} __packed;
#endif /* __TI_SCI_H */
......@@ -363,22 +363,16 @@ static int thunderx_gpio_irq_request_resources(struct irq_data *data)
{
struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
struct thunderx_gpio *txgpio = txline->txgpio;
struct irq_data *parent_data = data->parent_data;
int r;
r = gpiochip_lock_as_irq(&txgpio->chip, txline->line);
if (r)
return r;
if (parent_data && parent_data->chip->irq_request_resources) {
r = parent_data->chip->irq_request_resources(parent_data);
if (r)
goto error;
}
r = irq_chip_request_resources_parent(data);
if (r)
gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
return 0;
error:
gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
return r;
}
......@@ -386,10 +380,8 @@ static void thunderx_gpio_irq_release_resources(struct irq_data *data)
{
struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
struct thunderx_gpio *txgpio = txline->txgpio;
struct irq_data *parent_data = data->parent_data;
if (parent_data && parent_data->chip->irq_release_resources)
parent_data->chip->irq_release_resources(parent_data);
irq_chip_release_resources_parent(data);
gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
}
......
......@@ -94,6 +94,7 @@ config IOMMU_DMA
bool
select IOMMU_API
select IOMMU_IOVA
select IRQ_MSI_IOMMU
select NEED_SG_DMA_LENGTH
config FSL_PAMU
......
......@@ -907,17 +907,18 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
return NULL;
}
void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr)
{
struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
struct device *dev = msi_desc_to_dev(desc);
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct iommu_dma_cookie *cookie;
struct iommu_dma_msi_page *msi_page;
phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
unsigned long flags;
if (!domain || !domain->iova_cookie)
return;
if (!domain || !domain->iova_cookie) {
desc->iommu_cookie = NULL;
return 0;
}
cookie = domain->iova_cookie;
......@@ -930,19 +931,26 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
spin_unlock_irqrestore(&cookie->msi_lock, flags);
if (WARN_ON(!msi_page)) {
/*
* We're called from a void callback, so the best we can do is
* 'fail' by filling the message with obviously bogus values.
* Since we got this far due to an IOMMU being present, it's
* not like the existing address would have worked anyway...
*/
msg->address_hi = ~0U;
msg->address_lo = ~0U;
msg->data = ~0U;
} else {
msg->address_hi = upper_32_bits(msi_page->iova);
msg->address_lo &= cookie_msi_granule(cookie) - 1;
msg->address_lo += lower_32_bits(msi_page->iova);
}
msi_desc_set_iommu_cookie(desc, msi_page);
if (!msi_page)
return -ENOMEM;
return 0;
}
void iommu_dma_compose_msi_msg(struct msi_desc *desc,
struct msi_msg *msg)
{
struct device *dev = msi_desc_to_dev(desc);
const struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
const struct iommu_dma_msi_page *msi_page;
msi_page = msi_desc_get_iommu_cookie(desc);
if (!domain || !domain->iova_cookie || WARN_ON(!msi_page))
return;
msg->address_hi = upper_32_bits(msi_page->iova);
msg->address_lo &= cookie_msi_granule(domain->iova_cookie) - 1;
msg->address_lo += lower_32_bits(msi_page->iova);
}
......@@ -6,7 +6,6 @@ config IRQCHIP
config ARM_GIC
bool
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
......@@ -33,7 +32,6 @@ config GIC_NON_BANKED
config ARM_GIC_V3
bool
select IRQ_DOMAIN
select GENERIC_IRQ_MULTI_HANDLER
select IRQ_DOMAIN_HIERARCHY
select PARTITION_PERCPU
......@@ -59,7 +57,6 @@ config ARM_GIC_V3_ITS_FSL_MC
config ARM_NVIC
bool
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_CHIP
......@@ -358,7 +355,6 @@ config STM32_EXTI
config QCOM_IRQ_COMBINER
bool "QCOM IRQ combiner support"
depends on ARCH_QCOM && ACPI
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Say yes here to add support for the IRQ combiner devices embedded
......@@ -375,7 +371,6 @@ config IRQ_UNIPHIER_AIDET
config MESON_IRQ_GPIO
bool "Meson GPIO Interrupt Multiplexer"
depends on ARCH_MESON
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Support Meson SoC Family GPIO Interrupt Multiplexer
......@@ -391,7 +386,6 @@ config GOLDFISH_PIC
config QCOM_PDC
bool "QCOM PDC"
depends on ARCH_QCOM
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Power Domain Controller driver to manage and configure wakeup
......@@ -431,6 +425,27 @@ config LS1X_IRQ
help
Support for the Loongson-1 platform Interrupt Controller.
config TI_SCI_INTR_IRQCHIP
bool
depends on TI_SCI_PROTOCOL
select IRQ_DOMAIN_HIERARCHY
help
This enables the irqchip driver support for K3 Interrupt router
over TI System Control Interface available on some new TI's SoCs.
If you wish to use interrupt router irq resources managed by the
TI System Controller, say Y here. Otherwise, say N.
config TI_SCI_INTA_IRQCHIP
bool
depends on TI_SCI_PROTOCOL
select IRQ_DOMAIN_HIERARCHY
select TI_SCI_INTA_MSI_DOMAIN
help
This enables the irqchip driver support for K3 Interrupt aggregator
over TI System Control Interface available on some new TI's SoCs.
If you wish to use interrupt aggregator irq resources managed by the
TI System Controller, say Y here. Otherwise, say N.
endmenu
config SIFIVE_PLIC
......
......@@ -98,3 +98,5 @@ obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
......@@ -343,6 +343,9 @@ int __init bcm7038_l1_of_init(struct device_node *dn,
goto out_unmap;
}
pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n",
dn, IRQS_PER_WORD * intc->n_words);
return 0;
out_unmap:
......
......@@ -318,6 +318,9 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn,
}
}
pr_info("registered %s intc (%pOF, parent IRQ(s): %d)\n",
intc_name, dn, data->num_parent_irqs);
return 0;
out_free_domain:
......
......@@ -264,6 +264,8 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np,
ct->chip.irq_set_wake = irq_gc_set_wake;
}
pr_info("registered L2 intc (%pOF, parent irq: %d)\n", np, parent_irq);
return 0;
out_free_domain:
......
......@@ -19,7 +19,6 @@
#include <linux/of_irq.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
......@@ -28,17 +27,27 @@ struct gic_clk_data {
const char *const *clocks;
};
struct gic_chip_pm {
struct gic_chip_data *chip_data;
const struct gic_clk_data *clk_data;
struct clk_bulk_data *clks;
};
static int gic_runtime_resume(struct device *dev)
{
struct gic_chip_data *gic = dev_get_drvdata(dev);
struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
struct gic_chip_data *gic = chip_pm->chip_data;
const struct gic_clk_data *data = chip_pm->clk_data;
int ret;
ret = pm_clk_resume(dev);
if (ret)
ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
return ret;
}
/*
* On the very first resume, the pointer to the driver data
* On the very first resume, the pointer to chip_pm->chip_data
* will be NULL and this is intentional, because we do not
* want to restore the GIC on the very first resume. So if
* the pointer is not valid just return.
......@@ -54,35 +63,14 @@ static int gic_runtime_resume(struct device *dev)
static int gic_runtime_suspend(struct device *dev)
{
struct gic_chip_data *gic = dev_get_drvdata(dev);
struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
struct gic_chip_data *gic = chip_pm->chip_data;
const struct gic_clk_data *data = chip_pm->clk_data;
gic_dist_save(gic);
gic_cpu_save(gic);
return pm_clk_suspend(dev);
}
static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
{
unsigned int i;
int ret;
if (!dev || !data)
return -EINVAL;
ret = pm_clk_create(dev);
if (ret)
return ret;
for (i = 0; i < data->num_clocks; i++) {
ret = of_pm_clk_add_clk(dev, data->clocks[i]);
if (ret) {
dev_err(dev, "failed to add clock %s\n",
data->clocks[i]);
pm_clk_destroy(dev);
return ret;
}
}
clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks);
return 0;
}
......@@ -91,8 +79,8 @@ static int gic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct gic_clk_data *data;
struct gic_chip_data *gic;
int ret, irq;
struct gic_chip_pm *chip_pm;
int ret, irq, i;
data = of_device_get_match_data(&pdev->dev);
if (!data) {
......@@ -100,28 +88,41 @@ static int gic_probe(struct platform_device *pdev)
return -ENODEV;
}
chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL);
if (!chip_pm)
return -ENOMEM;
irq = irq_of_parse_and_map(dev->of_node, 0);
if (!irq) {
dev_err(dev, "no parent interrupt found!\n");
return -EINVAL;
}
ret = gic_get_clocks(dev, data);
chip_pm->clks = devm_kcalloc(dev, data->num_clocks,
sizeof(*chip_pm->clks), GFP_KERNEL);
if (!chip_pm->clks)
return -ENOMEM;
for (i = 0; i < data->num_clocks; i++)
chip_pm->clks[i].id = data->clocks[i];
ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks);
if (ret)
goto irq_dispose;
chip_pm->clk_data = data;
dev_set_drvdata(dev, chip_pm);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0)
goto rpm_disable;
ret = gic_of_init_child(dev, &gic, irq);
ret = gic_of_init_child(dev, &chip_pm->chip_data, irq);
if (ret)
goto rpm_put;
platform_set_drvdata(pdev, gic);
pm_runtime_put(dev);
dev_info(dev, "GIC IRQ controller registered\n");
......@@ -132,7 +133,6 @@ static int gic_probe(struct platform_device *pdev)
pm_runtime_put_sync(dev);
rpm_disable:
pm_runtime_disable(dev);
pm_clk_destroy(dev);
irq_dispose:
irq_dispose_mapping(irq);
......@@ -142,6 +142,8 @@ static int gic_probe(struct platform_device *pdev)
static const struct dev_pm_ops gic_pm_ops = {
SET_RUNTIME_PM_OPS(gic_runtime_suspend,
gic_runtime_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static const char * const gic400_clocks[] = {
......
......@@ -110,7 +110,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
msg->data -= v2m->spi_offset;
iommu_dma_map_msi_msg(data->irq, msg);
iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
}
static struct irq_chip gicv2m_irq_chip = {
......@@ -167,6 +167,7 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq,
static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
msi_alloc_info_t *info = args;
struct v2m_data *v2m = NULL, *tmp;
int hwirq, offset, i, err = 0;
......@@ -186,6 +187,11 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
hwirq = v2m->spi_start + offset;
err = iommu_dma_prepare_msi(info->desc,
v2m->res.start + V2M_MSI_SETSPI_NS);
if (err)
return err;
for (i = 0; i < nr_irqs; i++) {
err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
if (err)
......
......@@ -26,7 +26,6 @@
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/list.h>
#include <linux/list_sort.h>
#include <linux/log2.h>
#include <linux/memblock.h>
#include <linux/mm.h>
......@@ -1179,7 +1178,7 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
msg->address_hi = upper_32_bits(addr);
msg->data = its_get_event_id(d);
iommu_dma_map_msi_msg(d->irq, msg);
iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
}
static int its_irq_set_irqchip_state(struct irq_data *d,
......@@ -1465,9 +1464,8 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span)
{
struct lpi_range *range;
range = kzalloc(sizeof(*range), GFP_KERNEL);
range = kmalloc(sizeof(*range), GFP_KERNEL);
if (range) {
INIT_LIST_HEAD(&range->entry);
range->base_id = base;
range->span = span;
}
......@@ -1475,31 +1473,6 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span)
return range;
}
static int lpi_range_cmp(void *priv, struct list_head *a, struct list_head *b)
{
struct lpi_range *ra, *rb;
ra = container_of(a, struct lpi_range, entry);
rb = container_of(b, struct lpi_range, entry);
return ra->base_id - rb->base_id;
}
static void merge_lpi_ranges(void)
{
struct lpi_range *range, *tmp;
list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) {
if (!list_is_last(&range->entry, &lpi_range_list) &&
(tmp->base_id == (range->base_id + range->span))) {
tmp->base_id = range->base_id;
tmp->span += range->span;
list_del(&range->entry);
kfree(range);
}
}
}
static int alloc_lpi_range(u32 nr_lpis, u32 *base)
{
struct lpi_range *range, *tmp;
......@@ -1529,25 +1502,49 @@ static int alloc_lpi_range(u32 nr_lpis, u32 *base)
return err;
}
static void merge_lpi_ranges(struct lpi_range *a, struct lpi_range *b)
{
if (&a->entry == &lpi_range_list || &b->entry == &lpi_range_list)
return;
if (a->base_id + a->span != b->base_id)
return;
b->base_id = a->base_id;
b->span += a->span;
list_del(&a->entry);
kfree(a);
}
static int free_lpi_range(u32 base, u32 nr_lpis)
{
struct lpi_range *new;
int err = 0;
struct lpi_range *new, *old;
new = mk_lpi_range(base, nr_lpis);
if (!new)
return -ENOMEM;
mutex_lock(&lpi_range_lock);
new = mk_lpi_range(base, nr_lpis);
if (!new) {
err = -ENOMEM;
goto out;
list_for_each_entry_reverse(old, &lpi_range_list, entry) {
if (old->base_id < base)
break;
}
/*
* old is the last element with ->base_id smaller than base,
* so new goes right after it. If there are no elements with
* ->base_id smaller than base, &old->entry ends up pointing
* at the head of the list, and inserting new it the start of
* the list is the right thing to do in that case as well.
*/
list_add(&new->entry, &old->entry);
/*
* Now check if we can merge with the preceding and/or
* following ranges.
*/
merge_lpi_ranges(old, new);
merge_lpi_ranges(new, list_next_entry(new, entry));
list_add(&new->entry, &lpi_range_list);
list_sort(NULL, &lpi_range_list, lpi_range_cmp);
merge_lpi_ranges();
out:
mutex_unlock(&lpi_range_lock);
return err;
return 0;
}
static int __init its_lpi_init(u32 id_bits)
......@@ -2487,7 +2484,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
int err = 0;
/*
* We ignore "dev" entierely, and rely on the dev_id that has
* We ignore "dev" entirely, and rely on the dev_id that has
* been passed via the scratchpad. This limits this domain's
* usefulness to upper layers that definitely know that they
* are built on top of the ITS.
......@@ -2566,6 +2563,7 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
{
msi_alloc_info_t *info = args;
struct its_device *its_dev = info->scratchpad[0].ptr;
struct its_node *its = its_dev->its;
irq_hw_number_t hwirq;
int err;
int i;
......@@ -2574,6 +2572,10 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
if (err)
return err;
err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev));
if (err)
return err;
for (i = 0; i < nr_irqs; i++) {
err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
if (err)
......
......@@ -84,6 +84,7 @@ static void mbi_free_msi(struct mbi_range *mbi, unsigned int hwirq,
static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
msi_alloc_info_t *info = args;
struct mbi_range *mbi = NULL;
int hwirq, offset, i, err = 0;
......@@ -104,6 +105,11 @@ static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
hwirq = mbi->spi_start + offset;
err = iommu_dma_prepare_msi(info->desc,
mbi_phys_base + GICD_SETSPI_NSR);
if (err)
return err;
for (i = 0; i < nr_irqs; i++) {
err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
if (err)
......@@ -142,7 +148,7 @@ static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR);
msg[0].data = data->parent_data->hwirq;
iommu_dma_map_msi_msg(data->irq, msg);
iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
}
#ifdef CONFIG_PCI_MSI
......@@ -202,7 +208,7 @@ static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg)
msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR);
msg[1].data = data->parent_data->hwirq;
iommu_dma_map_msi_msg(data->irq, &msg[1]);
iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]);
}
/* Platform-MSI specific irqchip */
......
......@@ -144,7 +144,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct irqsteer_data *data;
struct resource *res;
u32 irqs_num;
int i, ret;
......@@ -152,8 +151,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->regs = devm_ioremap_resource(&pdev->dev, res);
data->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(data->regs)) {
dev_err(&pdev->dev, "failed to initialize reg\n");
return PTR_ERR(data->regs);
......
......@@ -100,7 +100,7 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
msg->data |= cpumask_first(mask);
}
iommu_dma_map_msi_msg(data->irq, msg);
iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
}
static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
......@@ -141,6 +141,7 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
unsigned int nr_irqs,
void *args)
{
msi_alloc_info_t *info = args;
struct ls_scfg_msi *msi_data = domain->host_data;
int pos, err = 0;
......@@ -154,6 +155,10 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
err = -ENOSPC;
spin_unlock(&msi_data->lock);
if (err)
return err;
err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr);
if (err)
return err;
......
......@@ -389,10 +389,8 @@ static int intc_irqpin_probe(struct platform_device *pdev)
int k;
p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
if (!p) {
dev_err(dev, "failed to allocate driver data\n");
if (!p)
return -ENOMEM;
}
/* deal with driver instance configuration */
of_property_read_u32(dev->of_node, "sense-bitfield-width",
......
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Texas Instruments' K3 Interrupt Router irqchip driver
*
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
* Lokesh Vutla <lokeshvutla@ti.com>
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/soc/ti/ti_sci_protocol.h>
#define TI_SCI_DEV_ID_MASK 0xffff
#define TI_SCI_DEV_ID_SHIFT 16
#define TI_SCI_IRQ_ID_MASK 0xffff
#define TI_SCI_IRQ_ID_SHIFT 0
#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \
(TI_SCI_DEV_ID_MASK))
#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK))
#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \
TI_SCI_DEV_ID_SHIFT) | \
((index) & TI_SCI_IRQ_ID_MASK))
/**
* struct ti_sci_intr_irq_domain - Structure representing a TISCI based
* Interrupt Router IRQ domain.
* @sci: Pointer to TISCI handle
* @dst_irq: TISCI resource pointer representing GIC irq controller.
* @dst_id: TISCI device ID of the GIC irq controller.
* @type: Specifies the trigger type supported by this Interrupt Router
*/
struct ti_sci_intr_irq_domain {
const struct ti_sci_handle *sci;
struct ti_sci_resource *dst_irq;
u32 dst_id;
u32 type;
};
static struct irq_chip ti_sci_intr_irq_chip = {
.name = "INTR",
.irq_eoi = irq_chip_eoi_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_affinity = irq_chip_set_affinity_parent,
};
/**
* ti_sci_intr_irq_domain_translate() - Retrieve hwirq and type from
* IRQ firmware specific handler.
* @domain: Pointer to IRQ domain
* @fwspec: Pointer to IRQ specific firmware structure
* @hwirq: IRQ number identified by hardware
* @type: IRQ type
*
* Return 0 if all went ok else appropriate error.
*/
static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
struct ti_sci_intr_irq_domain *intr = domain->host_data;
if (fwspec->param_count != 2)
return -EINVAL;
*hwirq = TO_HWIRQ(fwspec->param[0], fwspec->param[1]);
*type = intr->type;
return 0;
}
/**
* ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain.
* @domain: Domain to which the irqs belong
* @virq: Linux virtual IRQ to be freed.
* @nr_irqs: Number of continuous irqs to be freed
*/
static void ti_sci_intr_irq_domain_free(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs)
{
struct ti_sci_intr_irq_domain *intr = domain->host_data;
struct irq_data *data, *parent_data;
u16 dev_id, irq_index;
parent_data = irq_domain_get_irq_data(domain->parent, virq);
data = irq_domain_get_irq_data(domain, virq);
irq_index = HWIRQ_TO_IRQID(data->hwirq);
dev_id = HWIRQ_TO_DEVID(data->hwirq);
intr->sci->ops.rm_irq_ops.free_irq(intr->sci, dev_id, irq_index,
intr->dst_id, parent_data->hwirq);
ti_sci_release_resource(intr->dst_irq, parent_data->hwirq);
irq_domain_free_irqs_parent(domain, virq, 1);
irq_domain_reset_irq_data(data);
}
/**
* ti_sci_intr_alloc_gic_irq() - Allocate GIC specific IRQ
* @domain: Pointer to the interrupt router IRQ domain
* @virq: Corresponding Linux virtual IRQ number
* @hwirq: Corresponding hwirq for the IRQ within this IRQ domain
*
* Returns 0 if all went well else appropriate error pointer.
*/
static int ti_sci_intr_alloc_gic_irq(struct irq_domain *domain,
unsigned int virq, u32 hwirq)
{
struct ti_sci_intr_irq_domain *intr = domain->host_data;
struct irq_fwspec fwspec;
u16 dev_id, irq_index;
u16 dst_irq;
int err;
dev_id = HWIRQ_TO_DEVID(hwirq);
irq_index = HWIRQ_TO_IRQID(hwirq);
dst_irq = ti_sci_get_free_resource(intr->dst_irq);
if (dst_irq == TI_SCI_RESOURCE_NULL)
return -EINVAL;
fwspec.fwnode = domain->parent->fwnode;
fwspec.param_count = 3;
fwspec.param[0] = 0; /* SPI */
fwspec.param[1] = dst_irq - 32; /* SPI offset */
fwspec.param[2] = intr->type;
err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
if (err)
goto err_irqs;
err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, dev_id, irq_index,
intr->dst_id, dst_irq);
if (err)
goto err_msg;
return 0;
err_msg:
irq_domain_free_irqs_parent(domain, virq, 1);
err_irqs:
ti_sci_release_resource(intr->dst_irq, dst_irq);
return err;
}
/**
* ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs
* @domain: Point to the interrupt router IRQ domain
* @virq: Corresponding Linux virtual IRQ number
* @nr_irqs: Continuous irqs to be allocated
* @data: Pointer to firmware specifier
*
* Return 0 if all went well else appropriate error value.
*/
static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs,
void *data)
{
struct irq_fwspec *fwspec = data;
unsigned long hwirq;
unsigned int flags;
int err;
err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags);
if (err)
return err;
err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq);
if (err)
return err;
irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
&ti_sci_intr_irq_chip, NULL);
return 0;
}
static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
.free = ti_sci_intr_irq_domain_free,
.alloc = ti_sci_intr_irq_domain_alloc,
.translate = ti_sci_intr_irq_domain_translate,
};
static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
{
struct irq_domain *parent_domain, *domain;
struct ti_sci_intr_irq_domain *intr;
struct device_node *parent_node;
struct device *dev = &pdev->dev;
int ret;
parent_node = of_irq_find_parent(dev_of_node(dev));
if (!parent_node) {
dev_err(dev, "Failed to get IRQ parent node\n");
return -ENODEV;
}
parent_domain = irq_find_host(parent_node);
if (!parent_domain) {
dev_err(dev, "Failed to find IRQ parent domain\n");
return -ENODEV;
}
intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL);
if (!intr)
return -ENOMEM;
ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type",
&intr->type);
if (ret) {
dev_err(dev, "missing ti,intr-trigger-type property\n");
return -EINVAL;
}
intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
if (IS_ERR(intr->sci)) {
ret = PTR_ERR(intr->sci);
if (ret != -EPROBE_DEFER)
dev_err(dev, "ti,sci read fail %d\n", ret);
intr->sci = NULL;
return ret;
}
ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id",
&intr->dst_id);
if (ret) {
dev_err(dev, "missing 'ti,sci-dst-id' property\n");
return -EINVAL;
}
intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev,
intr->dst_id,
"ti,sci-rm-range-girq");
if (IS_ERR(intr->dst_irq)) {
dev_err(dev, "Destination irq resource allocation failed\n");
return PTR_ERR(intr->dst_irq);
}
domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
&ti_sci_intr_irq_domain_ops, intr);
if (!domain) {
dev_err(dev, "Failed to allocate IRQ domain\n");
return -ENOMEM;
}
return 0;
}
static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = {
{ .compatible = "ti,sci-intr", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match);
static struct platform_driver ti_sci_intr_irq_domain_driver = {
.probe = ti_sci_intr_irq_domain_probe,
.driver = {
.name = "ti-sci-intr",
.of_match_table = ti_sci_intr_irq_domain_of_match,
},
};
module_platform_driver(ti_sci_intr_irq_domain_driver);
MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol");
MODULE_LICENSE("GPL v2");
......@@ -74,4 +74,10 @@ config TI_SCI_PM_DOMAINS
called ti_sci_pm_domains. Note this is needed early in boot before
rootfs may be available.
config TI_SCI_INTA_MSI_DOMAIN
bool
select GENERIC_MSI_IRQ_DOMAIN
help
Driver to enable Interrupt Aggregator specific MSI Domain.
endif # SOC_TI
......@@ -8,3 +8,4 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
obj-$(CONFIG_AMX3_PM) += pm33xx.o
obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o
obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o
obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o
// SPDX-License-Identifier: GPL-2.0
/*
* Texas Instruments' K3 Interrupt Aggregator MSI bus
*
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
* Lokesh Vutla <lokeshvutla@ti.com>
*/
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/soc/ti/ti_sci_inta_msi.h>
#include <linux/soc/ti/ti_sci_protocol.h>
static void ti_sci_inta_msi_write_msg(struct irq_data *data,
struct msi_msg *msg)
{
/* Nothing to do */
}
static void ti_sci_inta_msi_compose_msi_msg(struct irq_data *data,
struct msi_msg *msg)
{
/* Nothing to do */
}
static void ti_sci_inta_msi_update_chip_ops(struct msi_domain_info *info)
{
struct irq_chip *chip = info->chip;
if (WARN_ON(!chip))
return;
chip->irq_request_resources = irq_chip_request_resources_parent;
chip->irq_release_resources = irq_chip_release_resources_parent;
chip->irq_compose_msi_msg = ti_sci_inta_msi_compose_msi_msg;
chip->irq_write_msi_msg = ti_sci_inta_msi_write_msg;
chip->irq_set_type = irq_chip_set_type_parent;
chip->irq_unmask = irq_chip_unmask_parent;
chip->irq_mask = irq_chip_mask_parent;
chip->irq_ack = irq_chip_ack_parent;
}
struct irq_domain *ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
struct msi_domain_info *info,
struct irq_domain *parent)
{
struct irq_domain *domain;
ti_sci_inta_msi_update_chip_ops(info);
domain = msi_create_irq_domain(fwnode, info, parent);
if (domain)
irq_domain_update_bus_token(domain, DOMAIN_BUS_TI_SCI_INTA_MSI);
return domain;
}
EXPORT_SYMBOL_GPL(ti_sci_inta_msi_create_irq_domain);
static void ti_sci_inta_msi_free_descs(struct device *dev)
{
struct msi_desc *desc, *tmp;
list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
list_del(&desc->list);
free_msi_entry(desc);
}
}
static int ti_sci_inta_msi_alloc_descs(struct device *dev,
struct ti_sci_resource *res)
{
struct msi_desc *msi_desc;
int set, i, count = 0;
for (set = 0; set < res->sets; set++) {
for (i = 0; i < res->desc[set].num; i++) {
msi_desc = alloc_msi_entry(dev, 1, NULL);
if (!msi_desc) {
ti_sci_inta_msi_free_descs(dev);
return -ENOMEM;
}
msi_desc->inta.dev_index = res->desc[set].start + i;
INIT_LIST_HEAD(&msi_desc->list);
list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
count++;
}
}
return count;
}
int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev,
struct ti_sci_resource *res)
{
struct platform_device *pdev = to_platform_device(dev);
struct irq_domain *msi_domain;
int ret, nvec;
msi_domain = dev_get_msi_domain(dev);
if (!msi_domain)
return -EINVAL;
if (pdev->id < 0)
return -ENODEV;
nvec = ti_sci_inta_msi_alloc_descs(dev, res);
if (nvec <= 0)
return nvec;
ret = msi_domain_alloc_irqs(msi_domain, dev, nvec);
if (ret) {
dev_err(dev, "Failed to allocate IRQs %d\n", ret);
goto cleanup;
}
return 0;
cleanup:
ti_sci_inta_msi_free_descs(&pdev->dev);
return ret;
}
EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_alloc_irqs);
void ti_sci_inta_msi_domain_free_irqs(struct device *dev)
{
msi_domain_free_irqs(dev->msi_domain, dev);
ti_sci_inta_msi_free_descs(dev);
}
EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_free_irqs);
unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 dev_index)
{
struct msi_desc *desc;
for_each_msi_entry(desc, dev)
if (desc->inta.dev_index == dev_index)
return desc->irq;
return -ENODEV;
}
EXPORT_SYMBOL_GPL(ti_sci_inta_msi_get_virq);
......@@ -71,12 +71,25 @@ void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir, unsigned long attrs);
/* The DMA API isn't _quite_ the whole story, though... */
void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
/*
* iommu_dma_prepare_msi() - Map the MSI page in the IOMMU device
*
* The MSI page will be stored in @desc.
*
* Return: 0 on success otherwise an error describing the failure.
*/
int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr);
/* Update the MSI message if required. */
void iommu_dma_compose_msi_msg(struct msi_desc *desc,
struct msi_msg *msg);
void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
#else
struct iommu_domain;
struct msi_desc;
struct msi_msg;
struct device;
......@@ -99,7 +112,14 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
{
}
static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
static inline int iommu_dma_prepare_msi(struct msi_desc *desc,
phys_addr_t msi_addr)
{
return 0;
}
static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc,
struct msi_msg *msg)
{
}
......
......@@ -625,6 +625,8 @@ extern int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on);
extern int irq_chip_set_vcpu_affinity_parent(struct irq_data *data,
void *vcpu_info);
extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type);
extern int irq_chip_request_resources_parent(struct irq_data *data);
extern void irq_chip_release_resources_parent(struct irq_data *data);
#endif
/* Handling of unhandled and spurious interrupts: */
......
......@@ -165,7 +165,7 @@
#define GICR_PROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nCnB)
#define GICR_PROPBASER_nC GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nC)
#define GICR_PROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt)
#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt)
#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb)
#define GICR_PROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWt)
#define GICR_PROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWb)
#define GICR_PROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWt)
......@@ -192,7 +192,7 @@
#define GICR_PENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nCnB)
#define GICR_PENDBASER_nC GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nC)
#define GICR_PENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt)
#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt)
#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb)
#define GICR_PENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWt)
#define GICR_PENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWb)
#define GICR_PENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWt)
......@@ -251,7 +251,7 @@
#define GICR_VPROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nCnB)
#define GICR_VPROPBASER_nC GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nC)
#define GICR_VPROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt)
#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt)
#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWb)
#define GICR_VPROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWt)
#define GICR_VPROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWb)
#define GICR_VPROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWt)
......@@ -277,7 +277,7 @@
#define GICR_VPENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nCnB)
#define GICR_VPENDBASER_nC GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nC)
#define GICR_VPENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt)
#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt)
#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWb)
#define GICR_VPENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWt)
#define GICR_VPENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWb)
#define GICR_VPENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWt)
......@@ -351,7 +351,7 @@
#define GITS_CBASER_nCnB GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nCnB)
#define GITS_CBASER_nC GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nC)
#define GITS_CBASER_RaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt)
#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt)
#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWb)
#define GITS_CBASER_WaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWt)
#define GITS_CBASER_WaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWb)
#define GITS_CBASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWt)
......@@ -377,7 +377,7 @@
#define GITS_BASER_nCnB GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nCnB)
#define GITS_BASER_nC GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nC)
#define GITS_BASER_RaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt)
#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt)
#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb)
#define GITS_BASER_WaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWt)
#define GITS_BASER_WaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWb)
#define GITS_BASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWt)
......
......@@ -82,6 +82,7 @@ enum irq_domain_bus_token {
DOMAIN_BUS_NEXUS,
DOMAIN_BUS_IPI,
DOMAIN_BUS_FSL_MC_MSI,
DOMAIN_BUS_TI_SCI_INTA_MSI,
};
/**
......
......@@ -47,6 +47,14 @@ struct fsl_mc_msi_desc {
u16 msi_index;
};
/**
* ti_sci_inta_msi_desc - TISCI based INTA specific msi descriptor data
* @dev_index: TISCI device index
*/
struct ti_sci_inta_msi_desc {
u16 dev_index;
};
/**
* struct msi_desc - Descriptor structure for MSI based interrupts
* @list: List head for management
......@@ -68,6 +76,7 @@ struct fsl_mc_msi_desc {
* @mask_base: [PCI MSI-X] Mask register base address
* @platform: [platform] Platform device specific msi descriptor data
* @fsl_mc: [fsl-mc] FSL MC device specific msi descriptor data
* @inta: [INTA] TISCI based INTA specific msi descriptor data
*/
struct msi_desc {
/* Shared device/bus type independent data */
......@@ -77,6 +86,9 @@ struct msi_desc {
struct device *dev;
struct msi_msg msg;
struct irq_affinity_desc *affinity;
#ifdef CONFIG_IRQ_MSI_IOMMU
const void *iommu_cookie;
#endif
union {
/* PCI MSI/X specific data */
......@@ -106,6 +118,7 @@ struct msi_desc {
*/
struct platform_msi_desc platform;
struct fsl_mc_msi_desc fsl_mc;
struct ti_sci_inta_msi_desc inta;
};
};
......@@ -119,6 +132,29 @@ struct msi_desc {
#define for_each_msi_entry_safe(desc, tmp, dev) \
list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list)
#ifdef CONFIG_IRQ_MSI_IOMMU
static inline const void *msi_desc_get_iommu_cookie(struct msi_desc *desc)
{
return desc->iommu_cookie;
}
static inline void msi_desc_set_iommu_cookie(struct msi_desc *desc,
const void *iommu_cookie)
{
desc->iommu_cookie = iommu_cookie;
}
#else
static inline const void *msi_desc_get_iommu_cookie(struct msi_desc *desc)
{
return NULL;
}
static inline void msi_desc_set_iommu_cookie(struct msi_desc *desc,
const void *iommu_cookie)
{
}
#endif
#ifdef CONFIG_PCI_MSI
#define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev)
#define for_each_pci_msi_entry(desc, pdev) \
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Texas Instruments' K3 TI SCI INTA MSI helper
*
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
* Lokesh Vutla <lokeshvutla@ti.com>
*/
#ifndef __INCLUDE_LINUX_TI_SCI_INTA_MSI_H
#define __INCLUDE_LINUX_TI_SCI_INTA_MSI_H
#include <linux/msi.h>
#include <linux/soc/ti/ti_sci_protocol.h>
struct irq_domain
*ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
struct msi_domain_info *info,
struct irq_domain *parent);
int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev,
struct ti_sci_resource *res);
unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 index);
void ti_sci_inta_msi_domain_free_irqs(struct device *dev);
#endif /* __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H */
......@@ -192,15 +192,68 @@ struct ti_sci_clk_ops {
u64 *current_freq);
};
/**
* struct ti_sci_rm_core_ops - Resource management core operations
* @get_range: Get a range of resources belonging to ti sci host.
* @get_rage_from_shost: Get a range of resources belonging to
* specified host id.
* - s_host: Host processing entity to which the
* resources are allocated
*
* NOTE: for these functions, all the parameters are consolidated and defined
* as below:
* - handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
* - dev_id: TISCI device ID.
* - subtype: Resource assignment subtype that is being requested
* from the given device.
* - range_start: Start index of the resource range
* - range_end: Number of resources in the range
*/
struct ti_sci_rm_core_ops {
int (*get_range)(const struct ti_sci_handle *handle, u32 dev_id,
u8 subtype, u16 *range_start, u16 *range_num);
int (*get_range_from_shost)(const struct ti_sci_handle *handle,
u32 dev_id, u8 subtype, u8 s_host,
u16 *range_start, u16 *range_num);
};
/**
* struct ti_sci_rm_irq_ops: IRQ management operations
* @set_irq: Set an IRQ route between the requested source
* and destination
* @set_event_map: Set an Event based peripheral irq to Interrupt
* Aggregator.
* @free_irq: Free an an IRQ route between the requested source
* destination.
* @free_event_map: Free an event based peripheral irq to Interrupt
* Aggregator.
*/
struct ti_sci_rm_irq_ops {
int (*set_irq)(const struct ti_sci_handle *handle, u16 src_id,
u16 src_index, u16 dst_id, u16 dst_host_irq);
int (*set_event_map)(const struct ti_sci_handle *handle, u16 src_id,
u16 src_index, u16 ia_id, u16 vint,
u16 global_event, u8 vint_status_bit);
int (*free_irq)(const struct ti_sci_handle *handle, u16 src_id,
u16 src_index, u16 dst_id, u16 dst_host_irq);
int (*free_event_map)(const struct ti_sci_handle *handle, u16 src_id,
u16 src_index, u16 ia_id, u16 vint,
u16 global_event, u8 vint_status_bit);
};
/**
* struct ti_sci_ops - Function support for TI SCI
* @dev_ops: Device specific operations
* @clk_ops: Clock specific operations
* @rm_core_ops: Resource management core operations.
* @rm_irq_ops: IRQ management specific operations
*/
struct ti_sci_ops {
struct ti_sci_core_ops core_ops;
struct ti_sci_dev_ops dev_ops;
struct ti_sci_clk_ops clk_ops;
struct ti_sci_rm_core_ops rm_core_ops;
struct ti_sci_rm_irq_ops rm_irq_ops;
};
/**
......@@ -213,10 +266,47 @@ struct ti_sci_handle {
struct ti_sci_ops ops;
};
#define TI_SCI_RESOURCE_NULL 0xffff
/**
* struct ti_sci_resource_desc - Description of TI SCI resource instance range.
* @start: Start index of the resource.
* @num: Number of resources.
* @res_map: Bitmap to manage the allocation of these resources.
*/
struct ti_sci_resource_desc {
u16 start;
u16 num;
unsigned long *res_map;
};
/**
* struct ti_sci_resource - Structure representing a resource assigned
* to a device.
* @sets: Number of sets available from this resource type
* @lock: Lock to guard the res map in each set.
* @desc: Array of resource descriptors.
*/
struct ti_sci_resource {
u16 sets;
raw_spinlock_t lock;
struct ti_sci_resource_desc *desc;
};
#if IS_ENABLED(CONFIG_TI_SCI_PROTOCOL)
const struct ti_sci_handle *ti_sci_get_handle(struct device *dev);
int ti_sci_put_handle(const struct ti_sci_handle *handle);
const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev);
const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np,
const char *property);
const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev,
const char *property);
u16 ti_sci_get_free_resource(struct ti_sci_resource *res);
void ti_sci_release_resource(struct ti_sci_resource *res, u16 id);
u32 ti_sci_get_num_resources(struct ti_sci_resource *res);
struct ti_sci_resource *
devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
struct device *dev, u32 dev_id, char *of_prop);
#else /* CONFIG_TI_SCI_PROTOCOL */
......@@ -236,6 +326,40 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev)
return ERR_PTR(-EINVAL);
}
static inline
const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np,
const char *property)
{
return ERR_PTR(-EINVAL);
}
static inline
const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev,
const char *property)
{
return ERR_PTR(-EINVAL);
}
static inline u16 ti_sci_get_free_resource(struct ti_sci_resource *res)
{
return TI_SCI_RESOURCE_NULL;
}
static inline void ti_sci_release_resource(struct ti_sci_resource *res, u16 id)
{
}
static inline u32 ti_sci_get_num_resources(struct ti_sci_resource *res)
{
return 0;
}
static inline struct ti_sci_resource *
devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
struct device *dev, u32 dev_id, char *of_prop)
{
return ERR_PTR(-EINVAL);
}
#endif /* CONFIG_TI_SCI_PROTOCOL */
#endif /* __TISCI_PROTOCOL_H */
......@@ -91,6 +91,9 @@ config GENERIC_MSI_IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GENERIC_MSI_IRQ
config IRQ_MSI_IOMMU
bool
config HANDLE_DOMAIN_IRQ
bool
......
......@@ -1459,6 +1459,33 @@ int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on)
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent);
/**
* irq_chip_request_resources_parent - Request resources on the parent interrupt
* @data: Pointer to interrupt specific data
*/
int irq_chip_request_resources_parent(struct irq_data *data)
{
data = data->parent_data;
if (data->chip->irq_request_resources)
return data->chip->irq_request_resources(data);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(irq_chip_request_resources_parent);
/**
* irq_chip_release_resources_parent - Release resources on the parent interrupt
* @data: Pointer to interrupt specific data
*/
void irq_chip_release_resources_parent(struct irq_data *data)
{
data = data->parent_data;
if (data->chip->irq_release_resources)
data->chip->irq_release_resources(data);
}
EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent);
#endif
/**
......
......@@ -1297,7 +1297,7 @@ int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
/**
* __irq_domain_alloc_irqs - Allocate IRQs from domain
* @domain: domain to allocate from
* @irq_base: allocate specified IRQ nubmer if irq_base >= 0
* @irq_base: allocate specified IRQ number if irq_base >= 0
* @nr_irqs: number of IRQs to allocate
* @node: NUMA node id for memory allocation
* @arg: domain specific argument
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment