mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-04 04:04:19 +00:00
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 ...
This commit is contained in:
commit
d9351ea14d
@ -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
|
||||
|
@ -0,0 +1,66 @@
|
||||
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>;
|
||||
};
|
@ -0,0 +1,82 @@
|
||||
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.
|
||||
|
@ -64,6 +64,22 @@ struct ti_sci_xfers_info {
|
||||
spinlock_t xfer_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_sci_rm_type_map - Structure representing TISCI Resource
|
||||
* management representation of dev_ids.
|
||||
* @dev_id: TISCI device ID
|
||||
* @type: Corresponding id as identified by TISCI RM.
|
||||
*
|
||||
* Note: This is used only as a work around for using RM range apis
|
||||
* for AM654 SoC. For future SoCs dev_id will be used as type
|
||||
* for RM range APIs. In order to maintain ABI backward compatibility
|
||||
* type is not being changed for AM654 SoC.
|
||||
*/
|
||||
struct ti_sci_rm_type_map {
|
||||
u32 dev_id;
|
||||
u16 type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_sci_desc - Description of SoC integration
|
||||
* @default_host_id: Host identifier representing the compute entity
|
||||
@ -71,12 +87,14 @@ struct ti_sci_xfers_info {
|
||||
* @max_msgs: Maximum number of messages that can be pending
|
||||
* simultaneously in the system
|
||||
* @max_msg_size: Maximum size of data per message that can be handled.
|
||||
* @rm_type_map: RM resource type mapping structure.
|
||||
*/
|
||||
struct ti_sci_desc {
|
||||
u8 default_host_id;
|
||||
int max_rx_timeout_ms;
|
||||
int max_msgs;
|
||||
int max_msg_size;
|
||||
struct ti_sci_rm_type_map *rm_type_map;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1600,6 +1618,392 @@ static int ti_sci_cmd_core_reboot(const struct ti_sci_handle *handle)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_sci_get_resource_type(struct ti_sci_info *info, u16 dev_id,
|
||||
u16 *type)
|
||||
{
|
||||
struct ti_sci_rm_type_map *rm_type_map = info->desc->rm_type_map;
|
||||
bool found = false;
|
||||
int i;
|
||||
|
||||
/* If map is not provided then assume dev_id is used as type */
|
||||
if (!rm_type_map) {
|
||||
*type = dev_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; rm_type_map[i].dev_id; i++) {
|
||||
if (rm_type_map[i].dev_id == dev_id) {
|
||||
*type = rm_type_map[i].type;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_get_resource_range - Helper to get a range of resources assigned
|
||||
* to a host. Resource is uniquely identified by
|
||||
* type and subtype.
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @dev_id: TISCI device ID.
|
||||
* @subtype: Resource assignment subtype that is being requested
|
||||
* from the given device.
|
||||
* @s_host: Host processor ID to which the resources are allocated
|
||||
* @range_start: Start index of the resource range
|
||||
* @range_num: Number of resources in the range
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_get_resource_range(const struct ti_sci_handle *handle,
|
||||
u32 dev_id, u8 subtype, u8 s_host,
|
||||
u16 *range_start, u16 *range_num)
|
||||
{
|
||||
struct ti_sci_msg_resp_get_resource_range *resp;
|
||||
struct ti_sci_msg_req_get_resource_range *req;
|
||||
struct ti_sci_xfer *xfer;
|
||||
struct ti_sci_info *info;
|
||||
struct device *dev;
|
||||
u16 type;
|
||||
int ret = 0;
|
||||
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
if (!handle)
|
||||
return -EINVAL;
|
||||
|
||||
info = handle_to_ti_sci_info(handle);
|
||||
dev = info->dev;
|
||||
|
||||
xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_RESOURCE_RANGE,
|
||||
TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
|
||||
sizeof(*req), sizeof(*resp));
|
||||
if (IS_ERR(xfer)) {
|
||||
ret = PTR_ERR(xfer);
|
||||
dev_err(dev, "Message alloc failed(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ti_sci_get_resource_type(info, dev_id, &type);
|
||||
if (ret) {
|
||||
dev_err(dev, "rm type lookup failed for %u\n", dev_id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
req = (struct ti_sci_msg_req_get_resource_range *)xfer->xfer_buf;
|
||||
req->secondary_host = s_host;
|
||||
req->type = type & MSG_RM_RESOURCE_TYPE_MASK;
|
||||
req->subtype = subtype & MSG_RM_RESOURCE_SUBTYPE_MASK;
|
||||
|
||||
ret = ti_sci_do_xfer(info, xfer);
|
||||
if (ret) {
|
||||
dev_err(dev, "Mbox send fail %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
resp = (struct ti_sci_msg_resp_get_resource_range *)xfer->xfer_buf;
|
||||
|
||||
if (!ti_sci_is_response_ack(resp)) {
|
||||
ret = -ENODEV;
|
||||
} else if (!resp->range_start && !resp->range_num) {
|
||||
ret = -ENODEV;
|
||||
} else {
|
||||
*range_start = resp->range_start;
|
||||
*range_num = resp->range_num;
|
||||
};
|
||||
|
||||
fail:
|
||||
ti_sci_put_one_xfer(&info->minfo, xfer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_get_resource_range - Get a range of resources assigned to host
|
||||
* that is same as ti sci interface host.
|
||||
* @handle: Pointer to TISCI 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_num: Number of resources in the range
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle,
|
||||
u32 dev_id, u8 subtype,
|
||||
u16 *range_start, u16 *range_num)
|
||||
{
|
||||
return ti_sci_get_resource_range(handle, dev_id, subtype,
|
||||
TI_SCI_IRQ_SECONDARY_HOST_INVALID,
|
||||
range_start, range_num);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_get_resource_range_from_shost - Get a range of resources
|
||||
* assigned to a specified host.
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @dev_id: TISCI device ID.
|
||||
* @subtype: Resource assignment subtype that is being requested
|
||||
* from the given device.
|
||||
* @s_host: Host processor ID to which the resources are allocated
|
||||
* @range_start: Start index of the resource range
|
||||
* @range_num: Number of resources in the range
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static
|
||||
int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle,
|
||||
u32 dev_id, u8 subtype, u8 s_host,
|
||||
u16 *range_start, u16 *range_num)
|
||||
{
|
||||
return ti_sci_get_resource_range(handle, dev_id, subtype, s_host,
|
||||
range_start, range_num);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_manage_irq() - Helper api to configure/release the irq route between
|
||||
* the requested source and destination
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @valid_params: Bit fields defining the validity of certain params
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @dst_id: Device ID of the IRQ destination
|
||||
* @dst_host_irq: IRQ number of the destination device
|
||||
* @ia_id: Device ID of the IA, if the IRQ flows through this IA
|
||||
* @vint: Virtual interrupt to be used within the IA
|
||||
* @global_event: Global event number to be used for the requesting event
|
||||
* @vint_status_bit: Virtual interrupt status bit to be used for the event
|
||||
* @s_host: Secondary host ID to which the irq/event is being
|
||||
* requested for.
|
||||
* @type: Request type irq set or release.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_manage_irq(const struct ti_sci_handle *handle,
|
||||
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 s_host,
|
||||
u16 type)
|
||||
{
|
||||
struct ti_sci_msg_req_manage_irq *req;
|
||||
struct ti_sci_msg_hdr *resp;
|
||||
struct ti_sci_xfer *xfer;
|
||||
struct ti_sci_info *info;
|
||||
struct device *dev;
|
||||
int ret = 0;
|
||||
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
if (!handle)
|
||||
return -EINVAL;
|
||||
|
||||
info = handle_to_ti_sci_info(handle);
|
||||
dev = info->dev;
|
||||
|
||||
xfer = ti_sci_get_one_xfer(info, type, TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
|
||||
sizeof(*req), sizeof(*resp));
|
||||
if (IS_ERR(xfer)) {
|
||||
ret = PTR_ERR(xfer);
|
||||
dev_err(dev, "Message alloc failed(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
req = (struct ti_sci_msg_req_manage_irq *)xfer->xfer_buf;
|
||||
req->valid_params = valid_params;
|
||||
req->src_id = src_id;
|
||||
req->src_index = src_index;
|
||||
req->dst_id = dst_id;
|
||||
req->dst_host_irq = dst_host_irq;
|
||||
req->ia_id = ia_id;
|
||||
req->vint = vint;
|
||||
req->global_event = global_event;
|
||||
req->vint_status_bit = vint_status_bit;
|
||||
req->secondary_host = s_host;
|
||||
|
||||
ret = ti_sci_do_xfer(info, xfer);
|
||||
if (ret) {
|
||||
dev_err(dev, "Mbox send fail %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf;
|
||||
|
||||
ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV;
|
||||
|
||||
fail:
|
||||
ti_sci_put_one_xfer(&info->minfo, xfer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_set_irq() - Helper api to configure the irq route between the
|
||||
* requested source and destination
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @valid_params: Bit fields defining the validity of certain params
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @dst_id: Device ID of the IRQ destination
|
||||
* @dst_host_irq: IRQ number of the destination device
|
||||
* @ia_id: Device ID of the IA, if the IRQ flows through this IA
|
||||
* @vint: Virtual interrupt to be used within the IA
|
||||
* @global_event: Global event number to be used for the requesting event
|
||||
* @vint_status_bit: Virtual interrupt status bit to be used for the event
|
||||
* @s_host: Secondary host ID to which the irq/event is being
|
||||
* requested for.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_set_irq(const struct ti_sci_handle *handle, 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 s_host)
|
||||
{
|
||||
pr_debug("%s: IRQ set with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n",
|
||||
__func__, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint, global_event,
|
||||
vint_status_bit);
|
||||
|
||||
return ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_SET_IRQ);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_free_irq() - Helper api to free the irq route between the
|
||||
* requested source and destination
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @valid_params: Bit fields defining the validity of certain params
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @dst_id: Device ID of the IRQ destination
|
||||
* @dst_host_irq: IRQ number of the destination device
|
||||
* @ia_id: Device ID of the IA, if the IRQ flows through this IA
|
||||
* @vint: Virtual interrupt to be used within the IA
|
||||
* @global_event: Global event number to be used for the requesting event
|
||||
* @vint_status_bit: Virtual interrupt status bit to be used for the event
|
||||
* @s_host: Secondary host ID to which the irq/event is being
|
||||
* requested for.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_free_irq(const struct ti_sci_handle *handle, 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 s_host)
|
||||
{
|
||||
pr_debug("%s: IRQ release with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n",
|
||||
__func__, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint, global_event,
|
||||
vint_status_bit);
|
||||
|
||||
return ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_FREE_IRQ);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_set_irq() - Configure a host irq route between the requested
|
||||
* source and destination.
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @dst_id: Device ID of the IRQ destination
|
||||
* @dst_host_irq: IRQ number of the destination device
|
||||
* @vint_irq: Boolean specifying if this interrupt belongs to
|
||||
* Interrupt Aggregator.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_set_irq(const struct ti_sci_handle *handle, u16 src_id,
|
||||
u16 src_index, u16 dst_id, u16 dst_host_irq)
|
||||
{
|
||||
u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID;
|
||||
|
||||
return ti_sci_set_irq(handle, valid_params, src_id, src_index, dst_id,
|
||||
dst_host_irq, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_set_event_map() - Configure an event based irq route between the
|
||||
* requested source and Interrupt Aggregator.
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @ia_id: Device ID of the IA, if the IRQ flows through this IA
|
||||
* @vint: Virtual interrupt to be used within the IA
|
||||
* @global_event: Global event number to be used for the requesting event
|
||||
* @vint_status_bit: Virtual interrupt status bit to be used for the event
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_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)
|
||||
{
|
||||
u32 valid_params = MSG_FLAG_IA_ID_VALID | MSG_FLAG_VINT_VALID |
|
||||
MSG_FLAG_GLB_EVNT_VALID |
|
||||
MSG_FLAG_VINT_STS_BIT_VALID;
|
||||
|
||||
return ti_sci_set_irq(handle, valid_params, src_id, src_index, 0, 0,
|
||||
ia_id, vint, global_event, vint_status_bit, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_free_irq() - Free a host irq route between the between the
|
||||
* requested source and destination.
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @dst_id: Device ID of the IRQ destination
|
||||
* @dst_host_irq: IRQ number of the destination device
|
||||
* @vint_irq: Boolean specifying if this interrupt belongs to
|
||||
* Interrupt Aggregator.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_free_irq(const struct ti_sci_handle *handle, u16 src_id,
|
||||
u16 src_index, u16 dst_id, u16 dst_host_irq)
|
||||
{
|
||||
u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID;
|
||||
|
||||
return ti_sci_free_irq(handle, valid_params, src_id, src_index, dst_id,
|
||||
dst_host_irq, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_free_event_map() - Free an event map between the requested source
|
||||
* and Interrupt Aggregator.
|
||||
* @handle: Pointer to TISCI handle.
|
||||
* @src_id: Device ID of the IRQ source
|
||||
* @src_index: IRQ source index within the source device
|
||||
* @ia_id: Device ID of the IA, if the IRQ flows through this IA
|
||||
* @vint: Virtual interrupt to be used within the IA
|
||||
* @global_event: Global event number to be used for the requesting event
|
||||
* @vint_status_bit: Virtual interrupt status bit to be used for the event
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_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)
|
||||
{
|
||||
u32 valid_params = MSG_FLAG_IA_ID_VALID |
|
||||
MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID |
|
||||
MSG_FLAG_VINT_STS_BIT_VALID;
|
||||
|
||||
return ti_sci_free_irq(handle, valid_params, src_id, src_index, 0, 0,
|
||||
ia_id, vint, global_event, vint_status_bit, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ti_sci_setup_ops() - Setup the operations structures
|
||||
* @info: pointer to TISCI pointer
|
||||
@ -1610,6 +2014,8 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
|
||||
struct ti_sci_core_ops *core_ops = &ops->core_ops;
|
||||
struct ti_sci_dev_ops *dops = &ops->dev_ops;
|
||||
struct ti_sci_clk_ops *cops = &ops->clk_ops;
|
||||
struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops;
|
||||
struct ti_sci_rm_irq_ops *iops = &ops->rm_irq_ops;
|
||||
|
||||
core_ops->reboot_device = ti_sci_cmd_core_reboot;
|
||||
|
||||
@ -1640,6 +2046,15 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
|
||||
cops->get_best_match_freq = ti_sci_cmd_clk_get_match_freq;
|
||||
cops->set_freq = ti_sci_cmd_clk_set_freq;
|
||||
cops->get_freq = ti_sci_cmd_clk_get_freq;
|
||||
|
||||
rm_core_ops->get_range = ti_sci_cmd_get_resource_range;
|
||||
rm_core_ops->get_range_from_shost =
|
||||
ti_sci_cmd_get_resource_range_from_shost;
|
||||
|
||||
iops->set_irq = ti_sci_cmd_set_irq;
|
||||
iops->set_event_map = ti_sci_cmd_set_event_map;
|
||||
iops->free_irq = ti_sci_cmd_free_irq;
|
||||
iops->free_event_map = ti_sci_cmd_free_event_map;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1764,6 +2179,219 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_ti_sci_get_handle);
|
||||
|
||||
/**
|
||||
* ti_sci_get_by_phandle() - Get the TI SCI handle using DT phandle
|
||||
* @np: device node
|
||||
* @property: property name containing phandle on TISCI node
|
||||
*
|
||||
* NOTE: The function does not track individual clients of the framework
|
||||
* and is expected to be maintained by caller of TI SCI protocol library.
|
||||
* ti_sci_put_handle must be balanced with successful ti_sci_get_by_phandle
|
||||
* Return: pointer to handle if successful, else:
|
||||
* -EPROBE_DEFER if the instance is not ready
|
||||
* -ENODEV if the required node handler is missing
|
||||
* -EINVAL if invalid conditions are encountered.
|
||||
*/
|
||||
const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np,
|
||||
const char *property)
|
||||
{
|
||||
struct ti_sci_handle *handle = NULL;
|
||||
struct device_node *ti_sci_np;
|
||||
struct ti_sci_info *info;
|
||||
struct list_head *p;
|
||||
|
||||
if (!np) {
|
||||
pr_err("I need a device pointer\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
ti_sci_np = of_parse_phandle(np, property, 0);
|
||||
if (!ti_sci_np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
mutex_lock(&ti_sci_list_mutex);
|
||||
list_for_each(p, &ti_sci_list) {
|
||||
info = list_entry(p, struct ti_sci_info, node);
|
||||
if (ti_sci_np == info->dev->of_node) {
|
||||
handle = &info->handle;
|
||||
info->users++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ti_sci_list_mutex);
|
||||
of_node_put(ti_sci_np);
|
||||
|
||||
if (!handle)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return handle;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ti_sci_get_by_phandle);
|
||||
|
||||
/**
|
||||
* devm_ti_sci_get_by_phandle() - Managed get handle using phandle
|
||||
* @dev: Device pointer requesting TISCI handle
|
||||
* @property: property name containing phandle on TISCI node
|
||||
*
|
||||
* NOTE: This releases the handle once the device resources are
|
||||
* no longer needed. MUST NOT BE released with ti_sci_put_handle.
|
||||
* The function does not track individual clients of the framework
|
||||
* and is expected to be maintained by caller of TI SCI protocol library.
|
||||
*
|
||||
* Return: 0 if all went fine, else corresponding error.
|
||||
*/
|
||||
const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev,
|
||||
const char *property)
|
||||
{
|
||||
const struct ti_sci_handle *handle;
|
||||
const struct ti_sci_handle **ptr;
|
||||
|
||||
ptr = devres_alloc(devm_ti_sci_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
handle = ti_sci_get_by_phandle(dev_of_node(dev), property);
|
||||
|
||||
if (!IS_ERR(handle)) {
|
||||
*ptr = handle;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_ti_sci_get_by_phandle);
|
||||
|
||||
/**
|
||||
* ti_sci_get_free_resource() - Get a free resource from TISCI resource.
|
||||
* @res: Pointer to the TISCI resource
|
||||
*
|
||||
* Return: resource num if all went ok else TI_SCI_RESOURCE_NULL.
|
||||
*/
|
||||
u16 ti_sci_get_free_resource(struct ti_sci_resource *res)
|
||||
{
|
||||
unsigned long flags;
|
||||
u16 set, free_bit;
|
||||
|
||||
raw_spin_lock_irqsave(&res->lock, flags);
|
||||
for (set = 0; set < res->sets; set++) {
|
||||
free_bit = find_first_zero_bit(res->desc[set].res_map,
|
||||
res->desc[set].num);
|
||||
if (free_bit != res->desc[set].num) {
|
||||
set_bit(free_bit, res->desc[set].res_map);
|
||||
raw_spin_unlock_irqrestore(&res->lock, flags);
|
||||
return res->desc[set].start + free_bit;
|
||||
}
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&res->lock, flags);
|
||||
|
||||
return TI_SCI_RESOURCE_NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ti_sci_get_free_resource);
|
||||
|
||||
/**
|
||||
* ti_sci_release_resource() - Release a resource from TISCI resource.
|
||||
* @res: Pointer to the TISCI resource
|
||||
* @id: Resource id to be released.
|
||||
*/
|
||||
void ti_sci_release_resource(struct ti_sci_resource *res, u16 id)
|
||||
{
|
||||
unsigned long flags;
|
||||
u16 set;
|
||||
|
||||
raw_spin_lock_irqsave(&res->lock, flags);
|
||||
for (set = 0; set < res->sets; set++) {
|
||||
if (res->desc[set].start <= id &&
|
||||
(res->desc[set].num + res->desc[set].start) > id)
|
||||
clear_bit(id - res->desc[set].start,
|
||||
res->desc[set].res_map);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&res->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ti_sci_release_resource);
|
||||
|
||||
/**
|
||||
* ti_sci_get_num_resources() - Get the number of resources in TISCI resource
|
||||
* @res: Pointer to the TISCI resource
|
||||
*
|
||||
* Return: Total number of available resources.
|
||||
*/
|
||||
u32 ti_sci_get_num_resources(struct ti_sci_resource *res)
|
||||
{
|
||||
u32 set, count = 0;
|
||||
|
||||
for (set = 0; set < res->sets; set++)
|
||||
count += res->desc[set].num;
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ti_sci_get_num_resources);
|
||||
|
||||
/**
|
||||
* devm_ti_sci_get_of_resource() - Get a TISCI resource assigned to a device
|
||||
* @handle: TISCI handle
|
||||
* @dev: Device pointer to which the resource is assigned
|
||||
* @dev_id: TISCI device id to which the resource is assigned
|
||||
* @of_prop: property name by which the resource are represented
|
||||
*
|
||||
* Return: Pointer to ti_sci_resource if all went well else appropriate
|
||||
* error pointer.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct ti_sci_resource *res;
|
||||
u32 resource_subtype;
|
||||
int i, ret;
|
||||
|
||||
res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
|
||||
if (!res)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
res->sets = of_property_count_elems_of_size(dev_of_node(dev), of_prop,
|
||||
sizeof(u32));
|
||||
if (res->sets < 0) {
|
||||
dev_err(dev, "%s resource type ids not available\n", of_prop);
|
||||
return ERR_PTR(res->sets);
|
||||
}
|
||||
|
||||
res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc),
|
||||
GFP_KERNEL);
|
||||
if (!res->desc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < res->sets; i++) {
|
||||
ret = of_property_read_u32_index(dev_of_node(dev), of_prop, i,
|
||||
&resource_subtype);
|
||||
if (ret)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = handle->ops.rm_core_ops.get_range(handle, dev_id,
|
||||
resource_subtype,
|
||||
&res->desc[i].start,
|
||||
&res->desc[i].num);
|
||||
if (ret) {
|
||||
dev_err(dev, "dev = %d subtype %d not allocated for this host\n",
|
||||
dev_id, resource_subtype);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "dev = %d, subtype = %d, start = %d, num = %d\n",
|
||||
dev_id, resource_subtype, res->desc[i].start,
|
||||
res->desc[i].num);
|
||||
|
||||
res->desc[i].res_map =
|
||||
devm_kzalloc(dev, BITS_TO_LONGS(res->desc[i].num) *
|
||||
sizeof(*res->desc[i].res_map), GFP_KERNEL);
|
||||
if (!res->desc[i].res_map)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
raw_spin_lock_init(&res->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
@ -1784,10 +2412,33 @@ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
|
||||
/* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */
|
||||
.max_msgs = 20,
|
||||
.max_msg_size = 64,
|
||||
.rm_type_map = NULL,
|
||||
};
|
||||
|
||||
static struct ti_sci_rm_type_map ti_sci_am654_rm_type_map[] = {
|
||||
{.dev_id = 56, .type = 0x00b}, /* GIC_IRQ */
|
||||
{.dev_id = 179, .type = 0x000}, /* MAIN_NAV_UDMASS_IA0 */
|
||||
{.dev_id = 187, .type = 0x009}, /* MAIN_NAV_RA */
|
||||
{.dev_id = 188, .type = 0x006}, /* MAIN_NAV_UDMAP */
|
||||
{.dev_id = 194, .type = 0x007}, /* MCU_NAV_UDMAP */
|
||||
{.dev_id = 195, .type = 0x00a}, /* MCU_NAV_RA */
|
||||
{.dev_id = 0, .type = 0x000}, /* end of table */
|
||||
};
|
||||
|
||||
/* Description for AM654 */
|
||||
static const struct ti_sci_desc ti_sci_pmmc_am654_desc = {
|
||||
.default_host_id = 12,
|
||||
/* Conservative duration */
|
||||
.max_rx_timeout_ms = 10000,
|
||||
/* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */
|
||||
.max_msgs = 20,
|
||||
.max_msg_size = 60,
|
||||
.rm_type_map = ti_sci_am654_rm_type_map,
|
||||
};
|
||||
|
||||
static const struct of_device_id ti_sci_of_match[] = {
|
||||
{.compatible = "ti,k2g-sci", .data = &ti_sci_pmmc_k2g_desc},
|
||||
{.compatible = "ti,am654-sci", .data = &ti_sci_pmmc_am654_desc},
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_sci_of_match);
|
||||
|
@ -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",
|
||||
|
@ -14,8 +14,10 @@
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
@ -37,12 +39,6 @@ struct stm32_exti_bank {
|
||||
|
||||
#define UNDEF_REG ~0
|
||||
|
||||
enum stm32_exti_hwspinlock {
|
||||
HWSPINLOCK_UNKNOWN,
|
||||
HWSPINLOCK_NONE,
|
||||
HWSPINLOCK_READY,
|
||||
};
|
||||
|
||||
struct stm32_desc_irq {
|
||||
u32 exti;
|
||||
u32 irq_parent;
|
||||
@ -69,8 +65,6 @@ struct stm32_exti_host_data {
|
||||
void __iomem *base;
|
||||
struct stm32_exti_chip_data *chips_data;
|
||||
const struct stm32_exti_drv_data *drv_data;
|
||||
struct device_node *node;
|
||||
enum stm32_exti_hwspinlock hwlock_state;
|
||||
struct hwspinlock *hwlock;
|
||||
};
|
||||
|
||||
@ -285,49 +279,27 @@ static int stm32_exti_set_type(struct irq_data *d,
|
||||
|
||||
static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data)
|
||||
{
|
||||
struct stm32_exti_host_data *host_data = chip_data->host_data;
|
||||
struct hwspinlock *hwlock;
|
||||
int id, ret = 0, timeout = 0;
|
||||
int ret, timeout = 0;
|
||||
|
||||
/* first time, check for hwspinlock availability */
|
||||
if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) {
|
||||
id = of_hwspin_lock_get_id(host_data->node, 0);
|
||||
if (id >= 0) {
|
||||
hwlock = hwspin_lock_request_specific(id);
|
||||
if (hwlock) {
|
||||
/* found valid hwspinlock */
|
||||
host_data->hwlock_state = HWSPINLOCK_READY;
|
||||
host_data->hwlock = hwlock;
|
||||
pr_debug("%s hwspinlock = %d\n", __func__, id);
|
||||
} else {
|
||||
host_data->hwlock_state = HWSPINLOCK_NONE;
|
||||
}
|
||||
} else if (id != -EPROBE_DEFER) {
|
||||
host_data->hwlock_state = HWSPINLOCK_NONE;
|
||||
} else {
|
||||
/* hwspinlock driver shall be ready at that stage */
|
||||
ret = -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
if (!chip_data->host_data->hwlock)
|
||||
return 0;
|
||||
|
||||
if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) {
|
||||
/*
|
||||
* Use the x_raw API since we are under spin_lock protection.
|
||||
* Do not use the x_timeout API because we are under irq_disable
|
||||
* mode (see __setup_irq())
|
||||
*/
|
||||
do {
|
||||
ret = hwspin_trylock_raw(host_data->hwlock);
|
||||
if (!ret)
|
||||
return 0;
|
||||
/*
|
||||
* Use the x_raw API since we are under spin_lock protection.
|
||||
* Do not use the x_timeout API because we are under irq_disable
|
||||
* mode (see __setup_irq())
|
||||
*/
|
||||
do {
|
||||
ret = hwspin_trylock_raw(chip_data->host_data->hwlock);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
udelay(HWSPNLCK_RETRY_DELAY);
|
||||
timeout += HWSPNLCK_RETRY_DELAY;
|
||||
} while (timeout < HWSPNLCK_TIMEOUT);
|
||||
udelay(HWSPNLCK_RETRY_DELAY);
|
||||
timeout += HWSPNLCK_RETRY_DELAY;
|
||||
} while (timeout < HWSPNLCK_TIMEOUT);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
if (ret == -EBUSY)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
if (ret)
|
||||
pr_err("%s can't get hwspinlock (%d)\n", __func__, ret);
|
||||
@ -337,7 +309,7 @@ static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data)
|
||||
|
||||
static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data)
|
||||
{
|
||||
if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY))
|
||||
if (chip_data->host_data->hwlock)
|
||||
hwspin_unlock_raw(chip_data->host_data->hwlock);
|
||||
}
|
||||
|
||||
@ -586,8 +558,7 @@ static int stm32_exti_h_set_affinity(struct irq_data *d,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int stm32_exti_h_suspend(void)
|
||||
static int __maybe_unused stm32_exti_h_suspend(void)
|
||||
{
|
||||
struct stm32_exti_chip_data *chip_data;
|
||||
int i;
|
||||
@ -602,7 +573,7 @@ static int stm32_exti_h_suspend(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_exti_h_resume(void)
|
||||
static void __maybe_unused stm32_exti_h_resume(void)
|
||||
{
|
||||
struct stm32_exti_chip_data *chip_data;
|
||||
int i;
|
||||
@ -616,17 +587,22 @@ static void stm32_exti_h_resume(void)
|
||||
}
|
||||
|
||||
static struct syscore_ops stm32_exti_h_syscore_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.suspend = stm32_exti_h_suspend,
|
||||
.resume = stm32_exti_h_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void stm32_exti_h_syscore_init(void)
|
||||
static void stm32_exti_h_syscore_init(struct stm32_exti_host_data *host_data)
|
||||
{
|
||||
stm32_host_data = host_data;
|
||||
register_syscore_ops(&stm32_exti_h_syscore_ops);
|
||||
}
|
||||
#else
|
||||
static inline void stm32_exti_h_syscore_init(void) {}
|
||||
#endif
|
||||
|
||||
static void stm32_exti_h_syscore_deinit(void)
|
||||
{
|
||||
unregister_syscore_ops(&stm32_exti_h_syscore_ops);
|
||||
}
|
||||
|
||||
static struct irq_chip stm32_exti_h_chip = {
|
||||
.name = "stm32-exti-h",
|
||||
@ -683,8 +659,6 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
|
||||
return NULL;
|
||||
|
||||
host_data->drv_data = dd;
|
||||
host_data->node = node;
|
||||
host_data->hwlock_state = HWSPINLOCK_UNKNOWN;
|
||||
host_data->chips_data = kcalloc(dd->bank_nr,
|
||||
sizeof(struct stm32_exti_chip_data),
|
||||
GFP_KERNEL);
|
||||
@ -711,7 +685,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
|
||||
|
||||
static struct
|
||||
stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
||||
u32 bank_idx)
|
||||
u32 bank_idx,
|
||||
struct device_node *node)
|
||||
{
|
||||
const struct stm32_exti_bank *stm32_bank;
|
||||
struct stm32_exti_chip_data *chip_data;
|
||||
@ -731,7 +706,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
||||
writel_relaxed(0, base + stm32_bank->imr_ofst);
|
||||
writel_relaxed(0, base + stm32_bank->emr_ofst);
|
||||
|
||||
pr_info("%pOF: bank%d\n", h_data->node, bank_idx);
|
||||
pr_info("%pOF: bank%d\n", node, bank_idx);
|
||||
|
||||
return chip_data;
|
||||
}
|
||||
@ -771,7 +746,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data,
|
||||
struct stm32_exti_chip_data *chip_data;
|
||||
|
||||
stm32_bank = drv_data->exti_banks[i];
|
||||
chip_data = stm32_exti_chip_init(host_data, i);
|
||||
chip_data = stm32_exti_chip_init(host_data, i, node);
|
||||
|
||||
gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK);
|
||||
|
||||
@ -815,50 +790,130 @@ static const struct irq_domain_ops stm32_exti_h_domain_ops = {
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
static int
|
||||
__init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data,
|
||||
struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static void stm32_exti_remove_irq(void *data)
|
||||
{
|
||||
struct irq_domain *domain = data;
|
||||
|
||||
irq_domain_remove(domain);
|
||||
}
|
||||
|
||||
static int stm32_exti_remove(struct platform_device *pdev)
|
||||
{
|
||||
stm32_exti_h_syscore_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_exti_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, i;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct irq_domain *parent_domain, *domain;
|
||||
struct stm32_exti_host_data *host_data;
|
||||
int ret, i;
|
||||
const struct stm32_exti_drv_data *drv_data;
|
||||
struct resource *res;
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("interrupt-parent not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
host_data = stm32_exti_host_init(drv_data, node);
|
||||
host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL);
|
||||
if (!host_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* check for optional hwspinlock which may be not available yet */
|
||||
ret = of_hwspin_lock_get_id(np, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
/* hwspinlock framework not yet ready */
|
||||
return ret;
|
||||
|
||||
if (ret >= 0) {
|
||||
host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
|
||||
if (!host_data->hwlock) {
|
||||
dev_err(dev, "Failed to request hwspinlock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOENT) {
|
||||
/* note: ENOENT is a valid case (means 'no hwspinlock') */
|
||||
dev_err(dev, "Failed to get hwspinlock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* initialize host_data */
|
||||
drv_data = of_device_get_match_data(dev);
|
||||
if (!drv_data) {
|
||||
dev_err(dev, "no of match data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
host_data->drv_data = drv_data;
|
||||
|
||||
host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr,
|
||||
sizeof(*host_data->chips_data),
|
||||
GFP_KERNEL);
|
||||
if (!host_data->chips_data)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host_data->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(host_data->base)) {
|
||||
dev_err(dev, "Unable to map registers\n");
|
||||
return PTR_ERR(host_data->base);
|
||||
}
|
||||
|
||||
for (i = 0; i < drv_data->bank_nr; i++)
|
||||
stm32_exti_chip_init(host_data, i);
|
||||
stm32_exti_chip_init(host_data, i, np);
|
||||
|
||||
parent_domain = irq_find_host(of_irq_find_parent(np));
|
||||
if (!parent_domain) {
|
||||
dev_err(dev, "GIC interrupt-parent not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
domain = irq_domain_add_hierarchy(parent_domain, 0,
|
||||
drv_data->bank_nr * IRQS_PER_BANK,
|
||||
node, &stm32_exti_h_domain_ops,
|
||||
np, &stm32_exti_h_domain_ops,
|
||||
host_data);
|
||||
|
||||
if (!domain) {
|
||||
pr_err("%pOFn: Could not register exti domain.\n", node);
|
||||
ret = -ENOMEM;
|
||||
goto out_unmap;
|
||||
dev_err(dev, "Could not register exti domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
stm32_exti_h_syscore_init();
|
||||
ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stm32_exti_h_syscore_init(host_data);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
iounmap(host_data->base);
|
||||
kfree(host_data->chips_data);
|
||||
kfree(host_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* platform driver only for MP1 */
|
||||
static const struct of_device_id stm32_exti_ids[] = {
|
||||
{ .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_exti_ids);
|
||||
|
||||
static struct platform_driver stm32_exti_driver = {
|
||||
.probe = stm32_exti_probe,
|
||||
.remove = stm32_exti_remove,
|
||||
.driver = {
|
||||
.name = "stm32_exti",
|
||||
.of_match_table = stm32_exti_ids,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init stm32_exti_arch_init(void)
|
||||
{
|
||||
return platform_driver_register(&stm32_exti_driver);
|
||||
}
|
||||
|
||||
static void __exit stm32_exti_arch_exit(void)
|
||||
{
|
||||
return platform_driver_unregister(&stm32_exti_driver);
|
||||
}
|
||||
|
||||
arch_initcall(stm32_exti_arch_init);
|
||||
module_exit(stm32_exti_arch_exit);
|
||||
|
||||
/* no platform driver for F4 and H7 */
|
||||
static int __init stm32f4_exti_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
{
|
||||
@ -874,11 +929,3 @@ static int __init stm32h7_exti_of_init(struct device_node *np,
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init);
|
||||
|
||||
static int __init stm32mp1_exti_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init);
|
||||
|
615
drivers/irqchip/irq-ti-sci-inta.c
Normal file
615
drivers/irqchip/irq-ti-sci-inta.c
Normal file
@ -0,0 +1,615 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Texas Instruments' K3 Interrupt Aggregator irqchip driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Lokesh Vutla <lokeshvutla@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/soc/ti/ti_sci_inta_msi.h>
|
||||
#include <linux/soc/ti/ti_sci_protocol.h>
|
||||
#include <asm-generic/msi.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))
|
||||
|
||||
#define MAX_EVENTS_PER_VINT 64
|
||||
#define VINT_ENABLE_SET_OFFSET 0x0
|
||||
#define VINT_ENABLE_CLR_OFFSET 0x8
|
||||
#define VINT_STATUS_OFFSET 0x18
|
||||
|
||||
/**
|
||||
* struct ti_sci_inta_event_desc - Description of an event coming to
|
||||
* Interrupt Aggregator. This serves
|
||||
* as a mapping table for global event,
|
||||
* hwirq and vint bit.
|
||||
* @global_event: Global event number corresponding to this event
|
||||
* @hwirq: Hwirq of the incoming interrupt
|
||||
* @vint_bit: Corresponding vint bit to which this event is attached.
|
||||
*/
|
||||
struct ti_sci_inta_event_desc {
|
||||
u16 global_event;
|
||||
u32 hwirq;
|
||||
u8 vint_bit;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out
|
||||
* of Interrupt Aggregator.
|
||||
* @domain: Pointer to IRQ domain to which this vint belongs.
|
||||
* @list: List entry for the vint list
|
||||
* @event_map: Bitmap to manage the allocation of events to vint.
|
||||
* @events: Array of event descriptors assigned to this vint.
|
||||
* @parent_virq: Linux IRQ number that gets attached to parent
|
||||
* @vint_id: TISCI vint ID
|
||||
*/
|
||||
struct ti_sci_inta_vint_desc {
|
||||
struct irq_domain *domain;
|
||||
struct list_head list;
|
||||
DECLARE_BITMAP(event_map, MAX_EVENTS_PER_VINT);
|
||||
struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT];
|
||||
unsigned int parent_virq;
|
||||
u16 vint_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_sci_inta_irq_domain - Structure representing a TISCI based
|
||||
* Interrupt Aggregator IRQ domain.
|
||||
* @sci: Pointer to TISCI handle
|
||||
* @vint: TISCI resource pointer representing IA inerrupts.
|
||||
* @global_event: TISCI resource pointer representing global events.
|
||||
* @vint_list: List of the vints active in the system
|
||||
* @vint_mutex: Mutex to protect vint_list
|
||||
* @base: Base address of the memory mapped IO registers
|
||||
* @pdev: Pointer to platform device.
|
||||
*/
|
||||
struct ti_sci_inta_irq_domain {
|
||||
const struct ti_sci_handle *sci;
|
||||
struct ti_sci_resource *vint;
|
||||
struct ti_sci_resource *global_event;
|
||||
struct list_head vint_list;
|
||||
/* Mutex to protect vint list */
|
||||
struct mutex vint_mutex;
|
||||
void __iomem *base;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
#define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \
|
||||
events[i])
|
||||
|
||||
/**
|
||||
* ti_sci_inta_irq_handler() - Chained IRQ handler for the vint irqs
|
||||
* @desc: Pointer to irq_desc corresponding to the irq
|
||||
*/
|
||||
static void ti_sci_inta_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct ti_sci_inta_vint_desc *vint_desc;
|
||||
struct ti_sci_inta_irq_domain *inta;
|
||||
struct irq_domain *domain;
|
||||
unsigned int virq, bit;
|
||||
unsigned long val;
|
||||
|
||||
vint_desc = irq_desc_get_handler_data(desc);
|
||||
domain = vint_desc->domain;
|
||||
inta = domain->host_data;
|
||||
|
||||
chained_irq_enter(irq_desc_get_chip(desc), desc);
|
||||
|
||||
val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 +
|
||||
VINT_STATUS_OFFSET);
|
||||
|
||||
for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) {
|
||||
virq = irq_find_mapping(domain, vint_desc->events[bit].hwirq);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
|
||||
chained_irq_exit(irq_desc_get_chip(desc), desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator
|
||||
* @domain: IRQ domain corresponding to Interrupt Aggregator
|
||||
*
|
||||
* Return 0 if all went well else corresponding error value.
|
||||
*/
|
||||
static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain)
|
||||
{
|
||||
struct ti_sci_inta_irq_domain *inta = domain->host_data;
|
||||
struct ti_sci_inta_vint_desc *vint_desc;
|
||||
struct irq_fwspec parent_fwspec;
|
||||
unsigned int parent_virq;
|
||||
u16 vint_id;
|
||||
|
||||
vint_id = ti_sci_get_free_resource(inta->vint);
|
||||
if (vint_id == TI_SCI_RESOURCE_NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL);
|
||||
if (!vint_desc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
vint_desc->domain = domain;
|
||||
vint_desc->vint_id = vint_id;
|
||||
INIT_LIST_HEAD(&vint_desc->list);
|
||||
|
||||
parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev)));
|
||||
parent_fwspec.param_count = 2;
|
||||
parent_fwspec.param[0] = inta->pdev->id;
|
||||
parent_fwspec.param[1] = vint_desc->vint_id;
|
||||
|
||||
parent_virq = irq_create_fwspec_mapping(&parent_fwspec);
|
||||
if (parent_virq <= 0) {
|
||||
kfree(vint_desc);
|
||||
return ERR_PTR(parent_virq);
|
||||
}
|
||||
vint_desc->parent_virq = parent_virq;
|
||||
|
||||
list_add_tail(&vint_desc->list, &inta->vint_list);
|
||||
irq_set_chained_handler_and_data(vint_desc->parent_virq,
|
||||
ti_sci_inta_irq_handler, vint_desc);
|
||||
|
||||
return vint_desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_alloc_event() - Attach an event to a IA vint.
|
||||
* @vint_desc: Pointer to vint_desc to which the event gets attached
|
||||
* @free_bit: Bit inside vint to which event gets attached
|
||||
* @hwirq: hwirq of the input event
|
||||
*
|
||||
* Return event_desc pointer if all went ok else appropriate error value.
|
||||
*/
|
||||
static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_event(struct ti_sci_inta_vint_desc *vint_desc,
|
||||
u16 free_bit,
|
||||
u32 hwirq)
|
||||
{
|
||||
struct ti_sci_inta_irq_domain *inta = vint_desc->domain->host_data;
|
||||
struct ti_sci_inta_event_desc *event_desc;
|
||||
u16 dev_id, dev_index;
|
||||
int err;
|
||||
|
||||
dev_id = HWIRQ_TO_DEVID(hwirq);
|
||||
dev_index = HWIRQ_TO_IRQID(hwirq);
|
||||
|
||||
event_desc = &vint_desc->events[free_bit];
|
||||
event_desc->hwirq = hwirq;
|
||||
event_desc->vint_bit = free_bit;
|
||||
event_desc->global_event = ti_sci_get_free_resource(inta->global_event);
|
||||
if (event_desc->global_event == TI_SCI_RESOURCE_NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
err = inta->sci->ops.rm_irq_ops.set_event_map(inta->sci,
|
||||
dev_id, dev_index,
|
||||
inta->pdev->id,
|
||||
vint_desc->vint_id,
|
||||
event_desc->global_event,
|
||||
free_bit);
|
||||
if (err)
|
||||
goto free_global_event;
|
||||
|
||||
return event_desc;
|
||||
free_global_event:
|
||||
ti_sci_release_resource(inta->global_event, event_desc->global_event);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_alloc_irq() - Allocate an irq within INTA domain
|
||||
* @domain: irq_domain pointer corresponding to INTA
|
||||
* @hwirq: hwirq of the input event
|
||||
*
|
||||
* Note: Allocation happens in the following manner:
|
||||
* - Find a free bit available in any of the vints available in the list.
|
||||
* - If not found, allocate a vint from the vint pool
|
||||
* - Attach the free bit to input hwirq.
|
||||
* Return event_desc if all went ok else appropriate error value.
|
||||
*/
|
||||
static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *domain,
|
||||
u32 hwirq)
|
||||
{
|
||||
struct ti_sci_inta_irq_domain *inta = domain->host_data;
|
||||
struct ti_sci_inta_vint_desc *vint_desc = NULL;
|
||||
struct ti_sci_inta_event_desc *event_desc;
|
||||
u16 free_bit;
|
||||
|
||||
mutex_lock(&inta->vint_mutex);
|
||||
list_for_each_entry(vint_desc, &inta->vint_list, list) {
|
||||
free_bit = find_first_zero_bit(vint_desc->event_map,
|
||||
MAX_EVENTS_PER_VINT);
|
||||
if (free_bit != MAX_EVENTS_PER_VINT) {
|
||||
set_bit(free_bit, vint_desc->event_map);
|
||||
goto alloc_event;
|
||||
}
|
||||
}
|
||||
|
||||
/* No free bits available. Allocate a new vint */
|
||||
vint_desc = ti_sci_inta_alloc_parent_irq(domain);
|
||||
if (IS_ERR(vint_desc)) {
|
||||
mutex_unlock(&inta->vint_mutex);
|
||||
return ERR_PTR(PTR_ERR(vint_desc));
|
||||
}
|
||||
|
||||
free_bit = find_first_zero_bit(vint_desc->event_map,
|
||||
MAX_EVENTS_PER_VINT);
|
||||
set_bit(free_bit, vint_desc->event_map);
|
||||
|
||||
alloc_event:
|
||||
event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq);
|
||||
if (IS_ERR(event_desc))
|
||||
clear_bit(free_bit, vint_desc->event_map);
|
||||
|
||||
mutex_unlock(&inta->vint_mutex);
|
||||
return event_desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_free_parent_irq() - Free a parent irq to INTA
|
||||
* @inta: Pointer to inta domain.
|
||||
* @vint_desc: Pointer to vint_desc that needs to be freed.
|
||||
*/
|
||||
static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta,
|
||||
struct ti_sci_inta_vint_desc *vint_desc)
|
||||
{
|
||||
if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) {
|
||||
list_del(&vint_desc->list);
|
||||
ti_sci_release_resource(inta->vint, vint_desc->vint_id);
|
||||
irq_dispose_mapping(vint_desc->parent_virq);
|
||||
kfree(vint_desc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_free_irq() - Free an IRQ within INTA domain
|
||||
* @event_desc: Pointer to event_desc that needs to be freed.
|
||||
* @hwirq: Hwirq number within INTA domain that needs to be freed
|
||||
*/
|
||||
static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc,
|
||||
u32 hwirq)
|
||||
{
|
||||
struct ti_sci_inta_vint_desc *vint_desc;
|
||||
struct ti_sci_inta_irq_domain *inta;
|
||||
|
||||
vint_desc = to_vint_desc(event_desc, event_desc->vint_bit);
|
||||
inta = vint_desc->domain->host_data;
|
||||
/* free event irq */
|
||||
mutex_lock(&inta->vint_mutex);
|
||||
inta->sci->ops.rm_irq_ops.free_event_map(inta->sci,
|
||||
HWIRQ_TO_DEVID(hwirq),
|
||||
HWIRQ_TO_IRQID(hwirq),
|
||||
inta->pdev->id,
|
||||
vint_desc->vint_id,
|
||||
event_desc->global_event,
|
||||
event_desc->vint_bit);
|
||||
|
||||
clear_bit(event_desc->vint_bit, vint_desc->event_map);
|
||||
ti_sci_release_resource(inta->global_event, event_desc->global_event);
|
||||
event_desc->global_event = TI_SCI_RESOURCE_NULL;
|
||||
event_desc->hwirq = 0;
|
||||
|
||||
ti_sci_inta_free_parent_irq(inta, vint_desc);
|
||||
mutex_unlock(&inta->vint_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_request_resources() - Allocate resources for input irq
|
||||
* @data: Pointer to corresponding irq_data
|
||||
*
|
||||
* Note: This is the core api where the actual allocation happens for input
|
||||
* hwirq. This allocation involves creating a parent irq for vint.
|
||||
* If this is done in irq_domain_ops.alloc() then a deadlock is reached
|
||||
* for allocation. So this allocation is being done in request_resources()
|
||||
*
|
||||
* Return: 0 if all went well else corresponding error.
|
||||
*/
|
||||
static int ti_sci_inta_request_resources(struct irq_data *data)
|
||||
{
|
||||
struct ti_sci_inta_event_desc *event_desc;
|
||||
|
||||
event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq);
|
||||
if (IS_ERR(event_desc))
|
||||
return PTR_ERR(event_desc);
|
||||
|
||||
data->chip_data = event_desc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_release_resources - Release resources for input irq
|
||||
* @data: Pointer to corresponding irq_data
|
||||
*
|
||||
* Note: Corresponding to request_resources(), all the unmapping and deletion
|
||||
* of parent vint irqs happens in this api.
|
||||
*/
|
||||
static void ti_sci_inta_release_resources(struct irq_data *data)
|
||||
{
|
||||
struct ti_sci_inta_event_desc *event_desc;
|
||||
|
||||
event_desc = irq_data_get_irq_chip_data(data);
|
||||
ti_sci_inta_free_irq(event_desc, data->hwirq);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_manage_event() - Control the event based on the offset
|
||||
* @data: Pointer to corresponding irq_data
|
||||
* @offset: register offset using which event is controlled.
|
||||
*/
|
||||
static void ti_sci_inta_manage_event(struct irq_data *data, u32 offset)
|
||||
{
|
||||
struct ti_sci_inta_event_desc *event_desc;
|
||||
struct ti_sci_inta_vint_desc *vint_desc;
|
||||
struct ti_sci_inta_irq_domain *inta;
|
||||
|
||||
event_desc = irq_data_get_irq_chip_data(data);
|
||||
vint_desc = to_vint_desc(event_desc, event_desc->vint_bit);
|
||||
inta = data->domain->host_data;
|
||||
|
||||
writeq_relaxed(BIT(event_desc->vint_bit),
|
||||
inta->base + vint_desc->vint_id * 0x1000 + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_mask_irq() - Mask an event
|
||||
* @data: Pointer to corresponding irq_data
|
||||
*/
|
||||
static void ti_sci_inta_mask_irq(struct irq_data *data)
|
||||
{
|
||||
ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_unmask_irq() - Unmask an event
|
||||
* @data: Pointer to corresponding irq_data
|
||||
*/
|
||||
static void ti_sci_inta_unmask_irq(struct irq_data *data)
|
||||
{
|
||||
ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_ack_irq() - Ack an event
|
||||
* @data: Pointer to corresponding irq_data
|
||||
*/
|
||||
static void ti_sci_inta_ack_irq(struct irq_data *data)
|
||||
{
|
||||
/*
|
||||
* Do not clear the event if hardware is capable of sending
|
||||
* a down event.
|
||||
*/
|
||||
if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH)
|
||||
ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET);
|
||||
}
|
||||
|
||||
static int ti_sci_inta_set_affinity(struct irq_data *d,
|
||||
const struct cpumask *mask_val, bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_set_type() - Update the trigger type of the irq.
|
||||
* @data: Pointer to corresponding irq_data
|
||||
* @type: Trigger type as specified by user
|
||||
*
|
||||
* Note: This updates the handle_irq callback for level msi.
|
||||
*
|
||||
* Return 0 if all went well else appropriate error.
|
||||
*/
|
||||
static int ti_sci_inta_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
/*
|
||||
* .alloc default sets handle_edge_irq. But if the user specifies
|
||||
* that IRQ is level MSI, then update the handle to handle_level_irq
|
||||
*/
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
return 0;
|
||||
case IRQF_TRIGGER_RISING:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct irq_chip ti_sci_inta_irq_chip = {
|
||||
.name = "INTA",
|
||||
.irq_ack = ti_sci_inta_ack_irq,
|
||||
.irq_mask = ti_sci_inta_mask_irq,
|
||||
.irq_set_type = ti_sci_inta_set_type,
|
||||
.irq_unmask = ti_sci_inta_unmask_irq,
|
||||
.irq_set_affinity = ti_sci_inta_set_affinity,
|
||||
.irq_request_resources = ti_sci_inta_request_resources,
|
||||
.irq_release_resources = ti_sci_inta_release_resources,
|
||||
};
|
||||
|
||||
/**
|
||||
* ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain
|
||||
* @domain: Domain to which the irqs belong
|
||||
* @virq: base linux virtual IRQ to be freed.
|
||||
* @nr_irqs: Number of continuous irqs to be freed
|
||||
*/
|
||||
static void ti_sci_inta_irq_domain_free(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *data = irq_domain_get_irq_data(domain, virq);
|
||||
|
||||
irq_domain_reset_irq_data(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs
|
||||
* @domain: Point to the interrupt aggregator IRQ domain
|
||||
* @virq: Corresponding Linux virtual IRQ number
|
||||
* @nr_irqs: Continuous irqs to be allocated
|
||||
* @data: Pointer to firmware specifier
|
||||
*
|
||||
* No actual allocation happens here.
|
||||
*
|
||||
* Return 0 if all went well else appropriate error value.
|
||||
*/
|
||||
static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs,
|
||||
void *data)
|
||||
{
|
||||
msi_alloc_info_t *arg = data;
|
||||
|
||||
irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip,
|
||||
NULL, handle_edge_irq, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = {
|
||||
.free = ti_sci_inta_irq_domain_free,
|
||||
.alloc = ti_sci_inta_irq_domain_alloc,
|
||||
};
|
||||
|
||||
static struct irq_chip ti_sci_inta_msi_irq_chip = {
|
||||
.name = "MSI-INTA",
|
||||
.flags = IRQCHIP_SUPPORTS_LEVEL_MSI,
|
||||
};
|
||||
|
||||
static void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg,
|
||||
struct msi_desc *desc)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(desc->dev);
|
||||
|
||||
arg->desc = desc;
|
||||
arg->hwirq = TO_HWIRQ(pdev->id, desc->inta.dev_index);
|
||||
}
|
||||
|
||||
static struct msi_domain_ops ti_sci_inta_msi_ops = {
|
||||
.set_desc = ti_sci_inta_msi_set_desc,
|
||||
};
|
||||
|
||||
static struct msi_domain_info ti_sci_inta_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_LEVEL_CAPABLE),
|
||||
.ops = &ti_sci_inta_msi_ops,
|
||||
.chip = &ti_sci_inta_msi_irq_chip,
|
||||
};
|
||||
|
||||
static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct irq_domain *parent_domain, *domain, *msi_domain;
|
||||
struct device_node *parent_node, *node;
|
||||
struct ti_sci_inta_irq_domain *inta;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
node = dev_of_node(dev);
|
||||
parent_node = of_irq_find_parent(node);
|
||||
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)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL);
|
||||
if (!inta)
|
||||
return -ENOMEM;
|
||||
|
||||
inta->pdev = pdev;
|
||||
inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
|
||||
if (IS_ERR(inta->sci)) {
|
||||
ret = PTR_ERR(inta->sci);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "ti,sci read fail %d\n", ret);
|
||||
inta->sci = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &pdev->id);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing 'ti,sci-dev-id' property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id,
|
||||
"ti,sci-rm-range-vint");
|
||||
if (IS_ERR(inta->vint)) {
|
||||
dev_err(dev, "VINT resource allocation failed\n");
|
||||
return PTR_ERR(inta->vint);
|
||||
}
|
||||
|
||||
inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id,
|
||||
"ti,sci-rm-range-global-event");
|
||||
if (IS_ERR(inta->global_event)) {
|
||||
dev_err(dev, "Global event resource allocation failed\n");
|
||||
return PTR_ERR(inta->global_event);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
inta->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(inta->base))
|
||||
return -ENODEV;
|
||||
|
||||
domain = irq_domain_add_linear(dev_of_node(dev),
|
||||
ti_sci_get_num_resources(inta->vint),
|
||||
&ti_sci_inta_irq_domain_ops, inta);
|
||||
if (!domain) {
|
||||
dev_err(dev, "Failed to allocate IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node),
|
||||
&ti_sci_inta_msi_domain_info,
|
||||
domain);
|
||||
if (!msi_domain) {
|
||||
irq_domain_remove(domain);
|
||||
dev_err(dev, "Failed to allocate msi domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&inta->vint_list);
|
||||
mutex_init(&inta->vint_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = {
|
||||
{ .compatible = "ti,sci-inta", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match);
|
||||
|
||||
static struct platform_driver ti_sci_inta_irq_domain_driver = {
|
||||
.probe = ti_sci_inta_irq_domain_probe,
|
||||
.driver = {
|
||||
.name = "ti-sci-inta",
|
||||
.of_match_table = ti_sci_inta_irq_domain_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_sci_inta_irq_domain_driver);
|
||||
|
||||
MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
|
||||
MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol");
|
||||
MODULE_LICENSE("GPL v2");
|
275
drivers/irqchip/irq-ti-sci-intr.c
Normal file
275
drivers/irqchip/irq-ti-sci-intr.c
Normal file
@ -0,0 +1,275 @@
|
||||
// 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
|
||||
|
146
drivers/soc/ti/ti_sci_inta_msi.c
Normal file
146
drivers/soc/ti/ti_sci_inta_msi.c
Normal file
@ -0,0 +1,146 @@
|
||||
// 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) \
|
||||
|
23
include/linux/soc/ti/ti_sci_inta_msi.h
Normal file
23
include/linux/soc/ti/ti_sci_inta_msi.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* 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
|
||||
|
Loading…
Reference in New Issue
Block a user