2018-11-06 08:42:48

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 00/13] Add support for TISCI irqchip drivers

TISCI abstracts the handling of IRQ routes where interrupt sources
are not directly connected to host interrupt controller. This series
adds support for:
- TISCI commands needed for IRQ configuration
- Interrupt Router(INTR) and Interrupt Aggregator(INTA) drivers

More information on TISCI IRQ management can be found here[1].
Complete TISCI resource management information can be found here[2].
AM65x SoC related TISCI information can be found here[3].
INTR and INTA related information can be found in TRM[4].

[1] http://downloads.ti.com/tisci/esd/latest/2_tisci_msgs/rm/rm_irq.html
[2] http://downloads.ti.com/tisci/esd/latest/2_tisci_msgs/index.html#resource-management-rm
[3] http://downloads.ti.com/tisci/esd/latest/5_soc_doc/index.html#am6-soc-family
[4] http://www.ti.com/lit/pdf/spruid7

Changes since v2:
- Added MSI support for allocating INTA interrupts
- Each patch contains respective changes.

Changes since v1:
- Consolidated both TISCI and irqchip drivers as suggested by Marc.
- Each patch contains respective changes.


Grygorii Strashko (1):
firmware: ti_sci: Add support to get TISCI handle using of_phandle

Lokesh Vutla (11):
firmware: ti_sci: Add support for RM core ops
firmware: ti_sci: Add support for IRQ management
firmware: ti_sci: Add helper apis to manage resources
dt-bindings: irqchip: Introduce TISCI Interrupt router bindings
irqchip: ti-sci-intr: Add support for Interrupt Router driver
genirq/msi: Add support for allocating single MSI for a device
genirq/msi: Add support for .msi_unprepare callback
soc: ti: Add MSI domain support for K3 Interrupt Aggregator
dt-bindings: irqchip: Introduce TISCI Interrupt Aggregator bindings
irqchip: ti-sci-inta: Add support for Interrupt Aggregator driver
soc: ti: am6: Enable interrupt controller drivers

Peter Ujfalusi (1):
firmware: ti_sci: Add RM mapping table for am654

.../bindings/arm/keystone/ti,sci.txt | 3 +-
.../interrupt-controller/ti,sci-inta.txt | 74 ++
.../interrupt-controller/ti,sci-intr.txt | 84 ++
MAINTAINERS | 4 +
drivers/firmware/ti_sci.c | 850 ++++++++++++++++++
drivers/firmware/ti_sci.h | 102 +++
drivers/irqchip/Kconfig | 23 +
drivers/irqchip/Makefile | 2 +
drivers/irqchip/irq-ti-sci-inta.c | 543 +++++++++++
drivers/irqchip/irq-ti-sci-intr.c | 310 +++++++
drivers/soc/ti/Kconfig | 9 +
drivers/soc/ti/Makefile | 1 +
drivers/soc/ti/k3_inta_msi.c | 163 ++++
include/linux/irqdomain.h | 1 +
include/linux/msi.h | 12 +
include/linux/soc/ti/k3_inta_msi.h | 21 +
include/linux/soc/ti/ti_sci_protocol.h | 169 ++++
kernel/irq/msi.c | 72 +-
18 files changed, 2420 insertions(+), 23 deletions(-)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
create mode 100644 drivers/irqchip/irq-ti-sci-inta.c
create mode 100644 drivers/irqchip/irq-ti-sci-intr.c
create mode 100644 drivers/soc/ti/k3_inta_msi.c
create mode 100644 include/linux/soc/ti/k3_inta_msi.h

--
2.19.1



2018-11-06 08:42:57

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 04/13] firmware: ti_sci: Add RM mapping table for am654

From: Peter Ujfalusi <[email protected]>

Add the resource mapping table for AM654 SoC as defined
in http://downloads.ti.com/tisci/esd/latest/5_soc_doc/am6x/resasg_types.html
Introduce a new compatible for AM654 "ti,am654-sci" for using
this resource map table.

Reviewed-by: Rob Herring <[email protected]>
Signed-off-by: Peter Ujfalusi <[email protected]>
Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- Added Reviewed-by tag

.../bindings/arm/keystone/ti,sci.txt | 3 ++-
drivers/firmware/ti_sci.c | 23 +++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt b/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
index b56a02c10ae6..6f0cd31c1520 100644
--- a/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
+++ b/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
@@ -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
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index 987bfb29475c..c2f0815edab6 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -2500,10 +2500,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);
--
2.19.1


2018-11-06 08:43:06

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 02/13] firmware: ti_sci: Add support for RM core ops

TISCI provides support for getting the resources(IRQ, RING etc..)
assigned to a specific device. These resources can be handled by
the client and in turn sends TISCI cmd to configure the resources.

It is very important that client should keep track on usage of these
resources.

Add support for TISCI commands to get resource ranges.

Signed-off-by: Lokesh Vutla <[email protected]>
Signed-off-by: Peter Ujfalusi <[email protected]>
---
Changes since v2:
- None

drivers/firmware/ti_sci.c | 170 +++++++++++++++++++++++++
drivers/firmware/ti_sci.h | 42 ++++++
include/linux/soc/ti/ti_sci_protocol.h | 27 ++++
3 files changed, 239 insertions(+)

diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index f0cafa8a2ee9..a2a099b8f62a 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -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;
};

/**
@@ -1617,6 +1635,153 @@ 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_setup_ops() - Setup the operations structures
* @info: pointer to TISCI pointer
@@ -1627,6 +1792,7 @@ 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;

core_ops->reboot_device = ti_sci_cmd_core_reboot;

@@ -1657,6 +1823,10 @@ 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;
}

/**
diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h
index 12bf316b68df..a043c4762791 100644
--- a/drivers/firmware/ti_sci.h
+++ b/drivers/firmware/ti_sci.h
@@ -35,6 +35,9 @@
#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
+
/**
* 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 +464,43 @@ 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;
+
#endif /* __TI_SCI_H */
diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h
index 515587e9d373..0c92a922db6a 100644
--- a/include/linux/soc/ti/ti_sci_protocol.h
+++ b/include/linux/soc/ti/ti_sci_protocol.h
@@ -192,15 +192,42 @@ 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_ops - Function support for TI SCI
* @dev_ops: Device specific operations
* @clk_ops: Clock specific operations
+ * @rm_core_ops: Resource management core 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;
};

/**
--
2.19.1


2018-11-06 08:43:09

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 03/13] firmware: ti_sci: Add support for IRQ management

TISCI abstracts the handling of IRQ routes where interrupt sources
are not directly connected to interrupt controller. Add support for
the set of TISCI commands for requesting and releasing IRQs.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v3:
- None

drivers/firmware/ti_sci.c | 446 +++++++++++++++++++++++++
drivers/firmware/ti_sci.h | 60 ++++
include/linux/soc/ti/ti_sci_protocol.h | 77 +++++
3 files changed, 583 insertions(+)

diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index a2a099b8f62a..987bfb29475c 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -1782,6 +1782,439 @@ int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle,
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
+ * @dt_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
+ * @dt_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
+ * @dt_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_direct_irq() - Configure a non-event based direct 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
+ * @dt_host_irq: IRQ number of the destination device
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_set_direct_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_irq() - Configure an event based 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
+ * @dt_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
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_set_event_irq(const struct ti_sci_handle *handle,
+ 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)
+{
+ u32 valid_params = MSG_FLAG_DST_ID_VALID |
+ MSG_FLAG_DST_HOST_IRQ_VALID | 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, dst_id,
+ dst_host_irq, ia_id, vint, global_event,
+ vint_status_bit, 0);
+}
+
+/**
+ * ti_sci_cmd_set_direct_irq_from_shost() - Configure a non-event based direct
+ * irq route between the source and
+ * destination belonging to a
+ * specified host.
+ * @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
+ * @dt_host_irq: IRQ number of the destination device
+ * @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_cmd_set_direct_irq_from_shost(const struct ti_sci_handle *handle,
+ u16 src_id, u16 src_index, u16 dst_id,
+ u16 dst_host_irq, u8 s_host)
+{
+ u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID |
+ MSG_FLAG_SHOST_VALID;
+
+ return ti_sci_set_irq(handle, valid_params, src_id, src_index,
+ dst_id, dst_host_irq, 0, 0, 0, 0, s_host);
+}
+
+/**
+ * ti_sci_cmd_set_event_irq_from_shost() - Configure an event based irq
+ * route between the source and
+ * destination belonging to a
+ * specified host.
+ * @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
+ * @dt_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_cmd_set_event_irq_from_shost(const struct ti_sci_handle *handle,
+ 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)
+{
+ u32 valid_params = MSG_FLAG_DST_ID_VALID |
+ MSG_FLAG_DST_HOST_IRQ_VALID | MSG_FLAG_IA_ID_VALID |
+ MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID |
+ MSG_FLAG_VINT_STS_BIT_VALID | MSG_FLAG_SHOST_VALID;
+
+ return ti_sci_set_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_cmd_set_event_irq_to_poll() - Configure an event based irq
+ * in polling mode
+ * @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
+ * @dt_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_cmd_set_event_irq_to_poll(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_direct_irq() - Free a non-event based direct 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
+ * @dt_host_irq: IRQ number of the destination device
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_free_direct_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_irq() - Free an event based 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
+ * @dt_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
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+static int ti_sci_cmd_free_event_irq(const struct ti_sci_handle *handle,
+ 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)
+{
+ u32 valid_params = MSG_FLAG_DST_ID_VALID |
+ MSG_FLAG_DST_HOST_IRQ_VALID | 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,
+ dst_id, dst_host_irq, ia_id, vint,
+ global_event, vint_status_bit, 0);
+}
+
+/**
+ * ti_sci_cmd_free_direct_irq_from_shost() - Free a non-event based direct irq
+ * route between the source and
+ * destination belonging to a
+ * specified host.
+ * @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
+ * @dt_host_irq: IRQ number of the destination device
+ * @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_cmd_free_direct_irq_from_shost(const struct ti_sci_handle *handle,
+ u16 src_id, u16 src_index, u16 dst_id,
+ u16 dst_host_irq, u8 s_host)
+{
+ u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID |
+ MSG_FLAG_SHOST_VALID;
+
+ return ti_sci_free_irq(handle, valid_params, src_id, src_index,
+ dst_id, dst_host_irq, 0, 0, 0, 0, s_host);
+}
+
+/**
+ * ti_sci_cmd_free_event_irq_from_shost() - Free an event based irq
+ * route between the source and
+ * destination belonging to a
+ * specified host.
+ * @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
+ * @dt_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_cmd_free_event_irq_from_shost(const struct ti_sci_handle *handle,
+ 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)
+{
+ u32 valid_params = MSG_FLAG_DST_ID_VALID |
+ MSG_FLAG_DST_HOST_IRQ_VALID | MSG_FLAG_IA_ID_VALID |
+ MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID |
+ MSG_FLAG_VINT_STS_BIT_VALID | MSG_FLAG_SHOST_VALID;
+
+ return ti_sci_free_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_cmd_free_event_irq_to_poll() - Free an event based irq
+ * in polling mode
+ * @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
+ * @dt_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_cmd_free_event_irq_to_poll(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
@@ -1793,6 +2226,7 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
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;

@@ -1827,6 +2261,18 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
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_direct_irq = ti_sci_cmd_set_direct_irq;
+ iops->set_event_irq = ti_sci_cmd_set_event_irq;
+ iops->set_direct_irq_from_shost = ti_sci_cmd_set_direct_irq_from_shost;
+ iops->set_event_irq_from_shost = ti_sci_cmd_set_event_irq_from_shost;
+ iops->set_event_irq_to_poll = ti_sci_cmd_set_event_irq_to_poll;
+ iops->free_direct_irq = ti_sci_cmd_free_direct_irq;
+ iops->free_event_irq = ti_sci_cmd_free_event_irq;
+ iops->free_direct_irq_from_shost =
+ ti_sci_cmd_free_direct_irq_from_shost;
+ iops->free_event_irq_from_shost = ti_sci_cmd_free_event_irq_from_shost;
+ iops->free_event_irq_to_poll = ti_sci_cmd_free_event_irq_to_poll;
}

/**
diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h
index a043c4762791..4983827151bf 100644
--- a/drivers/firmware/ti_sci.h
+++ b/drivers/firmware/ti_sci.h
@@ -38,6 +38,10 @@
/* 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
@@ -503,4 +507,60 @@ struct ti_sci_msg_resp_get_resource_range {
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 */
diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h
index 0c92a922db6a..6d17580839dc 100644
--- a/include/linux/soc/ti/ti_sci_protocol.h
+++ b/include/linux/soc/ti/ti_sci_protocol.h
@@ -217,17 +217,94 @@ struct ti_sci_rm_core_ops {
u16 *range_start, u16 *range_num);
};

+/**
+ * struct ti_sci_rm_irq_ops: IRQ management operations
+ * @set_direct_irq: Set Non-event Sourced direct irq to destination
+ * host(same host as ti sci interface id).
+ * @set_event_irq: Set Event based peripheral irq to destination
+ * host(same host as ti sci interface id).
+ * @set_direct_irq_from_shost: Set Non-event Sourced direct irq to a
+ * specified destination host.
+ * @set_event_irq_from_shost: Set Event based peripheral irq to a
+ * specified destination host.
+ * @set_event_irq_to_poll: Set Event based peripheral irq to polling mode.
+ * vint_status_bit is used for polling.
+ * @free_direct_irq: Free a non-event sourced direct irq to
+ * destination host(same as ti sci interface id)
+ * @free_event_irq: Free an event based peripheral irq to
+ * destination host(same as ti sci interface id)
+ * @free_direct_irq_from_shost: Free non-event based direct irq from a
+ * specified destination host.
+ * @free_event_irq_from_shost: Free event based peripheral irq from a
+ * specified destination host.
+ * @free_event_irq_to_poll: Free an event based peripheral irq that is
+ * configured in polling mode.
+ *
+ * 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
+ * - 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.
+ */
+struct ti_sci_rm_irq_ops {
+ int (*set_direct_irq)(const struct ti_sci_handle *handle, u16 src_id,
+ u16 src_index, u16 dst_id, u16 dst_host_irq);
+ int (*set_event_irq)(const struct ti_sci_handle *handle, 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);
+ int (*set_direct_irq_from_shost)(const struct ti_sci_handle *handle,
+ u16 src_id, u16 src_index, u16 dst_id,
+ u16 dst_host_irq, u8 s_host);
+ int (*set_event_irq_from_shost)(const struct ti_sci_handle *handle,
+ 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);
+ int (*set_event_irq_to_poll)(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_direct_irq)(const struct ti_sci_handle *handle, u16 src_id,
+ u16 src_index, u16 dst_id, u16 dst_host_irq);
+ int (*free_event_irq)(const struct ti_sci_handle *handle, 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);
+ int (*free_direct_irq_from_shost)(const struct ti_sci_handle *handle,
+ u16 src_id, u16 src_index, u16 dst_id,
+ u16 dst_host_irq, u8 s_host);
+ int (*free_event_irq_from_shost)(const struct ti_sci_handle *handle,
+ 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);
+ int (*free_event_irq_to_poll)(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;
};

/**
--
2.19.1


2018-11-06 08:43:29

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 06/13] dt-bindings: irqchip: Introduce TISCI Interrupt router bindings

Add the DT binding documentation for Interrupt router driver.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- Dropped interrupt-parent from reqired properties description
- Updated the interrupt cells to 4.

.../interrupt-controller/ti,sci-intr.txt | 84 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
new file mode 100644
index 000000000000..06e69f8c812c
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
@@ -0,0 +1,84 @@
+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. There is one register per output (MUXCNTL_N) that
+controls the selection.
+
+
+ Interrupt Router
+ +----------------------+
+ | Inputs Outputs |
+ +-------+ | +------+ |
+ | GPIO |----------->| | irq0 | | Host IRQ
+ +-------+ | +------+ | controller
+ | . +-----+ | +-------+
+ +-------+ | . | 0 | |----->| IRQ |
+ | INTA |----------->| . +-----+ | +-------+
+ +-------+ | . . |
+ | +------+ . |
+ | | irqM | +-----+ |
+ | +------+ | N | |
+ | +-----+ |
+ +----------------------+
+
+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:
+----------------------------
+- compatible: Must be "ti,sci-intr".
+- 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 4.
+ First cell should contain the TISCI device ID of source
+ Second cell should contain the interrupt source offset
+ within the device
+ Third cell specifies the trigger type as defined
+ in interrupts.txt in this directory.
+ Fourth cell should be 1 if the irq is coming from
+ interrupt aggregator else 0.
+- 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: TISCI subtype id representing the host irqs assigned
+ to this interrupt router.
+
+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-controller@1 {
+ compatible = "ti,sci-intr";
+ interrupt-controller;
+ interrupt-parent = <&gic>;
+ #interrupt-cells = <4>;
+ 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 IRQ_TYPE_EDGE_RISING 0>,
+ <57 257 IRQ_TYPE_EDGE_RISING 0>,
+ <57 258 IRQ_TYPE_EDGE_RISING 0>,
+ <57 259 IRQ_TYPE_EDGE_RISING 0>,
+ <57 260 IRQ_TYPE_EDGE_RISING 0>,
+ <57 261 IRQ_TYPE_EDGE_RISING 0>;
+ ...
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 88b28f6dec45..0e3aa3386287 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14739,6 +14739,7 @@ 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

THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <[email protected]>
--
2.19.1


2018-11-06 08:43:33

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 01/13] firmware: ti_sci: Add support to get TISCI handle using of_phandle

From: Grygorii Strashko <[email protected]>

TISCI has been updated to have support for Resource management(likes
interrupts etc..). And there can be multiple device instances of a
resource type in a SoC. So every driver corresponding to a resource type
should get a TISCI handle so that it can make TISCI calls. And each
DT node corresponding to a device should exist under its corresponding
bus node as per the SoC architecture.

But existing apis in TISCI library assumes that all TISCI users are
child nodes of TISCI. Which is not true in the above case. So introduce
(devm_)ti_sci_get_by_phandle() apis that can be used by TISCI users
to get TISCI handle using of phandle property.

Signed-off-by: Grygorii Strashko <[email protected]>
Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- None

drivers/firmware/ti_sci.c | 83 ++++++++++++++++++++++++++
include/linux/soc/ti/ti_sci_protocol.h | 17 ++++++
2 files changed, 100 insertions(+)

diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index 69ed1464175c..f0cafa8a2ee9 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -1781,6 +1781,89 @@ 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
+ * @propname: 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
+ * @propname: 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);
+
static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
void *cmd)
{
diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h
index 18435e5c6364..515587e9d373 100644
--- a/include/linux/soc/ti/ti_sci_protocol.h
+++ b/include/linux/soc/ti/ti_sci_protocol.h
@@ -217,6 +217,10 @@ struct ti_sci_handle {
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);

#else /* CONFIG_TI_SCI_PROTOCOL */

@@ -236,6 +240,19 @@ 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);
+}
#endif /* CONFIG_TI_SCI_PROTOCOL */

#endif /* __TISCI_PROTOCOL_H */
--
2.19.1


2018-11-06 08:43:45

by Lokesh Vutla

[permalink] [raw]
Subject: [RFC PATCH v3 08/13] genirq/msi: Add support for allocating single MSI for a device

Previously all MSIs for a device are allocated in one go
by calling msi_domain_alloc_irq() from a bus layer. This might
not be the case when a device is trying to allocate interrupts
dynamically based on a request to it.

So introduce msi_domain_alloc/free_irq() apis to allocate a single
msi. prepare and activate operations to be handled by bus layer
calling msi_domain_alloc/free_irq() apis.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- New patch

include/linux/msi.h | 3 +++
kernel/irq/msi.c | 62 +++++++++++++++++++++++++++++----------------
2 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 0e9c50052ff3..021f789226b5 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -299,8 +299,11 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
struct msi_domain_info *info,
struct irq_domain *parent);
+int msi_domain_alloc_irq(struct irq_domain *domain, struct device *dev,
+ struct msi_desc *desc, msi_alloc_info_t *arg);
int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
int nvec);
+void msi_domain_free_irq(struct msi_desc *desc);
void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);

diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 4ca2fd46645d..e37238317fab 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -387,6 +387,35 @@ static bool msi_check_reservation_mode(struct irq_domain *domain,
return desc->msi_attrib.is_msix || desc->msi_attrib.maskbit;
}

+int msi_domain_alloc_irq(struct irq_domain *domain, struct device *dev,
+ struct msi_desc *desc, msi_alloc_info_t *arg)
+{
+ struct msi_domain_info *info = domain->host_data;
+ struct msi_domain_ops *ops = info->ops;
+ int i, ret, virq;
+
+ ops->set_desc(arg, desc);
+
+ virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
+ dev_to_node(dev), arg, false,
+ desc->affinity);
+ if (virq < 0) {
+ ret = -ENOSPC;
+ if (ops->handle_error)
+ ret = ops->handle_error(domain, desc, ret);
+ if (ops->msi_finish)
+ ops->msi_finish(arg, ret);
+ return ret;
+ }
+
+ for (i = 0; i < desc->nvec_used; i++) {
+ irq_set_msi_desc_off(virq, i, desc);
+ irq_debugfs_copy_devname(virq + i, dev);
+ }
+
+ return 0;
+}
+
/**
* msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
* @domain: The domain to allocate from
@@ -404,7 +433,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
struct irq_data *irq_data;
struct msi_desc *desc;
msi_alloc_info_t arg;
- int i, ret, virq;
+ int ret, virq;
bool can_reserve;

ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
@@ -412,24 +441,9 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
return ret;

for_each_msi_entry(desc, dev) {
- ops->set_desc(&arg, desc);
-
- virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
- dev_to_node(dev), &arg, false,
- desc->affinity);
- if (virq < 0) {
- ret = -ENOSPC;
- if (ops->handle_error)
- ret = ops->handle_error(domain, desc, ret);
- if (ops->msi_finish)
- ops->msi_finish(&arg, ret);
+ ret = msi_domain_alloc_irq(domain, dev, desc, &arg);
+ if (ret)
return ret;
- }
-
- for (i = 0; i < desc->nvec_used; i++) {
- irq_set_msi_desc_off(virq, i, desc);
- irq_debugfs_copy_devname(virq + i, dev);
- }
}

if (ops->msi_finish)
@@ -487,6 +501,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
return ret;
}

+void msi_domain_free_irq(struct msi_desc *desc)
+{
+ irq_domain_free_irqs(desc->irq, desc->nvec_used);
+ desc->irq = 0;
+}
+
/**
* msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
* @domain: The domain to managing the interrupts
@@ -503,10 +523,8 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
* enough that there is no IRQ associated to this
* entry. If that's the case, don't do anything.
*/
- if (desc->irq) {
- irq_domain_free_irqs(desc->irq, desc->nvec_used);
- desc->irq = 0;
- }
+ if (desc->irq)
+ msi_domain_free_irq(desc);
}
}

--
2.19.1


2018-11-06 08:43:51

by Lokesh Vutla

[permalink] [raw]
Subject: [RFC PATCH v3 10/13] soc: ti: Add MSI domain support for K3 Interrupt Aggregator

With the system coprocessor managing the range allocation of the
inputs to Interrupt Aggregator, it is difficult to represent
the device IRQs from DT.

The suggestion is to use MSI in such cases where devices wants
to allocate and group interrupts dynamically.

Create a MSI domain bus layer that allocates and frees MSIs for
a device.

APIs that are implemented are as follows:
- inta_msi_create_irq_domain() that creates a MSI domain
- inta_msi_domain_alloc_group_irqs() that creates MSIs for the
specified device and source indexes. All these are expected to
be grouped by the parent interrupt controller to MSI domain.
- inta_msi_domain_free_group_irqs() frees the grouped irqs.

Signed-off-by: Lokesh Vutla <[email protected]>
---
- May be the same functionaly can be included in platform msi. But I would
like to get a feedback on the approach.

Changes since v1:
- New patch

drivers/soc/ti/Kconfig | 6 ++
drivers/soc/ti/Makefile | 1 +
drivers/soc/ti/k3_inta_msi.c | 163 +++++++++++++++++++++++++++++
include/linux/irqdomain.h | 1 +
include/linux/msi.h | 6 ++
include/linux/soc/ti/k3_inta_msi.h | 21 ++++
6 files changed, 198 insertions(+)
create mode 100644 drivers/soc/ti/k3_inta_msi.c
create mode 100644 include/linux/soc/ti/k3_inta_msi.h

diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index be4570baad96..7640490c2a6a 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -73,4 +73,10 @@ config TI_SCI_PM_DOMAINS
called ti_sci_pm_domains. Note this is needed early in boot before
rootfs may be available.

+config K3_INTA_MSI_DOMAIN
+ bool
+ select GENERIC_MSI_IRQ_DOMAIN
+ help
+ Driver to enable Interrupt Aggregator specific MSI Domain.
+
endif # SOC_TI
diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
index a22edc0b258a..152b195273ee 100644
--- a/drivers/soc/ti/Makefile
+++ b/drivers/soc/ti/Makefile
@@ -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_K3_INTA_MSI_DOMAIN) += k3_inta_msi.o
diff --git a/drivers/soc/ti/k3_inta_msi.c b/drivers/soc/ti/k3_inta_msi.c
new file mode 100644
index 000000000000..0236d836d7f6
--- /dev/null
+++ b/drivers/soc/ti/k3_inta_msi.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments' K3 Interrupt Aggregator driver MSI support
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla <[email protected]>
+ */
+
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+#include <linux/soc/ti/k3_inta_msi.h>
+
+#ifdef GENERIC_MSI_DOMAIN_OPS
+
+#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 TO_HWIRQ(id, index) (((id & TI_SCI_DEV_ID_MASK) << \
+ TI_SCI_DEV_ID_SHIFT) | \
+ (index & TI_SCI_IRQ_ID_MASK))
+static void inta_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+{
+ arg->desc = desc;
+ arg->hwirq = TO_HWIRQ(desc->inta.dev_id, desc->inta.msi_index);
+}
+#else
+#define inta_msi_set_desc NULL
+#endif
+
+static void inta_msi_update_dom_ops(struct msi_domain_info *info)
+{
+ struct msi_domain_ops *ops = info->ops;
+
+ BUG_ON(!ops);
+
+ if (ops->set_desc == NULL)
+ ops->set_desc = inta_msi_set_desc;
+}
+
+static void inta_msi_update_chip_ops(struct msi_domain_info *info)
+{
+ struct irq_chip *chip = info->chip;
+
+ BUG_ON(!chip);
+ if (!chip->irq_mask)
+ chip->irq_mask = irq_chip_mask_parent;
+ if (!chip->irq_unmask)
+ chip->irq_unmask = irq_chip_unmask_parent;
+ if (!chip->irq_eoi)
+ chip->irq_eoi = irq_chip_eoi_parent;
+ if (!chip->irq_set_affinity)
+ chip->irq_set_affinity = msi_domain_set_affinity;
+}
+
+struct irq_domain *inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
+ struct msi_domain_info *info,
+ struct irq_domain *parent)
+{
+ struct irq_domain *domain;
+
+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+ inta_msi_update_dom_ops(info);
+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+ inta_msi_update_chip_ops(info);
+
+ domain = msi_create_irq_domain(fwnode, info, parent);
+ if (domain)
+ irq_domain_update_bus_token(domain, DOMAIN_BUS_K3_INTA_MSI);
+
+ return domain;
+}
+EXPORT_SYMBOL_GPL(inta_msi_create_irq_domain);
+
+static struct msi_desc *inta_msi_alloc_desc(struct device *dev, u32 dev_id,
+ u32 index)
+{
+ struct msi_desc *msi_desc;
+
+ msi_desc = alloc_msi_entry(dev, 1, NULL);
+ if (!msi_desc) {
+ dev_err(dev, "Failed to allocate msi entry\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ msi_desc->inta.msi_index = index;
+ msi_desc->inta.dev_id = dev_id;
+ INIT_LIST_HEAD(&msi_desc->list);
+ list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
+
+ return msi_desc;
+}
+
+void inta_msi_domain_free_group_irqs(struct device *dev, u32 *arr_index,
+ int nr_irqs)
+{
+ struct irq_domain *msi_domain;
+ struct msi_desc *desc, *tmp;
+ unsigned int i, virq = 0;
+
+ msi_domain = dev_get_msi_domain(dev);
+
+ list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
+ for (i = 0; i < nr_irqs; i++) {
+ if (desc->inta.msi_index == arr_index[i]) {
+ msi_domain_free_irq(desc);
+ /* HACK to get parent IRQ. Any elegant solution? */
+ if (!virq)
+ virq = desc->msg.data;
+ list_del(&desc->list);
+ free_msi_entry(desc);
+ }
+ }
+ }
+
+ msi_domain_unprepare_irqs(msi_domain, nr_irqs, (void *)&virq);
+}
+EXPORT_SYMBOL_GPL(inta_msi_domain_free_group_irqs);
+
+int inta_msi_domain_alloc_group_irqs(struct device *dev, u32 dev_id,
+ int nr_irqs, u32 *arr_index)
+{
+ struct irq_domain *msi_domain;
+ struct msi_desc *msi_desc;
+ msi_alloc_info_t arg;
+ int ret, i;
+
+ msi_domain = dev_get_msi_domain(dev);
+ if (!msi_domain)
+ return -EINVAL;
+
+ if (nr_irqs < 1)
+ return -EINVAL;
+
+ ret = msi_domain_prepare_irqs(msi_domain, dev, nr_irqs, &arg);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ msi_desc = inta_msi_alloc_desc(dev, dev_id, arr_index[i]);
+ if (IS_ERR(msi_desc)) {
+ ret = PTR_ERR(msi_desc);
+ goto cleanup;
+ }
+
+ ret = msi_domain_alloc_irq(msi_domain, dev, msi_desc, &arg);
+ if (ret) {
+ dev_err(dev, "Failed to allocate IRQs\n");
+ goto cleanup;
+ }
+ }
+ return 0;
+
+cleanup:
+ inta_msi_domain_free_group_irqs(dev, arr_index, i);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(inta_msi_domain_alloc_group_irqs);
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 068aa46f0d55..47bb695e1067 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -81,6 +81,7 @@ enum irq_domain_bus_token {
DOMAIN_BUS_NEXUS,
DOMAIN_BUS_IPI,
DOMAIN_BUS_FSL_MC_MSI,
+ DOMAIN_BUS_K3_INTA_MSI,
};

/**
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 1e37aa569a3c..acc2873bb197 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -47,6 +47,11 @@ struct fsl_mc_msi_desc {
u16 msi_index;
};

+struct inta_msi_desc {
+ u16 dev_id;
+ u16 msi_index;
+};
+
/**
* struct msi_desc - Descriptor structure for MSI based interrupts
* @list: List head for management
@@ -106,6 +111,7 @@ struct msi_desc {
*/
struct platform_msi_desc platform;
struct fsl_mc_msi_desc fsl_mc;
+ struct inta_msi_desc inta;
};
};

diff --git a/include/linux/soc/ti/k3_inta_msi.h b/include/linux/soc/ti/k3_inta_msi.h
new file mode 100644
index 000000000000..42c6202f044d
--- /dev/null
+++ b/include/linux/soc/ti/k3_inta_msi.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Texas Instruments' K3 INTA MSI helper
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla <[email protected]>
+ */
+
+#ifndef __INCLUDE_LINUX_K3_INTA_MSI_H
+#define __INCLUDE_LINUX_K3_INTA_MSI_H
+
+#include <linux/msi.h>
+
+struct irq_domain *inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
+ struct msi_domain_info *info,
+ struct irq_domain *parent);
+int inta_msi_domain_alloc_group_irqs(struct device *dev, u32 dev_id,
+ int nr_irqs, u32 *arr_index);
+void inta_msi_domain_free_group_irqs(struct device *dev, u32 *arr_index,
+ int nr_irqs);
+#endif /* __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H */
--
2.19.1


2018-11-06 08:44:19

by Lokesh Vutla

[permalink] [raw]
Subject: [RFC PATCH v3 13/13] soc: ti: am6: Enable interrupt controller drivers

Select all the TISCI dependent interrupt controller drivers
for AM6 SoC.

Suggested-by: Marc Zyngier <[email protected]>
Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- None

drivers/soc/ti/Kconfig | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 7640490c2a6a..775217a3cfe5 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -5,6 +5,9 @@ if ARCH_K3

config ARCH_K3_AM6_SOC
bool "K3 AM6 SoC"
+ select TI_SCI_PROTOCOL
+ select TI_SCI_INTR_IRQCHIP
+ select TI_SCI_INTA_IRQCHIP
help
Enable support for TI's AM6 SoC Family support

--
2.19.1


2018-11-06 08:44:19

by Lokesh Vutla

[permalink] [raw]
Subject: [RFC PATCH v3 09/13] genirq/msi: Add support for .msi_unprepare callback

Add an optional callback .msi_unprepare to struct msi_domain_ops.
This is used to clear any effect that is done by .msi_prepare callback.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- New patch

include/linux/msi.h | 3 +++
kernel/irq/msi.c | 10 ++++++++++
2 files changed, 13 insertions(+)

diff --git a/include/linux/msi.h b/include/linux/msi.h
index 021f789226b5..1e37aa569a3c 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -237,6 +237,8 @@ struct msi_domain_ops {
int (*msi_prepare)(struct irq_domain *domain,
struct device *dev, int nvec,
msi_alloc_info_t *arg);
+ void (*msi_unprepare)(struct irq_domain *domain, int nvec,
+ void *data);
void (*msi_finish)(msi_alloc_info_t *arg, int retval);
void (*set_desc)(msi_alloc_info_t *arg,
struct msi_desc *desc);
@@ -317,6 +319,7 @@ void platform_msi_domain_free_irqs(struct device *dev);
/* When an MSI domain is used as an intermediate domain */
int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *args);
+void msi_domain_unprepare_irqs(struct irq_domain *domain, int nvec, void *data);
int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
int virq, int nvec, msi_alloc_info_t *args);
struct irq_domain *
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index e37238317fab..87ce5a5d0c6d 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -312,6 +312,16 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
return ret;
}

+void msi_domain_unprepare_irqs(struct irq_domain *domain, int nvec,
+ void *data)
+{
+ struct msi_domain_info *info = domain->host_data;
+ struct msi_domain_ops *ops = info->ops;
+
+ if (ops->msi_unprepare)
+ ops->msi_unprepare(domain, nvec, data);
+}
+
int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
int virq, int nvec, msi_alloc_info_t *arg)
{
--
2.19.1


2018-11-06 08:44:42

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 05/13] firmware: ti_sci: Add helper apis to manage resources

Each resource with in the device can be uniquely identified
by a type and subtype as defined by TISCI. Since this is generic
across the devices, resource allocation also can be made generic
instead of each client driver handling the resource. So add helper
apis to manage the resource.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- None

drivers/firmware/ti_sci.c | 128 +++++++++++++++++++++++++
include/linux/soc/ti/ti_sci_protocol.h | 48 ++++++++++
2 files changed, 176 insertions(+)

diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index c2f0815edab6..581d7a151533 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -2480,6 +2480,134 @@ const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev,
}
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
+ */
+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);
+
+/**
+ * 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
+ * @of_prop: property name by which the resource are represented
+ *
+ * Note: This function expects of_prop to be in the form of tuples
+ * <type, subtype>. Allocates and initializes ti_sci_resource structure
+ * for each of_prop. Client driver can directly call
+ * ti_sci_(get_free, release)_resource apis for handling the resource.
+ *
+ * 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)
+{
+ u32 resource_subtype;
+ u16 resource_type;
+ struct ti_sci_resource *res;
+ int sets, i, ret;
+
+ res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return ERR_PTR(-ENOMEM);
+
+ sets = of_property_count_elems_of_size(dev_of_node(dev), of_prop,
+ sizeof(u32));
+ if (sets < 0) {
+ dev_err(dev, "%s resource type ids not available\n", of_prop);
+ return ERR_PTR(sets);
+ }
+
+ res->sets = sets;
+
+ res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc),
+ GFP_KERNEL);
+ if (!res->desc)
+ return ERR_PTR(-ENOMEM);
+
+ ret = ti_sci_get_resource_type(handle_to_ti_sci_info(handle), dev_id,
+ &resource_type);
+ if (ret) {
+ dev_err(dev, "No valid resource type for %u\n", dev_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ 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, "type %d subtype %d not allocated for host %d\n",
+ resource_type, resource_subtype,
+ handle_to_ti_sci_info(handle)->host_id);
+ return ERR_PTR(ret);
+ }
+
+ dev_dbg(dev, "res type = %d, subtype = %d, start = %d, num = %d\n",
+ resource_type, 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;
+}
+EXPORT_SYMBOL_GPL(devm_ti_sci_get_of_resource);
+
static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
void *cmd)
{
diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h
index 6d17580839dc..2a1a41a17295 100644
--- a/include/linux/soc/ti/ti_sci_protocol.h
+++ b/include/linux/soc/ti/ti_sci_protocol.h
@@ -317,6 +317,33 @@ 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);
@@ -325,6 +352,11 @@ 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);
+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 */

@@ -357,6 +389,22 @@ const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev,
{
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 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 */
--
2.19.1


2018-11-06 08:44:58

by Lokesh Vutla

[permalink] [raw]
Subject: [PATCH v3 07/13] irqchip: ti-sci-intr: Add support for Interrupt Router driver

Texas Instruments' K3 generation SoCs has an IP Interrupt Router
that does allows for multiplexing of input interrupts to host
interrupt controller. Interrupt Router inputs are either from a
peripheral or from an Interrupt Aggregator which is another
interrupt controller.

Configuration of the interrupt router registers can only be done by
a system co-processor and the driver needs to send a message to this
co processor over TISCI protocol.

Add support for Interrupt Router driver over TISCI protocol.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- Updated interrupt cells to 4 in order to differentiate interrupts
from Interrupt Aggregator.

MAINTAINERS | 1 +
drivers/irqchip/Kconfig | 11 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-ti-sci-intr.c | 310 ++++++++++++++++++++++++++++++
4 files changed, 323 insertions(+)
create mode 100644 drivers/irqchip/irq-ti-sci-intr.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0e3aa3386287..de6fb2bf70f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14740,6 +14740,7 @@ 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: drivers/irqchip/irq-ti-sci-intr.c

THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <[email protected]>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 51a5ef0e96ed..f285b294fe2f 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -391,6 +391,17 @@ config CSKY_APB_INTC
by C-SKY single core SOC system. It use mmio map apb-bus to visit
the controller's register.

+config TI_SCI_INTR_IRQCHIP
+ bool
+ depends on TI_SCI_PROTOCOL && ARCH_K3
+ select IRQ_DOMAIN
+ 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.
+
endmenu

config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 794c13d3ac3d..8e1b656b5a07 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -91,3 +91,4 @@ obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
+obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c
new file mode 100644
index 000000000000..a5396e08412c
--- /dev/null
+++ b/drivers/irqchip/irq-ti-sci-intr.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments' K3 Interrupt Router irqchip driver
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla <[email protected]>
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/irqdomain.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 TI_SCI_EVENT_IRQ BIT(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 FWSPEC_TO_HWIRQ(fwspec) (((fwspec->param[0] & TI_SCI_DEV_ID_MASK) << \
+ TI_SCI_DEV_ID_SHIFT) | \
+ (fwspec->param[1] & 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 destination irq controller.
+ * @dst_id: TISCI device ID of the destination irq controller.
+ */
+struct ti_sci_intr_irq_domain {
+ const struct ti_sci_handle *sci;
+ struct ti_sci_resource *dst_irq;
+ u16 dst_id;
+};
+
+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_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = irq_chip_set_type_parent,
+ .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)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 4)
+ return -EINVAL;
+
+ *hwirq = FWSPEC_TO_HWIRQ(fwspec);
+ *type = fwspec->param[2];
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static inline void ti_sci_intr_delete_desc(struct ti_sci_intr_irq_domain *intr,
+ u16 src_id, u16 src_index,
+ u16 dst_irq)
+{
+ intr->sci->ops.rm_irq_ops.free_direct_irq(intr->sci, src_id, src_index,
+ intr->dst_id, dst_irq);
+}
+
+/**
+ * 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;
+ u64 flags;
+ int i;
+
+ intr = domain->host_data;
+
+ for (i = 0; i < nr_irqs; i++) {
+ data = irq_domain_get_irq_data(domain, virq + i);
+ flags = (u64)irq_data_get_irq_chip_data(data);
+ parent_data = irq_domain_get_irq_data(domain->parent, virq + i);
+
+ if (!(flags & TI_SCI_EVENT_IRQ))
+ ti_sci_intr_delete_desc(intr,
+ HWIRQ_TO_DEVID(data->hwirq),
+ HWIRQ_TO_IRQID(data->hwirq),
+ parent_data->hwirq);
+ ti_sci_release_resource(intr->dst_irq, parent_data->hwirq);
+ irq_domain_free_irqs_parent(domain, virq + i, 1);
+ irq_domain_reset_irq_data(data);
+ }
+}
+
+/**
+ * ti_sci_intr_allocate_gic_irq() - Allocate GIC specific IRQ
+ * @domain: Point to the interrupt router IRQ domain
+ * @dev: TISCI device IRQ generating the IRQ
+ * @irq: IRQ offset within the device
+ * @flags: Corresponding flags to the IRQ
+ * @event_irq: Flag to tell if requested irq is from interrupt aggregator.
+ *
+ * Returns 0 if all went well else appropriate error pointer.
+ */
+static int ti_sci_intr_allocate_gic_irq(struct irq_domain *domain,
+ unsigned int virq, u16 dev, u16 irq,
+ u32 flags, u8 event_irq)
+{
+ struct ti_sci_intr_irq_domain *intr = domain->host_data;
+ struct irq_fwspec fwspec;
+ u16 dst_irq;
+ int err;
+
+ if (!irq_domain_get_of_node(domain->parent))
+ return -EINVAL;
+
+ 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] = flags & IRQ_TYPE_SENSE_MASK;
+
+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+ if (err)
+ goto err_irqs;
+
+ /* If event is requested then return */
+ if (event_irq == TI_SCI_EVENT_IRQ)
+ return 0;
+
+ err = intr->sci->ops.rm_irq_ops.set_direct_irq(intr->sci, dev, irq,
+ intr->dst_id, dst_irq);
+ if (err) {
+ pr_err("%s: IRQ allocation failed from src = %d, src_index = %d to dst_id = %d, dst_irq = %d",
+ __func__, dev, irq, intr->dst_id, dst_irq);
+ 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;
+ u16 src_id, src_index;
+ unsigned long hwirq;
+ u8 event_irq;
+ int i, err;
+ u32 type;
+
+ err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &type);
+ if (err)
+ return err;
+
+ src_id = HWIRQ_TO_DEVID(hwirq);
+ src_index = HWIRQ_TO_IRQID(hwirq);
+ event_irq = fwspec->param[3];
+
+ for (i = 0; i < nr_irqs; i++) {
+ err = ti_sci_intr_allocate_gic_irq(domain, virq + i, src_id,
+ src_index + i, type,
+ event_irq);
+ if (err)
+ goto err_irq;
+
+ err = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &ti_sci_intr_irq_chip,
+ (void *)(u64)event_irq);
+ if (err)
+ goto err_irq;
+ }
+
+ return 0;
+err_irq:
+ ti_sci_intr_irq_domain_free(domain, virq, i);
+ return err;
+}
+
+static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
+ .alloc = ti_sci_intr_irq_domain_alloc,
+ .free = ti_sci_intr_irq_domain_free,
+ .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;
+
+ 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",
+ (u32 *)&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");
--
2.19.1


2018-11-06 08:45:15

by Lokesh Vutla

[permalink] [raw]
Subject: [RFC PATCH v3 12/13] irqchip: ti-sci-inta: Add support for Interrupt Aggregator driver

Texas Instruments' K3 generation SoCs has an IP Interrupt Aggregator
which is an interrupt controller that does the following:
- Converts events to interrupts that can be understood by
an interrupt router.
- Allows for multiplexing of events to interrupts.

Configuration of the interrupt aggregator registers can only be done by
a system co-processor and the driver needs to send a message to this
co processor over TISCI protocol.

Add support for Interrupt Aggregator driver over TISCI protocol.

Signed-off-by: Lokesh Vutla <[email protected]>
Signed-off-by: Peter Ujfalusi <[email protected]>
---
Changes since v2:
- Added support for MSI

MAINTAINERS | 1 +
drivers/irqchip/Kconfig | 12 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-ti-sci-inta.c | 543 ++++++++++++++++++++++++++++++
4 files changed, 557 insertions(+)
create mode 100644 drivers/irqchip/irq-ti-sci-inta.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2f20efab4664..fc89a1392478 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14742,6 +14742,7 @@ 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

THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <[email protected]>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index f285b294fe2f..4768208c464d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -402,6 +402,18 @@ config TI_SCI_INTR_IRQCHIP
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 && ARCH_K3
+ select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
+ select K3_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
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 8e1b656b5a07..a44d9e9dc4f9 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -92,3 +92,4 @@ obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
+obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c
new file mode 100644
index 000000000000..1d75fbe1c271
--- /dev/null
+++ b/drivers/irqchip/irq-ti-sci-inta.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments' K3 Interrupt Aggregator irqchip driver
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla <[email protected]>
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/msi.h>
+#include <linux/irqchip.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/irqdomain.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+#include <linux/soc/ti/k3_inta_msi.h>
+#include <linux/irqchip/chained_irq.h>
+#include <asm-generic/msi.h>
+
+#define MAX_EVENTS_PER_VINT 64
+
+#define VINT_ENABLE_SET_OFFSET 0x0
+#define VINT_ENABLE_CLR_OFFSET 0x8
+#define VINT_STATUS_OFFSET 0x18
+
+#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))
+
+/**
+ * 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.
+ * @base: Base address of the memory mapped IO registers
+ * @ia_id: TISCI device ID of this Interrupt Aggregator.
+ * @dst_id: TISCI device ID of the destination irq controller.
+ */
+struct ti_sci_inta_irq_domain {
+ const struct ti_sci_handle *sci;
+ struct ti_sci_resource *vint;
+ struct ti_sci_resource *global_event;
+ void __iomem *base;
+ u16 ia_id;
+ u16 dst_id;
+};
+
+/**
+ * struct ti_sci_inta_event_desc - Description of an event coming to
+ * Interrupt Aggregator.
+ * @global_event: Global event number corresponding to this event
+ * @src_id: TISCI device ID of the event source
+ * @src_index: Event source index within the device.
+ */
+struct ti_sci_inta_event_desc {
+ u16 global_event;
+ u16 src_id;
+ u16 src_index;
+};
+
+/**
+ * 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.
+ * @event_lock: lock to guard the event map
+ * @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 mutex event_lock;
+ unsigned long *event_map;
+ struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT];
+ unsigned int parent_virq;
+ u16 vint_id;
+};
+
+static int __get_event_index(struct ti_sci_inta_vint_desc *vint_desc,
+ int global_event)
+{
+ int event_index = -ENODEV, i;
+
+ for (i = 0; i < MAX_EVENTS_PER_VINT; i++) {
+ if (vint_desc->events[i].global_event == global_event)
+ event_index = i;
+ }
+
+ return event_index;
+}
+
+static void __ti_sci_inta_manage_event(struct irq_data *data, u32 offset)
+{
+ struct ti_sci_inta_vint_desc *vint_desc;
+ struct ti_sci_inta_irq_domain *inta;
+ int global_event, event_index;
+
+ vint_desc = irq_data_get_irq_chip_data(data);
+ global_event = data->hwirq;
+ event_index = __get_event_index(vint_desc, global_event);
+ inta = vint_desc->domain->host_data;
+
+ if (event_index < 0)
+ return;
+
+ writeq_relaxed(BIT(event_index), inta->base +
+ vint_desc->vint_id * 0x1000 + offset);
+}
+
+static void ti_sci_inta_mask_irq(struct irq_data *data)
+{
+ __ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET);
+}
+
+static void ti_sci_inta_unmask_irq(struct irq_data *data)
+{
+ __ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET);
+}
+
+static struct irq_chip ti_sci_inta_irq_chip = {
+ .name = "INTA",
+ .irq_mask = ti_sci_inta_mask_irq,
+ .irq_unmask = ti_sci_inta_unmask_irq,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+/**
+ * ti_sci_free_event_irq() - Free an event from vint
+ * @domain: Pointer to Interrupt Aggregator IRQ domain
+ * @vint_desc: Virtual interrupt descriptor containing the event.
+ * @global_event: Global event id to be freed.
+ */
+static void ti_sci_free_event_irq(struct irq_domain *domain,
+ struct ti_sci_inta_vint_desc *vint_desc,
+ u16 global_event)
+{
+ struct ti_sci_inta_irq_domain *inta = domain->host_data;
+ struct ti_sci_inta_event_desc *event_desc;
+ struct irq_data *gic_data;
+ int event_index = 0;
+
+ event_index = __get_event_index(vint_desc, global_event);
+ gic_data = irq_domain_get_irq_data(domain->parent->parent,
+ vint_desc->parent_virq);
+ event_desc = &vint_desc->events[event_index];
+ inta->sci->ops.rm_irq_ops.free_event_irq(inta->sci,
+ event_desc->src_id,
+ event_desc->src_index,
+ inta->dst_id,
+ gic_data->hwirq,
+ inta->ia_id,
+ vint_desc->vint_id,
+ event_desc->global_event,
+ event_index);
+
+ clear_bit(event_index, vint_desc->event_map);
+
+ ti_sci_release_resource(inta->global_event, event_desc->global_event);
+}
+
+static void ti_sci_inta_free_vint(struct ti_sci_inta_irq_domain *inta,
+ struct ti_sci_inta_vint_desc *vint_desc)
+{
+ /* If all events are cleared, delete parent irq */
+ if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) ==
+ MAX_EVENTS_PER_VINT) {
+ irq_dispose_mapping(vint_desc->parent_virq);
+ ti_sci_release_resource(inta->vint, vint_desc->vint_id);
+ kfree(vint_desc->event_map);
+ kfree(vint_desc);
+ }
+}
+
+/**
+ * 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_desc *desc = irq_to_desc(virq);
+ struct ti_sci_inta_vint_desc *vint_desc;
+ struct msi_desc *mdesc;
+ struct irq_data *data;
+
+ mdesc = desc->irq_common_data.msi_desc;
+ data = irq_domain_get_irq_data(domain, virq);
+ vint_desc = irq_data_get_irq_chip_data(data);
+
+ mdesc->msg.data = vint_desc->parent_virq;
+ ti_sci_free_event_irq(domain, vint_desc, data->hwirq);
+ irq_domain_reset_irq_data(data);
+}
+
+/**
+ * ti_sci_allocate_event_irq() - Allocate an event to a IA vint.
+ *
+ * Return 0 if all went ok else appropriate error value.
+ */
+static struct ti_sci_inta_event_desc *
+ti_sci_allocate_event_irq(struct irq_domain *domain, msi_alloc_info_t *arg)
+{
+ struct ti_sci_inta_irq_domain *inta = domain->host_data;
+ struct ti_sci_inta_event_desc *event_desc;
+ u16 free_bit, src_id, src_index, dst_irq;
+ struct ti_sci_inta_vint_desc *vint_desc;
+ struct irq_data *gic_data;
+ int err;
+
+ src_id = HWIRQ_TO_DEVID(arg->hwirq);
+ src_index = HWIRQ_TO_IRQID(arg->hwirq);
+ vint_desc = arg->scratchpad[0].ptr;
+ gic_data = irq_domain_get_irq_data(domain->parent->parent,
+ vint_desc->parent_virq);
+ dst_irq = gic_data->hwirq;
+
+ mutex_lock(&vint_desc->event_lock);
+ 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);
+ mutex_unlock(&vint_desc->event_lock);
+
+ event_desc = &vint_desc->events[free_bit];
+
+ event_desc->src_id = src_id;
+ event_desc->src_index = src_index;
+ event_desc->global_event = ti_sci_get_free_resource(inta->global_event);
+ if (event_desc->global_event == TI_SCI_RESOURCE_NULL) {
+ err = -EINVAL;
+ goto free_event;
+ }
+
+ err = inta->sci->ops.rm_irq_ops.set_event_irq(inta->sci,
+ src_id, src_index,
+ inta->dst_id,
+ dst_irq,
+ inta->ia_id,
+ vint_desc->vint_id,
+ event_desc->global_event,
+ free_bit);
+ if (err) {
+ pr_err("%s: Event allocation failed from src = %d, index = %d, to dst = %d,irq = %d,via ia_id = %d, vint = %d,global event = %d, status_bit = %d\n",
+ __func__, src_id, src_index, inta->dst_id, dst_irq,
+ inta->ia_id, vint_desc->vint_id,
+ event_desc->global_event, free_bit);
+ goto free_global_event;
+ }
+
+ return event_desc;
+free_global_event:
+ ti_sci_release_resource(inta->global_event, event_desc->global_event);
+free_event:
+ clear_bit(free_bit, vint_desc->event_map);
+ return ERR_PTR(err);
+}
+
+static void inta_msi_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;
+ u32 hwirq, bit, virq;
+ u64 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 (bit = 0; bit < MAX_EVENTS_PER_VINT; bit++) {
+ if (BIT(bit) & val) {
+ writeq_relaxed(BIT(bit),
+ inta->base + vint_desc->vint_id *
+ 0x1000 + VINT_STATUS_OFFSET);
+ hwirq = vint_desc->events[bit].global_event;
+ virq = irq_find_mapping(domain, 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
+ * @virq: Linux virtual IRQ number
+ *
+ * Return pointer to vint descriptor if all went well else corresponding
+ * error pointer.
+ */
+static struct ti_sci_inta_vint_desc *
+ti_sci_inta_alloc_parent_irq(struct irq_domain *domain, msi_alloc_info_t *arg)
+{
+ 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 virq;
+
+ if (!irq_domain_get_of_node(domain->parent))
+ return ERR_PTR(-EINVAL);
+
+ vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL);
+ if (!vint_desc)
+ return ERR_PTR(-ENOMEM);
+
+ vint_desc->event_map = kcalloc(BITS_TO_LONGS(MAX_EVENTS_PER_VINT),
+ sizeof(*vint_desc->event_map),
+ GFP_KERNEL);
+ if (!vint_desc->event_map) {
+ kfree(vint_desc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ vint_desc->domain = domain;
+ vint_desc->vint_id = ti_sci_get_free_resource(inta->vint);
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 4;
+ /* Interrupt parent is Interrupt Router */
+ parent_fwspec.param[0] = inta->ia_id;
+ parent_fwspec.param[1] = vint_desc->vint_id;
+ parent_fwspec.param[2] = IRQF_TRIGGER_HIGH;
+ parent_fwspec.param[3] = 1;
+
+ virq = irq_create_fwspec_mapping(&parent_fwspec);
+ if (virq <= 0)
+ goto err_irqs;
+
+ irq_set_chained_handler_and_data(virq, inta_msi_irq_handler, vint_desc);
+ vint_desc->parent_virq = virq;
+
+ mutex_init(&vint_desc->event_lock);
+
+ return vint_desc;
+
+err_irqs:
+ ti_sci_release_resource(inta->vint, vint_desc->vint_id);
+ kfree(vint_desc);
+ return ERR_PTR(virq);
+}
+
+/**
+ * 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
+ *
+ * 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)
+{
+ struct ti_sci_inta_event_desc *event_desc;
+ msi_alloc_info_t *arg = data;
+
+ event_desc = ti_sci_allocate_event_irq(domain, arg);
+ if (IS_ERR(event_desc)) {
+ ti_sci_inta_free_vint(domain->host_data,
+ arg->scratchpad[0].ptr);
+ return PTR_ERR(event_desc);
+ }
+
+ irq_domain_set_info(domain, virq, event_desc->global_event,
+ &ti_sci_inta_irq_chip, arg->scratchpad[0].ptr,
+ handle_edge_irq, NULL, NULL);
+
+ return 0;
+}
+
+static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = {
+ .alloc = ti_sci_inta_irq_domain_alloc,
+ .free = ti_sci_inta_irq_domain_free,
+};
+
+static int inta_msi_domain_ops_prepare(struct irq_domain *domain,
+ struct device *dev, int nvec,
+ msi_alloc_info_t *arg)
+{
+ struct ti_sci_inta_vint_desc *vint_desc;
+
+ memset(arg, 0, sizeof(*arg));
+
+ vint_desc = ti_sci_inta_alloc_parent_irq(domain->parent, arg);
+ if (IS_ERR(vint_desc))
+ return PTR_ERR(vint_desc);
+ arg->scratchpad[0].ptr = vint_desc;
+
+ return 0;
+}
+
+void inta_msi_domain_ops_unprepare(struct irq_domain *domain, int nvec,
+ void *data)
+{
+ struct ti_sci_inta_irq_domain *inta = domain->parent->host_data;
+ struct ti_sci_inta_vint_desc *vint_desc;
+ struct irq_desc *desc;
+ unsigned int virq;
+
+ virq = *(unsigned int *)data;
+ desc = irq_to_desc(virq);
+ vint_desc = irq_desc_get_handler_data(desc);
+ ti_sci_inta_free_vint(inta, vint_desc);
+}
+
+static struct irq_chip inta_msi_irq_chip = {
+ .name = "MSI-INTA",
+};
+
+static struct msi_domain_ops inta_msi_ops = {
+ .msi_prepare = inta_msi_domain_ops_prepare,
+ .msi_unprepare = inta_msi_domain_ops_unprepare,
+};
+
+static struct msi_domain_info inta_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+ .ops = &inta_msi_ops,
+ .chip = &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->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",
+ (u32 *)&inta->ia_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,
+ inta->ia_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,
+ inta->ia_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;
+
+ ret = of_property_read_u32(parent_node, "ti,sci-dst-id",
+ (u32 *)&inta->dst_id);
+
+ domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
+ &ti_sci_inta_irq_domain_ops, inta);
+ if (!domain) {
+ dev_err(dev, "Failed to allocate IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ msi_domain = inta_msi_create_irq_domain(of_node_to_fwnode(node),
+ &inta_msi_domain_info,
+ domain);
+ if (!msi_domain) {
+ irq_domain_remove(domain);
+ dev_err(dev, "Failed to allocate msi domain\n");
+ return -ENOMEM;
+ }
+
+ 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");
--
2.19.1


2018-11-06 08:45:19

by Lokesh Vutla

[permalink] [raw]
Subject: [RFC PATCH v3 11/13] dt-bindings: irqchip: Introduce TISCI Interrupt Aggregator bindings

Add the DT binding documentation for Interrupt Aggregator driver.

Signed-off-by: Lokesh Vutla <[email protected]>
---
Changes since v2:
- None

.../interrupt-controller/ti,sci-inta.txt | 74 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 75 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
new file mode 100644
index 000000000000..17b1fbd90312
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
@@ -0,0 +1,74 @@
+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 |
+ . | +--------------+ +------------+ | +------+
+ . | . . | | |
+Globalevents ------>| . . |------>| INTA |
+ . | . . | | |
+ . | . . | +------+
+ 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
+- #interrupt-cells: Specifies the number of cells needed to encode an
+ interrupt source. The value should be 4.
+ First cell should contain the TISCI device ID of source
+ Second cell should contain the event source offset
+ within the device
+ Third cell specified the interrupt number(vint)
+ reaching Interrupt aggregator.
+ Fourth cell specifies the trigger type as defined
+ in interrupts.txt in this directory.
+- interrupt-parent: phandle of irq parent for TISCI intr.
+- 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: TISCI subtype id representing the virtual interrupts
+ (vints) range within this IA, assigned to the
+ requesting host context.
+- ti,sci-rm-range-global-event: TISCI subtype id 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;
+ interrupt-parent = <&main_navss_intr>;
+ #interrupt-cells = <4>;
+ ti,sci = <&dmsc>;
+ ti,sci-dev-id = <179>;
+ ti,sci-rm-range-vint = <0x0>;
+ ti,sci-rm-range-global-event = <0x1>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index de6fb2bf70f4..2f20efab4664 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14740,6 +14740,7 @@ 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

THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
--
2.19.1


2018-11-13 06:16:02

by Lokesh Vutla

[permalink] [raw]
Subject: Re: [PATCH v3 06/13] dt-bindings: irqchip: Introduce TISCI Interrupt router bindings

Hi Rob,

On 12/11/18 11:30 PM, Rob Herring wrote:
> On Tue, Nov 06, 2018 at 02:10:58PM +0530, Lokesh Vutla wrote:
>> Add the DT binding documentation for Interrupt router driver.
>>
>> Signed-off-by: Lokesh Vutla <[email protected]>
>> ---
>> Changes since v2:
>> - Dropped interrupt-parent from reqired properties description
>> - Updated the interrupt cells to 4.
>>
>> .../interrupt-controller/ti,sci-intr.txt | 84 +++++++++++++++++++
>> MAINTAINERS | 1 +
>> 2 files changed, 85 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>>
>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>> new file mode 100644
>> index 000000000000..06e69f8c812c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>> @@ -0,0 +1,84 @@
>> +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. There is one register per output (MUXCNTL_N) that
>> +controls the selection.
>> +
>> +
>> + Interrupt Router
>> + +----------------------+
>> + | Inputs Outputs |
>> + +-------+ | +------+ |
>> + | GPIO |----------->| | irq0 | | Host IRQ
>> + +-------+ | +------+ | controller
>> + | . +-----+ | +-------+
>> + +-------+ | . | 0 | |----->| IRQ |
>> + | INTA |----------->| . +-----+ | +-------+
>> + +-------+ | . . |
>> + | +------+ . |
>> + | | irqM | +-----+ |
>> + | +------+ | N | |
>> + | +-----+ |
>> + +----------------------+
>> +
>> +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:
>> +----------------------------
>> +- compatible: Must be "ti,sci-intr".
>> +- 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 4.
>> + First cell should contain the TISCI device ID of source
>> + Second cell should contain the interrupt source offset
>> + within the device
>> + Third cell specifies the trigger type as defined
>> + in interrupts.txt in this directory.
>> + Fourth cell should be 1 if the irq is coming from
>> + interrupt aggregator else 0.
>> +- 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: TISCI subtype id representing the host irqs assigned
>> + to this interrupt router.
>
> u32 or array?

it is u32.

>
>> +
>> +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-controller@1 {
>
> Unit-address is not valid here without a reg property.

Sure will fix it in next version.

Thanks and regards,
Lokesh

>
>> + compatible = "ti,sci-intr";
>> + interrupt-controller;
>> + interrupt-parent = <&gic>;
>> + #interrupt-cells = <4>;
>> + 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 IRQ_TYPE_EDGE_RISING 0>,
>> + <57 257 IRQ_TYPE_EDGE_RISING 0>,
>> + <57 258 IRQ_TYPE_EDGE_RISING 0>,
>> + <57 259 IRQ_TYPE_EDGE_RISING 0>,
>> + <57 260 IRQ_TYPE_EDGE_RISING 0>,
>> + <57 261 IRQ_TYPE_EDGE_RISING 0>;
>> + ...
>> +};
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 88b28f6dec45..0e3aa3386287 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -14739,6 +14739,7 @@ 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
>>
>> THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
>> M: Hans Verkuil <[email protected]>
>> --
>> 2.19.1
>>
>

2018-11-13 07:36:58

by Lokesh Vutla

[permalink] [raw]
Subject: Re: [RFC PATCH v3 10/13] soc: ti: Add MSI domain support for K3 Interrupt Aggregator

Hi Marc,

On 06/11/18 2:11 PM, Lokesh Vutla wrote:
> With the system coprocessor managing the range allocation of the
> inputs to Interrupt Aggregator, it is difficult to represent
> the device IRQs from DT.
>
> The suggestion is to use MSI in such cases where devices wants
> to allocate and group interrupts dynamically.
>
> Create a MSI domain bus layer that allocates and frees MSIs for
> a device.
>
> APIs that are implemented are as follows:
> - inta_msi_create_irq_domain() that creates a MSI domain
> - inta_msi_domain_alloc_group_irqs() that creates MSIs for the
> specified device and source indexes. All these are expected to
> be grouped by the parent interrupt controller to MSI domain.
> - inta_msi_domain_free_group_irqs() frees the grouped irqs.
>
> Signed-off-by: Lokesh Vutla <[email protected]>


This is the initial implementation of MSI layer for the Interrupt Aggregator
driver.
In case if you have forgotten how the INTA is integrated with TISCI, below is
the explanation:

Device Index-x Device Index-y
| |
| |
....
\ /
\ /
\ (global events) /
+---------------------------+
| |
| INTA |
| |
+---------------------------+
| (vint)
|
\|/
+---------------------------+
| |
| INTR |
| |
+---------------------------+
|
|
\|/ (gic irq)
+---------------------------+
| |
| GIC |
| |
+---------------------------+


Now in the above diagram, Device indexes, global events, vints, gic irqs are
managed dynamically in the available ranges for the current host. When a message
is sent to
system co-processor with the parameters(dev_id, dev_index, global_event, vint,
gic_irq), it will:
- Program the device index to global event.
- Program the global event to vint in INTA (Grouping allowed if needed)
- Program the vint to gic irq in INTR

All the above configuration is done by sending a single message to system
co-processor using TISCI protocol.

Coming to software configuration:
- gic irq range is managed by INTR driver as there can be devices other than
INTA attached to INTR.
- Global event range and vint range is managed by INTA driver.

Now that device indexes are also managed dynamically by their respective
drivers, MSIs are being used for allocation of Linux interrupts. This series
tries to implement this allocation sequence using MSIs. I am mainly concerned
about the prepare and the unprepare part. Parent interrupts to INTA are
allocated and released using this prepare and unprepare callbacks. This might be
wrong but I couldn't find a right place for it. Also, compose and write_msi_msg
are no ops in this case as msi_msg addr and data are not used.

Please take a look at this RFC series and provide your feedback.

Thanks and regards,
Lokesh



> ---
> - May be the same functionaly can be included in platform msi. But I would
> like to get a feedback on the approach.
>
> Changes since v1:
> - New patch
>
> drivers/soc/ti/Kconfig | 6 ++
> drivers/soc/ti/Makefile | 1 +
> drivers/soc/ti/k3_inta_msi.c | 163 +++++++++++++++++++++++++++++
> include/linux/irqdomain.h | 1 +
> include/linux/msi.h | 6 ++
> include/linux/soc/ti/k3_inta_msi.h | 21 ++++
> 6 files changed, 198 insertions(+)
> create mode 100644 drivers/soc/ti/k3_inta_msi.c
> create mode 100644 include/linux/soc/ti/k3_inta_msi.h
>
> diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
> index be4570baad96..7640490c2a6a 100644
> --- a/drivers/soc/ti/Kconfig
> +++ b/drivers/soc/ti/Kconfig
> @@ -73,4 +73,10 @@ config TI_SCI_PM_DOMAINS
> called ti_sci_pm_domains. Note this is needed early in boot before
> rootfs may be available.
>
> +config K3_INTA_MSI_DOMAIN
> + bool
> + select GENERIC_MSI_IRQ_DOMAIN
> + help
> + Driver to enable Interrupt Aggregator specific MSI Domain.
> +
> endif # SOC_TI
> diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
> index a22edc0b258a..152b195273ee 100644
> --- a/drivers/soc/ti/Makefile
> +++ b/drivers/soc/ti/Makefile
> @@ -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_K3_INTA_MSI_DOMAIN) += k3_inta_msi.o
> diff --git a/drivers/soc/ti/k3_inta_msi.c b/drivers/soc/ti/k3_inta_msi.c
> new file mode 100644
> index 000000000000..0236d836d7f6
> --- /dev/null
> +++ b/drivers/soc/ti/k3_inta_msi.c
> @@ -0,0 +1,163 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Texas Instruments' K3 Interrupt Aggregator driver MSI support
> + *
> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> + * Lokesh Vutla <[email protected]>
> + */
> +
> +#include <linux/of_device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/msi.h>
> +#include <linux/soc/ti/k3_inta_msi.h>
> +
> +#ifdef GENERIC_MSI_DOMAIN_OPS
> +
> +#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 TO_HWIRQ(id, index) (((id & TI_SCI_DEV_ID_MASK) << \
> + TI_SCI_DEV_ID_SHIFT) | \
> + (index & TI_SCI_IRQ_ID_MASK))
> +static void inta_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
> +{
> + arg->desc = desc;
> + arg->hwirq = TO_HWIRQ(desc->inta.dev_id, desc->inta.msi_index);
> +}
> +#else
> +#define inta_msi_set_desc NULL
> +#endif
> +
> +static void inta_msi_update_dom_ops(struct msi_domain_info *info)
> +{
> + struct msi_domain_ops *ops = info->ops;
> +
> + BUG_ON(!ops);
> +
> + if (ops->set_desc == NULL)
> + ops->set_desc = inta_msi_set_desc;
> +}
> +
> +static void inta_msi_update_chip_ops(struct msi_domain_info *info)
> +{
> + struct irq_chip *chip = info->chip;
> +
> + BUG_ON(!chip);
> + if (!chip->irq_mask)
> + chip->irq_mask = irq_chip_mask_parent;
> + if (!chip->irq_unmask)
> + chip->irq_unmask = irq_chip_unmask_parent;
> + if (!chip->irq_eoi)
> + chip->irq_eoi = irq_chip_eoi_parent;
> + if (!chip->irq_set_affinity)
> + chip->irq_set_affinity = msi_domain_set_affinity;
> +}
> +
> +struct irq_domain *inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
> + struct msi_domain_info *info,
> + struct irq_domain *parent)
> +{
> + struct irq_domain *domain;
> +
> + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
> + inta_msi_update_dom_ops(info);
> + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
> + inta_msi_update_chip_ops(info);
> +
> + domain = msi_create_irq_domain(fwnode, info, parent);
> + if (domain)
> + irq_domain_update_bus_token(domain, DOMAIN_BUS_K3_INTA_MSI);
> +
> + return domain;
> +}
> +EXPORT_SYMBOL_GPL(inta_msi_create_irq_domain);
> +
> +static struct msi_desc *inta_msi_alloc_desc(struct device *dev, u32 dev_id,
> + u32 index)
> +{
> + struct msi_desc *msi_desc;
> +
> + msi_desc = alloc_msi_entry(dev, 1, NULL);
> + if (!msi_desc) {
> + dev_err(dev, "Failed to allocate msi entry\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + msi_desc->inta.msi_index = index;
> + msi_desc->inta.dev_id = dev_id;
> + INIT_LIST_HEAD(&msi_desc->list);
> + list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
> +
> + return msi_desc;
> +}
> +
> +void inta_msi_domain_free_group_irqs(struct device *dev, u32 *arr_index,
> + int nr_irqs)
> +{
> + struct irq_domain *msi_domain;
> + struct msi_desc *desc, *tmp;
> + unsigned int i, virq = 0;
> +
> + msi_domain = dev_get_msi_domain(dev);
> +
> + list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
> + for (i = 0; i < nr_irqs; i++) {
> + if (desc->inta.msi_index == arr_index[i]) {
> + msi_domain_free_irq(desc);
> + /* HACK to get parent IRQ. Any elegant solution? */
> + if (!virq)
> + virq = desc->msg.data;
> + list_del(&desc->list);
> + free_msi_entry(desc);
> + }
> + }
> + }
> +
> + msi_domain_unprepare_irqs(msi_domain, nr_irqs, (void *)&virq);
> +}
> +EXPORT_SYMBOL_GPL(inta_msi_domain_free_group_irqs);
> +
> +int inta_msi_domain_alloc_group_irqs(struct device *dev, u32 dev_id,
> + int nr_irqs, u32 *arr_index)
> +{
> + struct irq_domain *msi_domain;
> + struct msi_desc *msi_desc;
> + msi_alloc_info_t arg;
> + int ret, i;
> +
> + msi_domain = dev_get_msi_domain(dev);
> + if (!msi_domain)
> + return -EINVAL;
> +
> + if (nr_irqs < 1)
> + return -EINVAL;
> +
> + ret = msi_domain_prepare_irqs(msi_domain, dev, nr_irqs, &arg);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < nr_irqs; i++) {
> + msi_desc = inta_msi_alloc_desc(dev, dev_id, arr_index[i]);
> + if (IS_ERR(msi_desc)) {
> + ret = PTR_ERR(msi_desc);
> + goto cleanup;
> + }
> +
> + ret = msi_domain_alloc_irq(msi_domain, dev, msi_desc, &arg);
> + if (ret) {
> + dev_err(dev, "Failed to allocate IRQs\n");
> + goto cleanup;
> + }
> + }
> + return 0;
> +
> +cleanup:
> + inta_msi_domain_free_group_irqs(dev, arr_index, i);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(inta_msi_domain_alloc_group_irqs);
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index 068aa46f0d55..47bb695e1067 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -81,6 +81,7 @@ enum irq_domain_bus_token {
> DOMAIN_BUS_NEXUS,
> DOMAIN_BUS_IPI,
> DOMAIN_BUS_FSL_MC_MSI,
> + DOMAIN_BUS_K3_INTA_MSI,
> };
>
> /**
> diff --git a/include/linux/msi.h b/include/linux/msi.h
> index 1e37aa569a3c..acc2873bb197 100644
> --- a/include/linux/msi.h
> +++ b/include/linux/msi.h
> @@ -47,6 +47,11 @@ struct fsl_mc_msi_desc {
> u16 msi_index;
> };
>
> +struct inta_msi_desc {
> + u16 dev_id;
> + u16 msi_index;
> +};
> +
> /**
> * struct msi_desc - Descriptor structure for MSI based interrupts
> * @list: List head for management
> @@ -106,6 +111,7 @@ struct msi_desc {
> */
> struct platform_msi_desc platform;
> struct fsl_mc_msi_desc fsl_mc;
> + struct inta_msi_desc inta;
> };
> };
>
> diff --git a/include/linux/soc/ti/k3_inta_msi.h b/include/linux/soc/ti/k3_inta_msi.h
> new file mode 100644
> index 000000000000..42c6202f044d
> --- /dev/null
> +++ b/include/linux/soc/ti/k3_inta_msi.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Texas Instruments' K3 INTA MSI helper
> + *
> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> + * Lokesh Vutla <[email protected]>
> + */
> +
> +#ifndef __INCLUDE_LINUX_K3_INTA_MSI_H
> +#define __INCLUDE_LINUX_K3_INTA_MSI_H
> +
> +#include <linux/msi.h>
> +
> +struct irq_domain *inta_msi_create_irq_domain(struct fwnode_handle *fwnode,
> + struct msi_domain_info *info,
> + struct irq_domain *parent);
> +int inta_msi_domain_alloc_group_irqs(struct device *dev, u32 dev_id,
> + int nr_irqs, u32 *arr_index);
> +void inta_msi_domain_free_group_irqs(struct device *dev, u32 *arr_index,
> + int nr_irqs);
> +#endif /* __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H */
>

2018-11-16 02:09:58

by Lokesh Vutla

[permalink] [raw]
Subject: Re: [PATCH v3 06/13] dt-bindings: irqchip: Introduce TISCI Interrupt router bindings

Hi Rob,

On 11/13/2018 11:43 AM, Lokesh Vutla wrote:
> Hi Rob,
>
> On 12/11/18 11:30 PM, Rob Herring wrote:
>> On Tue, Nov 06, 2018 at 02:10:58PM +0530, Lokesh Vutla wrote:
>>> Add the DT binding documentation for Interrupt router driver.
>>>
>>> Signed-off-by: Lokesh Vutla <[email protected]>
>>> ---
>>> Changes since v2:
>>> - Dropped interrupt-parent from reqired properties description
>>> - Updated the interrupt cells to 4.
>>>
>>> ? .../interrupt-controller/ti,sci-intr.txt????? | 84 +++++++++++++++++++
>>> ? MAINTAINERS?????????????????????????????????? |? 1 +
>>> ? 2 files changed, 85 insertions(+)
>>> ? create mode 100644
>>> Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>>>
>>> diff --git
>>> a/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>>> b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>>> new file mode 100644
>>> index 000000000000..06e69f8c812c
>>> --- /dev/null
>>> +++
>>> b/Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>>> @@ -0,0 +1,84 @@
>>> +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. There is one register per output
>>> (MUXCNTL_N) that
>>> +controls the selection.
>>> +
>>> +
>>> +???????????????????????????????? Interrupt Router
>>> +???????????????????????????? +----------------------+
>>> +???????????????????????????? |? Inputs???? Outputs? |
>>> +??????? +-------+??????????? | +------+???????????? |
>>> +??????? | GPIO? |----------->| | irq0 |???????????? |?????? Host IRQ
>>> +??????? +-------+??????????? | +------+???????????? |????? controller
>>> +???????????????????????????? |??? .??????? +-----+? |????? +-------+
>>> +??????? +-------+??????????? |??? .??????? |? 0? |? |----->|? IRQ? |
>>> +??????? | INTA? |----------->|??? .??????? +-----+? |????? +-------+
>>> +??????? +-------+??????????? |??? .????????? .????? |
>>> +???????????????????????????? | +------+????? .????? |
>>> +???????????????????????????? | | irqM |??? +-----+? |
>>> +???????????????????????????? | +------+??? |? N? |? |
>>> +???????????????????????????? |???????????? +-----+? |
>>> +???????????????????????????? +----------------------+
>>> +
>>> +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:
>>> +----------------------------
>>> +- compatible:??????? Must be "ti,sci-intr".
>>> +- 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 4.
>>> +??????????? First cell should contain the TISCI device ID of source
>>> +??????????? Second cell should contain the interrupt source offset
>>> +??????????? within the device
>>> +??????????? Third cell specifies the trigger type as defined
>>> +??????????? in interrupts.txt in this directory.
>>> +??????????? Fourth cell should be 1 if the irq is coming from
>>> +??????????? interrupt aggregator else 0.
>>> +- 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:??? TISCI subtype id representing the host
>>> irqs assigned
>>> +??????????? to this interrupt router.
>>
>> u32 or array?
>
> it is u32.

Sorry, I am wrong here. There is one instance where there are more than
one set of gic irq ranges associated with this IP. Will fix it as an
array in next version.

Thanks and regards,
Lokesh

>
>>
>>> +
>>> +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-controller@1 {
>>
>> Unit-address is not valid here without a reg property.
>
> Sure will fix it in next version.
>
> Thanks and regards,
> Lokesh
>
>>
>>> +??? compatible = "ti,sci-intr";
>>> +??? interrupt-controller;
>>> +??? interrupt-parent = <&gic>;
>>> +??? #interrupt-cells = <4>;
>>> +??? 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 IRQ_TYPE_EDGE_RISING 0>,
>>> +??????????? <57 257 IRQ_TYPE_EDGE_RISING 0>,
>>> +??????????? <57 258 IRQ_TYPE_EDGE_RISING 0>,
>>> +??????????? <57 259 IRQ_TYPE_EDGE_RISING 0>,
>>> +??????????? <57 260 IRQ_TYPE_EDGE_RISING 0>,
>>> +??????????? <57 261 IRQ_TYPE_EDGE_RISING 0>;
>>> +??? ...
>>> +};
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 88b28f6dec45..0e3aa3386287 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -14739,6 +14739,7 @@ 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
>>> ? ? THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
>>> ? M:??? Hans Verkuil <[email protected]>
>>> --?
>>> 2.19.1
>>>
>>