Pull IRQ chip updates from Ingo Molnar: "A late irqchips update: - New TI INTR/INTA set of drivers - Rewrite of the stm32mp1-exti driver as a platform driver - Update the IOMMU MSI mapping API to be RT friendly - A number of cleanups and other low impact fixes" * 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (34 commits) iommu/dma-iommu: Remove iommu_dma_map_msi_msg() irqchip/gic-v3-mbi: Don't map the MSI page in mbi_compose_m{b, s}i_msg() irqchip/ls-scfg-msi: Don't map the MSI page in ls_scfg_msi_compose_msg() irqchip/gic-v3-its: Don't map the MSI page in its_irq_compose_msi_msg() irqchip/gicv2m: Don't map the MSI page in gicv2m_compose_msi_msg() iommu/dma-iommu: Split iommu_dma_map_msi_msg() in two parts genirq/msi: Add a new field in msi_desc to store an IOMMU cookie arm64: arch_k3: Enable interrupt controller drivers irqchip/ti-sci-inta: Add msi domain support soc: ti: Add MSI domain bus support for Interrupt Aggregator irqchip/ti-sci-inta: Add support for Interrupt Aggregator driver dt-bindings: irqchip: Introduce TISCI Interrupt Aggregator bindings irqchip/ti-sci-intr: Add support for Interrupt Router driver dt-bindings: irqchip: Introduce TISCI Interrupt router bindings gpio: thunderx: Use the default parent apis for {request,release}_resources genirq: Introduce irq_chip_{request,release}_resource_parent() apis firmware: ti_sci: Add helper apis to manage resources firmware: ti_sci: Add RM mapping table for am654 firmware: ti_sci: Add support for IRQ management firmware: ti_sci: Add support for RM core ops ...
Unified · Split
| @@ -24,7 +24,8 @@ relationship between the TI-SCI parent node to the child node. · 23 unmodified lines | ||
| 24 | 24 | |
| 25 | 25 | Required properties: |
| 26 | 26 | ------------------- |
| 27 | −- compatible: should be "ti,k2g-sci" | |
| 27 | +- compatible: should be "ti,k2g-sci" for TI 66AK2G SoC | |
| 28 | + should be "ti,am654-sci" for for TI AM654 SoC | |
| 28 | 29 | - mbox-names: |
| 29 | 30 | "rx" - Mailbox corresponding to receive path |
| 30 | 31 | "tx" - Mailbox corresponding to transmit path |
| @@ -0,0 +1,66 @@ | ||
| 1 | +Texas Instruments K3 Interrupt Aggregator | |
| 2 | +========================================= | |
| 3 | + | |
| 4 | +The Interrupt Aggregator (INTA) provides a centralized machine | |
| 5 | +which handles the termination of system events to that they can | |
| 6 | +be coherently processed by the host(s) in the system. A maximum | |
| 7 | +of 64 events can be mapped to a single interrupt. | |
| 8 | + | |
| 9 | + | |
| 10 | + Interrupt Aggregator | |
| 11 | + +-----------------------------------------+ | |
| 12 | + | Intmap VINT | | |
| 13 | + | +--------------+ +------------+ | | |
| 14 | + m ------>| | vint | bit | | 0 |.....|63| vint0 | | |
| 15 | + . | +--------------+ +------------+ | +------+ | |
| 16 | + . | . . | | HOST | | |
| 17 | +Globalevents ------>| . . |------>| IRQ | | |
| 18 | + . | . . | | CTRL | | |
| 19 | + . | . . | +------+ | |
| 20 | + n ------>| +--------------+ +------------+ | | |
| 21 | + | | vint | bit | | 0 |.....|63| vintx | | |
| 22 | + | +--------------+ +------------+ | | |
| 23 | + | | | |
| 24 | + +-----------------------------------------+ | |
| 25 | + | |
| 26 | +Configuration of these Intmap registers that maps global events to vint is done | |
| 27 | +by a system controller (like the Device Memory and Security Controller on K3 | |
| 28 | +AM654 SoC). Driver should request the system controller to get the range | |
| 29 | +of global events and vints assigned to the requesting host. Management | |
| 30 | +of these requested resources should be handled by driver and requests | |
| 31 | +system controller to map specific global event to vint, bit pair. | |
| 32 | + | |
| 33 | +Communication between the host processor running an OS and the system | |
| 34 | +controller happens through a protocol called TI System Control Interface | |
| 35 | +(TISCI protocol). For more details refer: | |
| 36 | +Documentation/devicetree/bindings/arm/keystone/ti,sci.txt | |
| 37 | + | |
| 38 | +TISCI Interrupt Aggregator Node: | |
| 39 | +------------------------------- | |
| 40 | +- compatible: Must be "ti,sci-inta". | |
| 41 | +- reg: Should contain registers location and length. | |
| 42 | +- interrupt-controller: Identifies the node as an interrupt controller | |
| 43 | +- msi-controller: Identifies the node as an MSI controller. | |
| 44 | +- interrupt-parent: phandle of irq parent. | |
| 45 | +- ti,sci: Phandle to TI-SCI compatible System controller node. | |
| 46 | +- ti,sci-dev-id: TISCI device ID of the Interrupt Aggregator. | |
| 47 | +- ti,sci-rm-range-vint: Array of TISCI subtype ids representing vints(inta | |
| 48 | + outputs) range within this INTA, assigned to the | |
| 49 | + requesting host context. | |
| 50 | +- ti,sci-rm-range-global-event: Array of TISCI subtype ids representing the | |
| 51 | + global events range reaching this IA and are assigned | |
| 52 | + to the requesting host context. | |
| 53 | + | |
| 54 | +Example: | |
| 55 | +-------- | |
| 56 | +main_udmass_inta: interrupt-controller@33d00000 { | |
| 57 | + compatible = "ti,sci-inta"; | |
| 58 | + reg = <0x0 0x33d00000 0x0 0x100000>; | |
| 59 | + interrupt-controller; | |
| 60 | + msi-controller; | |
| 61 | + interrupt-parent = <&main_navss_intr>; | |
| 62 | + ti,sci = <&dmsc>; | |
| 63 | + ti,sci-dev-id = <179>; | |
| 64 | + ti,sci-rm-range-vint = <0x0>; | |
| 65 | + ti,sci-rm-range-global-event = <0x1>; | |
| 66 | +}; | |
| @@ -0,0 +1,82 @@ | ||
| 1 | +Texas Instruments K3 Interrupt Router | |
| 2 | +===================================== | |
| 3 | + | |
| 4 | +The Interrupt Router (INTR) module provides a mechanism to mux M | |
| 5 | +interrupt inputs to N interrupt outputs, where all M inputs are selectable | |
| 6 | +to be driven per N output. An Interrupt Router can either handle edge triggered | |
| 7 | +or level triggered interrupts and that is fixed in hardware. | |
| 8 | + | |
| 9 | + Interrupt Router | |
| 10 | + +----------------------+ | |
| 11 | + | Inputs Outputs | | |
| 12 | + +-------+ | +------+ +-----+ | | |
| 13 | + | GPIO |----------->| | irq0 | | 0 | | Host IRQ | |
| 14 | + +-------+ | +------+ +-----+ | controller | |
| 15 | + | . . | +-------+ | |
| 16 | + +-------+ | . . |----->| IRQ | | |
| 17 | + | INTA |----------->| . . | +-------+ | |
| 18 | + +-------+ | . +-----+ | | |
| 19 | + | +------+ | N | | | |
| 20 | + | | irqM | +-----+ | | |
| 21 | + | +------+ | | |
| 22 | + | | | |
| 23 | + +----------------------+ | |
| 24 | + | |
| 25 | +There is one register per output (MUXCNTL_N) that controls the selection. | |
| 26 | +Configuration of these MUXCNTL_N registers is done by a system controller | |
| 27 | +(like the Device Memory and Security Controller on K3 AM654 SoC). System | |
| 28 | +controller will keep track of the used and unused registers within the Router. | |
| 29 | +Driver should request the system controller to get the range of GIC IRQs | |
| 30 | +assigned to the requesting hosts. It is the drivers responsibility to keep | |
| 31 | +track of Host IRQs. | |
| 32 | + | |
| 33 | +Communication between the host processor running an OS and the system | |
| 34 | +controller happens through a protocol called TI System Control Interface | |
| 35 | +(TISCI protocol). For more details refer: | |
| 36 | +Documentation/devicetree/bindings/arm/keystone/ti,sci.txt | |
| 37 | + | |
| 38 | +TISCI Interrupt Router Node: | |
| 39 | +---------------------------- | |
| 40 | +Required Properties: | |
| 41 | +- compatible: Must be "ti,sci-intr". | |
| 42 | +- ti,intr-trigger-type: Should be one of the following: | |
| 43 | + 1: If intr supports edge triggered interrupts. | |
| 44 | + 4: If intr supports level triggered interrupts. | |
| 45 | +- interrupt-controller: Identifies the node as an interrupt controller | |
| 46 | +- #interrupt-cells: Specifies the number of cells needed to encode an | |
| 47 | + interrupt source. The value should be 2. | |
| 48 | + First cell should contain the TISCI device ID of source | |
| 49 | + Second cell should contain the interrupt source offset | |
| 50 | + within the device. | |
| 51 | +- ti,sci: Phandle to TI-SCI compatible System controller node. | |
| 52 | +- ti,sci-dst-id: TISCI device ID of the destination IRQ controller. | |
| 53 | +- ti,sci-rm-range-girq: Array of TISCI subtype ids representing the host irqs | |
| 54 | + assigned to this interrupt router. Each subtype id | |
| 55 | + corresponds to a range of host irqs. | |
| 56 | + | |
| 57 | +For more details on TISCI IRQ resource management refer: | |
| 58 | +http://downloads.ti.com/tisci/esd/latest/2_tisci_msgs/rm/rm_irq.html | |
| 59 | + | |
| 60 | +Example: | |
| 61 | +-------- | |
| 62 | +The following example demonstrates both interrupt router node and the consumer | |
| 63 | +node(main gpio) on the AM654 SoC: | |
| 64 | + | |
| 65 | +main_intr: interrupt-controller0 { | |
| 66 | + compatible = "ti,sci-intr"; | |
| 67 | + ti,intr-trigger-type = <1>; | |
| 68 | + interrupt-controller; | |
| 69 | + interrupt-parent = <&gic500>; | |
| 70 | + #interrupt-cells = <2>; | |
| 71 | + ti,sci = <&dmsc>; | |
| 72 | + ti,sci-dst-id = <56>; | |
| 73 | + ti,sci-rm-range-girq = <0x1>; | |
| 74 | +}; | |
| 75 | + | |
| 76 | +main_gpio0: gpio@600000 { | |
| 77 | + ... | |
| 78 | + interrupt-parent = <&main_intr>; | |
| 79 | + interrupts = <57 256>, <57 257>, <57 258>, | |
| 80 | + <57 259>, <57 260>, <57 261>; | |
| 81 | + ... | |
| 82 | +}; | |
| @@ -15547,6 +15547,12 @@ F: Documentation/devicetree/bindings/reset/ti,sci-reset.txt · 15546 unmodified lines | ||
| 15547 | 15547 | F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt |
| 15548 | 15548 | F: drivers/clk/keystone/sci-clk.c |
| 15549 | 15549 | F: drivers/reset/reset-ti-sci.c |
| 15550 | +F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt | |
| 15551 | +F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt | |
| 15552 | +F: drivers/irqchip/irq-ti-sci-intr.c | |
| 15553 | +F: drivers/irqchip/irq-ti-sci-inta.c | |
| 15554 | +F: include/linux/soc/ti/ti_sci_inta_msi.h | |
| 15555 | +F: drivers/soc/ti/ti_sci_inta_msi.c | |
| 15550 | 15556 | |
| 15551 | 15557 | Texas Instruments ASoC drivers |
| 15552 | 15558 | M: Peter Ujfalusi <peter.ujfalusi@ti.com> |
| @@ -87,6 +87,11 @@ config ARCH_EXYNOS · 86 unmodified lines | ||
| 87 | 87 | config ARCH_K3 |
| 88 | 88 | bool "Texas Instruments Inc. K3 multicore SoC architecture" |
| 89 | 89 | select PM_GENERIC_DOMAINS if PM |
| 90 | + select MAILBOX | |
| 91 | + select TI_MESSAGE_MANAGER | |
| 92 | + select TI_SCI_PROTOCOL | |
| 93 | + select TI_SCI_INTR_IRQCHIP | |
| 94 | + select TI_SCI_INTA_IRQCHIP | |
| 90 | 95 | help |
| 91 | 96 | This enables support for Texas Instruments' K3 multicore SoC |
| 92 | 97 | architecture. |
| @@ -64,6 +64,22 @@ struct ti_sci_xfers_info { · 63 unmodified lines | ||
| 64 | 64 | spinlock_t xfer_lock; |
| 65 | 65 | }; |
| 66 | 66 | |
| 67 | +/** | |
| 68 | + * struct ti_sci_rm_type_map - Structure representing TISCI Resource | |
| 69 | + * management representation of dev_ids. | |
| 70 | + * @dev_id: TISCI device ID | |
| 71 | + * @type: Corresponding id as identified by TISCI RM. | |
| 72 | + * | |
| 73 | + * Note: This is used only as a work around for using RM range apis | |
| 74 | + * for AM654 SoC. For future SoCs dev_id will be used as type | |
| 75 | + * for RM range APIs. In order to maintain ABI backward compatibility | |
| 76 | + * type is not being changed for AM654 SoC. | |
| 77 | + */ | |
| 78 | +struct ti_sci_rm_type_map { | |
| 79 | + u32 dev_id; | |
| 80 | + u16 type; | |
| 81 | +}; | |
| 82 | + | |
| 67 | 83 | /** |
| 68 | 84 | * struct ti_sci_desc - Description of SoC integration |
| 69 | 85 | * @default_host_id: Host identifier representing the compute entity |
| @@ -71,12 +87,14 @@ struct ti_sci_xfers_info { · 1 unmodified line | ||
| 71 | 87 | * @max_msgs: Maximum number of messages that can be pending |
| 72 | 88 | * simultaneously in the system |
| 73 | 89 | * @max_msg_size: Maximum size of data per message that can be handled. |
| 90 | + * @rm_type_map: RM resource type mapping structure. | |
| 74 | 91 | */ |
| 75 | 92 | struct ti_sci_desc { |
| 76 | 93 | u8 default_host_id; |
| 77 | 94 | int max_rx_timeout_ms; |
| 78 | 95 | int max_msgs; |
| 79 | 96 | int max_msg_size; |
| 97 | + struct ti_sci_rm_type_map *rm_type_map; | |
| 80 | 98 | }; |
| 81 | 99 | |
| 82 | 100 | /** |
| @@ -1600,6 +1618,392 @@ fail: · 1517 unmodified lines | ||
| 1600 | 1618 | return ret; |
| 1601 | 1619 | } |
| 1602 | 1620 | |
| 1621 | +static int ti_sci_get_resource_type(struct ti_sci_info *info, u16 dev_id, | |
| 1622 | + u16 *type) | |
| 1623 | +{ | |
| 1624 | + struct ti_sci_rm_type_map *rm_type_map = info->desc->rm_type_map; | |
| 1625 | + bool found = false; | |
| 1626 | + int i; | |
| 1627 | + | |
| 1628 | + /* If map is not provided then assume dev_id is used as type */ | |
| 1629 | + if (!rm_type_map) { | |
| 1630 | + *type = dev_id; | |
| 1631 | + return 0; | |
| 1632 | + } | |
| 1633 | + | |
| 1634 | + for (i = 0; rm_type_map[i].dev_id; i++) { | |
| 1635 | + if (rm_type_map[i].dev_id == dev_id) { | |
| 1636 | + *type = rm_type_map[i].type; | |
| 1637 | + found = true; | |
| 1638 | + break; | |
| 1639 | + } | |
| 1640 | + } | |
| 1641 | + | |
| 1642 | + if (!found) | |
| 1643 | + return -EINVAL; | |
| 1644 | + | |
| 1645 | + return 0; | |
| 1646 | +} | |
| 1647 | + | |
| 1648 | +/** | |
| 1649 | + * ti_sci_get_resource_range - Helper to get a range of resources assigned | |
| 1650 | + * to a host. Resource is uniquely identified by | |
| 1651 | + * type and subtype. | |
| 1652 | + * @handle: Pointer to TISCI handle. | |
| 1653 | + * @dev_id: TISCI device ID. | |
| 1654 | + * @subtype: Resource assignment subtype that is being requested | |
| 1655 | + * from the given device. | |
| 1656 | + * @s_host: Host processor ID to which the resources are allocated | |
| 1657 | + * @range_start: Start index of the resource range | |
| 1658 | + * @range_num: Number of resources in the range | |
| 1659 | + * | |
| 1660 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1661 | + */ | |
| 1662 | +static int ti_sci_get_resource_range(const struct ti_sci_handle *handle, | |
| 1663 | + u32 dev_id, u8 subtype, u8 s_host, | |
| 1664 | + u16 *range_start, u16 *range_num) | |
| 1665 | +{ | |
| 1666 | + struct ti_sci_msg_resp_get_resource_range *resp; | |
| 1667 | + struct ti_sci_msg_req_get_resource_range *req; | |
| 1668 | + struct ti_sci_xfer *xfer; | |
| 1669 | + struct ti_sci_info *info; | |
| 1670 | + struct device *dev; | |
| 1671 | + u16 type; | |
| 1672 | + int ret = 0; | |
| 1673 | + | |
| 1674 | + if (IS_ERR(handle)) | |
| 1675 | + return PTR_ERR(handle); | |
| 1676 | + if (!handle) | |
| 1677 | + return -EINVAL; | |
| 1678 | + | |
| 1679 | + info = handle_to_ti_sci_info(handle); | |
| 1680 | + dev = info->dev; | |
| 1681 | + | |
| 1682 | + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_RESOURCE_RANGE, | |
| 1683 | + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, | |
| 1684 | + sizeof(*req), sizeof(*resp)); | |
| 1685 | + if (IS_ERR(xfer)) { | |
| 1686 | + ret = PTR_ERR(xfer); | |
| 1687 | + dev_err(dev, "Message alloc failed(%d)\n", ret); | |
| 1688 | + return ret; | |
| 1689 | + } | |
| 1690 | + | |
| 1691 | + ret = ti_sci_get_resource_type(info, dev_id, &type); | |
| 1692 | + if (ret) { | |
| 1693 | + dev_err(dev, "rm type lookup failed for %u\n", dev_id); | |
| 1694 | + goto fail; | |
| 1695 | + } | |
| 1696 | + | |
| 1697 | + req = (struct ti_sci_msg_req_get_resource_range *)xfer->xfer_buf; | |
| 1698 | + req->secondary_host = s_host; | |
| 1699 | + req->type = type & MSG_RM_RESOURCE_TYPE_MASK; | |
| 1700 | + req->subtype = subtype & MSG_RM_RESOURCE_SUBTYPE_MASK; | |
| 1701 | + | |
| 1702 | + ret = ti_sci_do_xfer(info, xfer); | |
| 1703 | + if (ret) { | |
| 1704 | + dev_err(dev, "Mbox send fail %d\n", ret); | |
| 1705 | + goto fail; | |
| 1706 | + } | |
| 1707 | + | |
| 1708 | + resp = (struct ti_sci_msg_resp_get_resource_range *)xfer->xfer_buf; | |
| 1709 | + | |
| 1710 | + if (!ti_sci_is_response_ack(resp)) { | |
| 1711 | + ret = -ENODEV; | |
| 1712 | + } else if (!resp->range_start && !resp->range_num) { | |
| 1713 | + ret = -ENODEV; | |
| 1714 | + } else { | |
| 1715 | + *range_start = resp->range_start; | |
| 1716 | + *range_num = resp->range_num; | |
| 1717 | + }; | |
| 1718 | + | |
| 1719 | +fail: | |
| 1720 | + ti_sci_put_one_xfer(&info->minfo, xfer); | |
| 1721 | + | |
| 1722 | + return ret; | |
| 1723 | +} | |
| 1724 | + | |
| 1725 | +/** | |
| 1726 | + * ti_sci_cmd_get_resource_range - Get a range of resources assigned to host | |
| 1727 | + * that is same as ti sci interface host. | |
| 1728 | + * @handle: Pointer to TISCI handle. | |
| 1729 | + * @dev_id: TISCI device ID. | |
| 1730 | + * @subtype: Resource assignment subtype that is being requested | |
| 1731 | + * from the given device. | |
| 1732 | + * @range_start: Start index of the resource range | |
| 1733 | + * @range_num: Number of resources in the range | |
| 1734 | + * | |
| 1735 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1736 | + */ | |
| 1737 | +static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle, | |
| 1738 | + u32 dev_id, u8 subtype, | |
| 1739 | + u16 *range_start, u16 *range_num) | |
| 1740 | +{ | |
| 1741 | + return ti_sci_get_resource_range(handle, dev_id, subtype, | |
| 1742 | + TI_SCI_IRQ_SECONDARY_HOST_INVALID, | |
| 1743 | + range_start, range_num); | |
| 1744 | +} | |
| 1745 | + | |
| 1746 | +/** | |
| 1747 | + * ti_sci_cmd_get_resource_range_from_shost - Get a range of resources | |
| 1748 | + * assigned to a specified host. | |
| 1749 | + * @handle: Pointer to TISCI handle. | |
| 1750 | + * @dev_id: TISCI device ID. | |
| 1751 | + * @subtype: Resource assignment subtype that is being requested | |
| 1752 | + * from the given device. | |
| 1753 | + * @s_host: Host processor ID to which the resources are allocated | |
| 1754 | + * @range_start: Start index of the resource range | |
| 1755 | + * @range_num: Number of resources in the range | |
| 1756 | + * | |
| 1757 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1758 | + */ | |
| 1759 | +static | |
| 1760 | +int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle, | |
| 1761 | + u32 dev_id, u8 subtype, u8 s_host, | |
| 1762 | + u16 *range_start, u16 *range_num) | |
| 1763 | +{ | |
| 1764 | + return ti_sci_get_resource_range(handle, dev_id, subtype, s_host, | |
| 1765 | + range_start, range_num); | |
| 1766 | +} | |
| 1767 | + | |
| 1768 | +/** | |
| 1769 | + * ti_sci_manage_irq() - Helper api to configure/release the irq route between | |
| 1770 | + * the requested source and destination | |
| 1771 | + * @handle: Pointer to TISCI handle. | |
| 1772 | + * @valid_params: Bit fields defining the validity of certain params | |
| 1773 | + * @src_id: Device ID of the IRQ source | |
| 1774 | + * @src_index: IRQ source index within the source device | |
| 1775 | + * @dst_id: Device ID of the IRQ destination | |
| 1776 | + * @dst_host_irq: IRQ number of the destination device | |
| 1777 | + * @ia_id: Device ID of the IA, if the IRQ flows through this IA | |
| 1778 | + * @vint: Virtual interrupt to be used within the IA | |
| 1779 | + * @global_event: Global event number to be used for the requesting event | |
| 1780 | + * @vint_status_bit: Virtual interrupt status bit to be used for the event | |
| 1781 | + * @s_host: Secondary host ID to which the irq/event is being | |
| 1782 | + * requested for. | |
| 1783 | + * @type: Request type irq set or release. | |
| 1784 | + * | |
| 1785 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1786 | + */ | |
| 1787 | +static int ti_sci_manage_irq(const struct ti_sci_handle *handle, | |
| 1788 | + u32 valid_params, u16 src_id, u16 src_index, | |
| 1789 | + u16 dst_id, u16 dst_host_irq, u16 ia_id, u16 vint, | |
| 1790 | + u16 global_event, u8 vint_status_bit, u8 s_host, | |
| 1791 | + u16 type) | |
| 1792 | +{ | |
| 1793 | + struct ti_sci_msg_req_manage_irq *req; | |
| 1794 | + struct ti_sci_msg_hdr *resp; | |
| 1795 | + struct ti_sci_xfer *xfer; | |
| 1796 | + struct ti_sci_info *info; | |
| 1797 | + struct device *dev; | |
| 1798 | + int ret = 0; | |
| 1799 | + | |
| 1800 | + if (IS_ERR(handle)) | |
| 1801 | + return PTR_ERR(handle); | |
| 1802 | + if (!handle) | |
| 1803 | + return -EINVAL; | |
| 1804 | + | |
| 1805 | + info = handle_to_ti_sci_info(handle); | |
| 1806 | + dev = info->dev; | |
| 1807 | + | |
| 1808 | + xfer = ti_sci_get_one_xfer(info, type, TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, | |
| 1809 | + sizeof(*req), sizeof(*resp)); | |
| 1810 | + if (IS_ERR(xfer)) { | |
| 1811 | + ret = PTR_ERR(xfer); | |
| 1812 | + dev_err(dev, "Message alloc failed(%d)\n", ret); | |
| 1813 | + return ret; | |
| 1814 | + } | |
| 1815 | + req = (struct ti_sci_msg_req_manage_irq *)xfer->xfer_buf; | |
| 1816 | + req->valid_params = valid_params; | |
| 1817 | + req->src_id = src_id; | |
| 1818 | + req->src_index = src_index; | |
| 1819 | + req->dst_id = dst_id; | |
| 1820 | + req->dst_host_irq = dst_host_irq; | |
| 1821 | + req->ia_id = ia_id; | |
| 1822 | + req->vint = vint; | |
| 1823 | + req->global_event = global_event; | |
| 1824 | + req->vint_status_bit = vint_status_bit; | |
| 1825 | + req->secondary_host = s_host; | |
| 1826 | + | |
| 1827 | + ret = ti_sci_do_xfer(info, xfer); | |
| 1828 | + if (ret) { | |
| 1829 | + dev_err(dev, "Mbox send fail %d\n", ret); | |
| 1830 | + goto fail; | |
| 1831 | + } | |
| 1832 | + | |
| 1833 | + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; | |
| 1834 | + | |
| 1835 | + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; | |
| 1836 | + | |
| 1837 | +fail: | |
| 1838 | + ti_sci_put_one_xfer(&info->minfo, xfer); | |
| 1839 | + | |
| 1840 | + return ret; | |
| 1841 | +} | |
| 1842 | + | |
| 1843 | +/** | |
| 1844 | + * ti_sci_set_irq() - Helper api to configure the irq route between the | |
| 1845 | + * requested source and destination | |
| 1846 | + * @handle: Pointer to TISCI handle. | |
| 1847 | + * @valid_params: Bit fields defining the validity of certain params | |
| 1848 | + * @src_id: Device ID of the IRQ source | |
| 1849 | + * @src_index: IRQ source index within the source device | |
| 1850 | + * @dst_id: Device ID of the IRQ destination | |
| 1851 | + * @dst_host_irq: IRQ number of the destination device | |
| 1852 | + * @ia_id: Device ID of the IA, if the IRQ flows through this IA | |
| 1853 | + * @vint: Virtual interrupt to be used within the IA | |
| 1854 | + * @global_event: Global event number to be used for the requesting event | |
| 1855 | + * @vint_status_bit: Virtual interrupt status bit to be used for the event | |
| 1856 | + * @s_host: Secondary host ID to which the irq/event is being | |
| 1857 | + * requested for. | |
| 1858 | + * | |
| 1859 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1860 | + */ | |
| 1861 | +static int ti_sci_set_irq(const struct ti_sci_handle *handle, u32 valid_params, | |
| 1862 | + u16 src_id, u16 src_index, u16 dst_id, | |
| 1863 | + u16 dst_host_irq, u16 ia_id, u16 vint, | |
| 1864 | + u16 global_event, u8 vint_status_bit, u8 s_host) | |
| 1865 | +{ | |
| 1866 | + 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", | |
| 1867 | + __func__, valid_params, src_id, src_index, | |
| 1868 | + dst_id, dst_host_irq, ia_id, vint, global_event, | |
| 1869 | + vint_status_bit); | |
| 1870 | + | |
| 1871 | + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, | |
| 1872 | + dst_id, dst_host_irq, ia_id, vint, | |
| 1873 | + global_event, vint_status_bit, s_host, | |
| 1874 | + TI_SCI_MSG_SET_IRQ); | |
| 1875 | +} | |
| 1876 | + | |
| 1877 | +/** | |
| 1878 | + * ti_sci_free_irq() - Helper api to free the irq route between the | |
| 1879 | + * requested source and destination | |
| 1880 | + * @handle: Pointer to TISCI handle. | |
| 1881 | + * @valid_params: Bit fields defining the validity of certain params | |
| 1882 | + * @src_id: Device ID of the IRQ source | |
| 1883 | + * @src_index: IRQ source index within the source device | |
| 1884 | + * @dst_id: Device ID of the IRQ destination | |
| 1885 | + * @dst_host_irq: IRQ number of the destination device | |
| 1886 | + * @ia_id: Device ID of the IA, if the IRQ flows through this IA | |
| 1887 | + * @vint: Virtual interrupt to be used within the IA | |
| 1888 | + * @global_event: Global event number to be used for the requesting event | |
| 1889 | + * @vint_status_bit: Virtual interrupt status bit to be used for the event | |
| 1890 | + * @s_host: Secondary host ID to which the irq/event is being | |
| 1891 | + * requested for. | |
| 1892 | + * | |
| 1893 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1894 | + */ | |
| 1895 | +static int ti_sci_free_irq(const struct ti_sci_handle *handle, u32 valid_params, | |
| 1896 | + u16 src_id, u16 src_index, u16 dst_id, | |
| 1897 | + u16 dst_host_irq, u16 ia_id, u16 vint, | |
| 1898 | + u16 global_event, u8 vint_status_bit, u8 s_host) | |
| 1899 | +{ | |
| 1900 | + 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", | |
| 1901 | + __func__, valid_params, src_id, src_index, | |
| 1902 | + dst_id, dst_host_irq, ia_id, vint, global_event, | |
| 1903 | + vint_status_bit); | |
| 1904 | + | |
| 1905 | + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, | |
| 1906 | + dst_id, dst_host_irq, ia_id, vint, | |
| 1907 | + global_event, vint_status_bit, s_host, | |
| 1908 | + TI_SCI_MSG_FREE_IRQ); | |
| 1909 | +} | |
| 1910 | + | |
| 1911 | +/** | |
| 1912 | + * ti_sci_cmd_set_irq() - Configure a host irq route between the requested | |
| 1913 | + * source and destination. | |
| 1914 | + * @handle: Pointer to TISCI handle. | |
| 1915 | + * @src_id: Device ID of the IRQ source | |
| 1916 | + * @src_index: IRQ source index within the source device | |
| 1917 | + * @dst_id: Device ID of the IRQ destination | |
| 1918 | + * @dst_host_irq: IRQ number of the destination device | |
| 1919 | + * @vint_irq: Boolean specifying if this interrupt belongs to | |
| 1920 | + * Interrupt Aggregator. | |
| 1921 | + * | |
| 1922 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1923 | + */ | |
| 1924 | +static int ti_sci_cmd_set_irq(const struct ti_sci_handle *handle, u16 src_id, | |
| 1925 | + u16 src_index, u16 dst_id, u16 dst_host_irq) | |
| 1926 | +{ | |
| 1927 | + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; | |
| 1928 | + | |
| 1929 | + return ti_sci_set_irq(handle, valid_params, src_id, src_index, dst_id, | |
| 1930 | + dst_host_irq, 0, 0, 0, 0, 0); | |
| 1931 | +} | |
| 1932 | + | |
| 1933 | +/** | |
| 1934 | + * ti_sci_cmd_set_event_map() - Configure an event based irq route between the | |
| 1935 | + * requested source and Interrupt Aggregator. | |
| 1936 | + * @handle: Pointer to TISCI handle. | |
| 1937 | + * @src_id: Device ID of the IRQ source | |
| 1938 | + * @src_index: IRQ source index within the source device | |
| 1939 | + * @ia_id: Device ID of the IA, if the IRQ flows through this IA | |
| 1940 | + * @vint: Virtual interrupt to be used within the IA | |
| 1941 | + * @global_event: Global event number to be used for the requesting event | |
| 1942 | + * @vint_status_bit: Virtual interrupt status bit to be used for the event | |
| 1943 | + * | |
| 1944 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1945 | + */ | |
| 1946 | +static int ti_sci_cmd_set_event_map(const struct ti_sci_handle *handle, | |
| 1947 | + u16 src_id, u16 src_index, u16 ia_id, | |
| 1948 | + u16 vint, u16 global_event, | |
| 1949 | + u8 vint_status_bit) | |
| 1950 | +{ | |
| 1951 | + u32 valid_params = MSG_FLAG_IA_ID_VALID | MSG_FLAG_VINT_VALID | | |
| 1952 | + MSG_FLAG_GLB_EVNT_VALID | | |
| 1953 | + MSG_FLAG_VINT_STS_BIT_VALID; | |
| 1954 | + | |
| 1955 | + return ti_sci_set_irq(handle, valid_params, src_id, src_index, 0, 0, | |
| 1956 | + ia_id, vint, global_event, vint_status_bit, 0); | |
| 1957 | +} | |
| 1958 | + | |
| 1959 | +/** | |
| 1960 | + * ti_sci_cmd_free_irq() - Free a host irq route between the between the | |
| 1961 | + * requested source and destination. | |
| 1962 | + * @handle: Pointer to TISCI handle. | |
| 1963 | + * @src_id: Device ID of the IRQ source | |
| 1964 | + * @src_index: IRQ source index within the source device | |
| 1965 | + * @dst_id: Device ID of the IRQ destination | |
| 1966 | + * @dst_host_irq: IRQ number of the destination device | |
| 1967 | + * @vint_irq: Boolean specifying if this interrupt belongs to | |
| 1968 | + * Interrupt Aggregator. | |
| 1969 | + * | |
| 1970 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1971 | + */ | |
| 1972 | +static int ti_sci_cmd_free_irq(const struct ti_sci_handle *handle, u16 src_id, | |
| 1973 | + u16 src_index, u16 dst_id, u16 dst_host_irq) | |
| 1974 | +{ | |
| 1975 | + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; | |
| 1976 | + | |
| 1977 | + return ti_sci_free_irq(handle, valid_params, src_id, src_index, dst_id, | |
| 1978 | + dst_host_irq, 0, 0, 0, 0, 0); | |
| 1979 | +} | |
| 1980 | + | |
| 1981 | +/** | |
| 1982 | + * ti_sci_cmd_free_event_map() - Free an event map between the requested source | |
| 1983 | + * and Interrupt Aggregator. | |
| 1984 | + * @handle: Pointer to TISCI handle. | |
| 1985 | + * @src_id: Device ID of the IRQ source | |
| 1986 | + * @src_index: IRQ source index within the source device | |
| 1987 | + * @ia_id: Device ID of the IA, if the IRQ flows through this IA | |
| 1988 | + * @vint: Virtual interrupt to be used within the IA | |
| 1989 | + * @global_event: Global event number to be used for the requesting event | |
| 1990 | + * @vint_status_bit: Virtual interrupt status bit to be used for the event | |
| 1991 | + * | |
| 1992 | + * Return: 0 if all went fine, else return appropriate error. | |
| 1993 | + */ | |
| 1994 | +static int ti_sci_cmd_free_event_map(const struct ti_sci_handle *handle, | |
| 1995 | + u16 src_id, u16 src_index, u16 ia_id, | |
| 1996 | + u16 vint, u16 global_event, | |
| 1997 | + u8 vint_status_bit) | |
| 1998 | +{ | |
| 1999 | + u32 valid_params = MSG_FLAG_IA_ID_VALID | | |
| 2000 | + MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID | | |
| 2001 | + MSG_FLAG_VINT_STS_BIT_VALID; | |
| 2002 | + | |
| 2003 | + return ti_sci_free_irq(handle, valid_params, src_id, src_index, 0, 0, | |
| 2004 | + ia_id, vint, global_event, vint_status_bit, 0); | |
| 2005 | +} | |
| 2006 | + | |
| 1603 | 2007 | /* |
| 1604 | 2008 | * ti_sci_setup_ops() - Setup the operations structures |
| 1605 | 2009 | * @info: pointer to TISCI pointer |
| @@ -1610,6 +2014,8 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) · 4 unmodified lines | ||
| 1610 | 2014 | struct ti_sci_core_ops *core_ops = &ops->core_ops; |
| 1611 | 2015 | struct ti_sci_dev_ops *dops = &ops->dev_ops; |
| 1612 | 2016 | struct ti_sci_clk_ops *cops = &ops->clk_ops; |
| 2017 | + struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops; | |
| 2018 | + struct ti_sci_rm_irq_ops *iops = &ops->rm_irq_ops; | |
| 1613 | 2019 | |
| 1614 | 2020 | core_ops->reboot_device = ti_sci_cmd_core_reboot; |
| 1615 | 2021 | |
| @@ -1640,6 +2046,15 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) · 24 unmodified lines | ||
| 1640 | 2046 | cops->get_best_match_freq = ti_sci_cmd_clk_get_match_freq; |
| 1641 | 2047 | cops->set_freq = ti_sci_cmd_clk_set_freq; |
| 1642 | 2048 | cops->get_freq = ti_sci_cmd_clk_get_freq; |
| 2049 | + | |
| 2050 | + rm_core_ops->get_range = ti_sci_cmd_get_resource_range; | |
| 2051 | + rm_core_ops->get_range_from_shost = | |
| 2052 | + ti_sci_cmd_get_resource_range_from_shost; | |
| 2053 | + | |
| 2054 | + iops->set_irq = ti_sci_cmd_set_irq; | |
| 2055 | + iops->set_event_map = ti_sci_cmd_set_event_map; | |
| 2056 | + iops->free_irq = ti_sci_cmd_free_irq; | |
| 2057 | + iops->free_event_map = ti_sci_cmd_free_event_map; | |
| 1643 | 2058 | } |
| 1644 | 2059 | |
| 1645 | 2060 | /** |
| @@ -1764,6 +2179,219 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev) · 118 unmodified lines | ||
| 1764 | 2179 | } |
| 1765 | 2180 | EXPORT_SYMBOL_GPL(devm_ti_sci_get_handle); |
| 1766 | 2181 | |
| 2182 | +/** | |
| 2183 | + * ti_sci_get_by_phandle() - Get the TI SCI handle using DT phandle | |
| 2184 | + * @np: device node | |
| 2185 | + * @property: property name containing phandle on TISCI node | |
| 2186 | + * | |
| 2187 | + * NOTE: The function does not track individual clients of the framework | |
| 2188 | + * and is expected to be maintained by caller of TI SCI protocol library. | |
| 2189 | + * ti_sci_put_handle must be balanced with successful ti_sci_get_by_phandle | |
| 2190 | + * Return: pointer to handle if successful, else: | |
| 2191 | + * -EPROBE_DEFER if the instance is not ready | |
| 2192 | + * -ENODEV if the required node handler is missing | |
| 2193 | + * -EINVAL if invalid conditions are encountered. | |
| 2194 | + */ | |
| 2195 | +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, | |
| 2196 | + const char *property) | |
| 2197 | +{ | |
| 2198 | + struct ti_sci_handle *handle = NULL; | |
| 2199 | + struct device_node *ti_sci_np; | |
| 2200 | + struct ti_sci_info *info; | |
| 2201 | + struct list_head *p; | |
| 2202 | + | |
| 2203 | + if (!np) { | |
| 2204 | + pr_err("I need a device pointer\n"); | |
| 2205 | + return ERR_PTR(-EINVAL); | |
| 2206 | + } | |
| 2207 | + | |
| 2208 | + ti_sci_np = of_parse_phandle(np, property, 0); | |
| 2209 | + if (!ti_sci_np) | |
| 2210 | + return ERR_PTR(-ENODEV); | |
| 2211 | + | |
| 2212 | + mutex_lock(&ti_sci_list_mutex); | |
| 2213 | + list_for_each(p, &ti_sci_list) { | |
| 2214 | + info = list_entry(p, struct ti_sci_info, node); | |
| 2215 | + if (ti_sci_np == info->dev->of_node) { | |
| 2216 | + handle = &info->handle; | |
| 2217 | + info->users++; | |
| 2218 | + break; | |
| 2219 | + } | |
| 2220 | + } | |
| 2221 | + mutex_unlock(&ti_sci_list_mutex); | |
| 2222 | + of_node_put(ti_sci_np); | |
| 2223 | + | |
| 2224 | + if (!handle) | |
| 2225 | + return ERR_PTR(-EPROBE_DEFER); | |
| 2226 | + | |
| 2227 | + return handle; | |
| 2228 | +} | |
| 2229 | +EXPORT_SYMBOL_GPL(ti_sci_get_by_phandle); | |
| 2230 | + | |
| 2231 | +/** | |
| 2232 | + * devm_ti_sci_get_by_phandle() - Managed get handle using phandle | |
| 2233 | + * @dev: Device pointer requesting TISCI handle | |
| 2234 | + * @property: property name containing phandle on TISCI node | |
| 2235 | + * | |
| 2236 | + * NOTE: This releases the handle once the device resources are | |
| 2237 | + * no longer needed. MUST NOT BE released with ti_sci_put_handle. | |
| 2238 | + * The function does not track individual clients of the framework | |
| 2239 | + * and is expected to be maintained by caller of TI SCI protocol library. | |
| 2240 | + * | |
| 2241 | + * Return: 0 if all went fine, else corresponding error. | |
| 2242 | + */ | |
| 2243 | +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, | |
| 2244 | + const char *property) | |
| 2245 | +{ | |
| 2246 | + const struct ti_sci_handle *handle; | |
| 2247 | + const struct ti_sci_handle **ptr; | |
| 2248 | + | |
| 2249 | + ptr = devres_alloc(devm_ti_sci_release, sizeof(*ptr), GFP_KERNEL); | |
| 2250 | + if (!ptr) | |
| 2251 | + return ERR_PTR(-ENOMEM); | |
| 2252 | + handle = ti_sci_get_by_phandle(dev_of_node(dev), property); | |
| 2253 | + | |
| 2254 | + if (!IS_ERR(handle)) { | |
| 2255 | + *ptr = handle; | |
| 2256 | + devres_add(dev, ptr); | |
| 2257 | + } else { | |
| 2258 | + devres_free(ptr); | |
| 2259 | + } | |
| 2260 | + | |
| 2261 | + return handle; | |
| 2262 | +} | |
| 2263 | +EXPORT_SYMBOL_GPL(devm_ti_sci_get_by_phandle); | |
| 2264 | + | |
| 2265 | +/** | |
| 2266 | + * ti_sci_get_free_resource() - Get a free resource from TISCI resource. | |
| 2267 | + * @res: Pointer to the TISCI resource | |
| 2268 | + * | |
| 2269 | + * Return: resource num if all went ok else TI_SCI_RESOURCE_NULL. | |
| 2270 | + */ | |
| 2271 | +u16 ti_sci_get_free_resource(struct ti_sci_resource *res) | |
| 2272 | +{ | |
| 2273 | + unsigned long flags; | |
| 2274 | + u16 set, free_bit; | |
| 2275 | + | |
| 2276 | + raw_spin_lock_irqsave(&res->lock, flags); | |
| 2277 | + for (set = 0; set < res->sets; set++) { | |
| 2278 | + free_bit = find_first_zero_bit(res->desc[set].res_map, | |
| 2279 | + res->desc[set].num); | |
| 2280 | + if (free_bit != res->desc[set].num) { | |
| 2281 | + set_bit(free_bit, res->desc[set].res_map); | |
| 2282 | + raw_spin_unlock_irqrestore(&res->lock, flags); | |
| 2283 | + return res->desc[set].start + free_bit; | |
| 2284 | + } | |
| 2285 | + } | |
| 2286 | + raw_spin_unlock_irqrestore(&res->lock, flags); | |
| 2287 | + | |
| 2288 | + return TI_SCI_RESOURCE_NULL; | |
| 2289 | +} | |
| 2290 | +EXPORT_SYMBOL_GPL(ti_sci_get_free_resource); | |
| 2291 | + | |
| 2292 | +/** | |
| 2293 | + * ti_sci_release_resource() - Release a resource from TISCI resource. | |
| 2294 | + * @res: Pointer to the TISCI resource | |
| 2295 | + * @id: Resource id to be released. | |
| 2296 | + */ | |
| 2297 | +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) | |
| 2298 | +{ | |
| 2299 | + unsigned long flags; | |
| 2300 | + u16 set; | |
| 2301 | + | |
| 2302 | + raw_spin_lock_irqsave(&res->lock, flags); | |
| 2303 | + for (set = 0; set < res->sets; set++) { | |
| 2304 | + if (res->desc[set].start <= id && | |
| 2305 | + (res->desc[set].num + res->desc[set].start) > id) | |
| 2306 | + clear_bit(id - res->desc[set].start, | |
| 2307 | + res->desc[set].res_map); | |
| 2308 | + } | |
| 2309 | + raw_spin_unlock_irqrestore(&res->lock, flags); | |
| 2310 | +} | |
| 2311 | +EXPORT_SYMBOL_GPL(ti_sci_release_resource); | |
| 2312 | + | |
| 2313 | +/** | |
| 2314 | + * ti_sci_get_num_resources() - Get the number of resources in TISCI resource | |
| 2315 | + * @res: Pointer to the TISCI resource | |
| 2316 | + * | |
| 2317 | + * Return: Total number of available resources. | |
| 2318 | + */ | |
| 2319 | +u32 ti_sci_get_num_resources(struct ti_sci_resource *res) | |
| 2320 | +{ | |
| 2321 | + u32 set, count = 0; | |
| 2322 | + | |
| 2323 | + for (set = 0; set < res->sets; set++) | |
| 2324 | + count += res->desc[set].num; | |
| 2325 | + | |
| 2326 | + return count; | |
| 2327 | +} | |
| 2328 | +EXPORT_SYMBOL_GPL(ti_sci_get_num_resources); | |
| 2329 | + | |
| 2330 | +/** | |
| 2331 | + * devm_ti_sci_get_of_resource() - Get a TISCI resource assigned to a device | |
| 2332 | + * @handle: TISCI handle | |
| 2333 | + * @dev: Device pointer to which the resource is assigned | |
| 2334 | + * @dev_id: TISCI device id to which the resource is assigned | |
| 2335 | + * @of_prop: property name by which the resource are represented | |
| 2336 | + * | |
| 2337 | + * Return: Pointer to ti_sci_resource if all went well else appropriate | |
| 2338 | + * error pointer. | |
| 2339 | + */ | |
| 2340 | +struct ti_sci_resource * | |
| 2341 | +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, | |
| 2342 | + struct device *dev, u32 dev_id, char *of_prop) | |
| 2343 | +{ | |
| 2344 | + struct ti_sci_resource *res; | |
| 2345 | + u32 resource_subtype; | |
| 2346 | + int i, ret; | |
| 2347 | + | |
| 2348 | + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); | |
| 2349 | + if (!res) | |
| 2350 | + return ERR_PTR(-ENOMEM); | |
| 2351 | + | |
| 2352 | + res->sets = of_property_count_elems_of_size(dev_of_node(dev), of_prop, | |
| 2353 | + sizeof(u32)); | |
| 2354 | + if (res->sets < 0) { | |
| 2355 | + dev_err(dev, "%s resource type ids not available\n", of_prop); | |
| 2356 | + return ERR_PTR(res->sets); | |
| 2357 | + } | |
| 2358 | + | |
| 2359 | + res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc), | |
| 2360 | + GFP_KERNEL); | |
| 2361 | + if (!res->desc) | |
| 2362 | + return ERR_PTR(-ENOMEM); | |
| 2363 | + | |
| 2364 | + for (i = 0; i < res->sets; i++) { | |
| 2365 | + ret = of_property_read_u32_index(dev_of_node(dev), of_prop, i, | |
| 2366 | + &resource_subtype); | |
| 2367 | + if (ret) | |
| 2368 | + return ERR_PTR(-EINVAL); | |
| 2369 | + | |
| 2370 | + ret = handle->ops.rm_core_ops.get_range(handle, dev_id, | |
| 2371 | + resource_subtype, | |
| 2372 | + &res->desc[i].start, | |
| 2373 | + &res->desc[i].num); | |
| 2374 | + if (ret) { | |
| 2375 | + dev_err(dev, "dev = %d subtype %d not allocated for this host\n", | |
| 2376 | + dev_id, resource_subtype); | |
| 2377 | + return ERR_PTR(ret); | |
| 2378 | + } | |
| 2379 | + | |
| 2380 | + dev_dbg(dev, "dev = %d, subtype = %d, start = %d, num = %d\n", | |
| 2381 | + dev_id, resource_subtype, res->desc[i].start, | |
| 2382 | + res->desc[i].num); | |
| 2383 | + | |
| 2384 | + res->desc[i].res_map = | |
| 2385 | + devm_kzalloc(dev, BITS_TO_LONGS(res->desc[i].num) * | |
| 2386 | + sizeof(*res->desc[i].res_map), GFP_KERNEL); | |
| 2387 | + if (!res->desc[i].res_map) | |
| 2388 | + return ERR_PTR(-ENOMEM); | |
| 2389 | + } | |
| 2390 | + raw_spin_lock_init(&res->lock); | |
| 2391 | + | |
| 2392 | + return res; | |
| 2393 | +} | |
| 2394 | + | |
| 1767 | 2395 | static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, |
| 1768 | 2396 | void *cmd) |
| 1769 | 2397 | { |
| @@ -1784,10 +2412,33 @@ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { · 14 unmodified lines | ||
| 1784 | 2412 | /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ |
| 1785 | 2413 | .max_msgs = 20, |
| 1786 | 2414 | .max_msg_size = 64, |
| 2415 | + .rm_type_map = NULL, | |
| 2416 | +}; | |
| 2417 | + | |
| 2418 | +static struct ti_sci_rm_type_map ti_sci_am654_rm_type_map[] = { | |
| 2419 | + {.dev_id = 56, .type = 0x00b}, /* GIC_IRQ */ | |
| 2420 | + {.dev_id = 179, .type = 0x000}, /* MAIN_NAV_UDMASS_IA0 */ | |
| 2421 | + {.dev_id = 187, .type = 0x009}, /* MAIN_NAV_RA */ | |
| 2422 | + {.dev_id = 188, .type = 0x006}, /* MAIN_NAV_UDMAP */ | |
| 2423 | + {.dev_id = 194, .type = 0x007}, /* MCU_NAV_UDMAP */ | |
| 2424 | + {.dev_id = 195, .type = 0x00a}, /* MCU_NAV_RA */ | |
| 2425 | + {.dev_id = 0, .type = 0x000}, /* end of table */ | |
| 2426 | +}; | |
| 2427 | + | |
| 2428 | +/* Description for AM654 */ | |
| 2429 | +static const struct ti_sci_desc ti_sci_pmmc_am654_desc = { | |
| 2430 | + .default_host_id = 12, | |
| 2431 | + /* Conservative duration */ | |
| 2432 | + .max_rx_timeout_ms = 10000, | |
| 2433 | + /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ | |
| 2434 | + .max_msgs = 20, | |
| 2435 | + .max_msg_size = 60, | |
| 2436 | + .rm_type_map = ti_sci_am654_rm_type_map, | |
| 1787 | 2437 | }; |
| 1788 | 2438 | |
| 1789 | 2439 | static const struct of_device_id ti_sci_of_match[] = { |
| 1790 | 2440 | {.compatible = "ti,k2g-sci", .data = &ti_sci_pmmc_k2g_desc}, |
| 2441 | + {.compatible = "ti,am654-sci", .data = &ti_sci_pmmc_am654_desc}, | |
| 1791 | 2442 | { /* Sentinel */ }, |
| 1792 | 2443 | }; |
| 1793 | 2444 | MODULE_DEVICE_TABLE(of, ti_sci_of_match); |
| @@ -35,6 +35,13 @@ · 34 unmodified lines | ||
| 35 | 35 | #define TI_SCI_MSG_QUERY_CLOCK_FREQ 0x010d |
| 36 | 36 | #define TI_SCI_MSG_GET_CLOCK_FREQ 0x010e |
| 37 | 37 | |
| 38 | +/* Resource Management Requests */ | |
| 39 | +#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500 | |
| 40 | + | |
| 41 | +/* IRQ requests */ | |
| 42 | +#define TI_SCI_MSG_SET_IRQ 0x1000 | |
| 43 | +#define TI_SCI_MSG_FREE_IRQ 0x1001 | |
| 44 | + | |
| 38 | 45 | /** |
| 39 | 46 | * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses |
| 40 | 47 | * @type: Type of messages: One of TI_SCI_MSG* values |
| @@ -461,4 +468,99 @@ struct ti_sci_msg_resp_get_clock_freq { · 420 unmodified lines | ||
| 461 | 468 | u64 freq_hz; |
| 462 | 469 | } __packed; |
| 463 | 470 | |
| 471 | +#define TI_SCI_IRQ_SECONDARY_HOST_INVALID 0xff | |
| 472 | + | |
| 473 | +/** | |
| 474 | + * struct ti_sci_msg_req_get_resource_range - Request to get a host's assigned | |
| 475 | + * range of resources. | |
| 476 | + * @hdr: Generic Header | |
| 477 | + * @type: Unique resource assignment type | |
| 478 | + * @subtype: Resource assignment subtype within the resource type. | |
| 479 | + * @secondary_host: Host processing entity to which the resources are | |
| 480 | + * allocated. This is required only when the destination | |
| 481 | + * host id id different from ti sci interface host id, | |
| 482 | + * else TI_SCI_IRQ_SECONDARY_HOST_INVALID can be passed. | |
| 483 | + * | |
| 484 | + * Request type is TI_SCI_MSG_GET_RESOURCE_RANGE. Responded with requested | |
| 485 | + * resource range which is of type TI_SCI_MSG_GET_RESOURCE_RANGE. | |
| 486 | + */ | |
| 487 | +struct ti_sci_msg_req_get_resource_range { | |
| 488 | + struct ti_sci_msg_hdr hdr; | |
| 489 | +#define MSG_RM_RESOURCE_TYPE_MASK GENMASK(9, 0) | |
| 490 | +#define MSG_RM_RESOURCE_SUBTYPE_MASK GENMASK(5, 0) | |
| 491 | + u16 type; | |
| 492 | + u8 subtype; | |
| 493 | + u8 secondary_host; | |
| 494 | +} __packed; | |
| 495 | + | |
| 496 | +/** | |
| 497 | + * struct ti_sci_msg_resp_get_resource_range - Response to resource get range. | |
| 498 | + * @hdr: Generic Header | |
| 499 | + * @range_start: Start index of the resource range. | |
| 500 | + * @range_num: Number of resources in the range. | |
| 501 | + * | |
| 502 | + * Response to request TI_SCI_MSG_GET_RESOURCE_RANGE. | |
| 503 | + */ | |
| 504 | +struct ti_sci_msg_resp_get_resource_range { | |
| 505 | + struct ti_sci_msg_hdr hdr; | |
| 506 | + u16 range_start; | |
| 507 | + u16 range_num; | |
| 508 | +} __packed; | |
| 509 | + | |
| 510 | +/** | |
| 511 | + * struct ti_sci_msg_req_manage_irq - Request to configure/release the route | |
| 512 | + * between the dev and the host. | |
| 513 | + * @hdr: Generic Header | |
| 514 | + * @valid_params: Bit fields defining the validity of interrupt source | |
| 515 | + * parameters. If a bit is not set, then corresponding | |
| 516 | + * field is not valid and will not be used for route set. | |
| 517 | + * Bit field definitions: | |
| 518 | + * 0 - Valid bit for @dst_id | |
| 519 | + * 1 - Valid bit for @dst_host_irq | |
| 520 | + * 2 - Valid bit for @ia_id | |
| 521 | + * 3 - Valid bit for @vint | |
| 522 | + * 4 - Valid bit for @global_event | |
| 523 | + * 5 - Valid bit for @vint_status_bit_index | |
| 524 | + * 31 - Valid bit for @secondary_host | |
| 525 | + * @src_id: IRQ source peripheral ID. | |
| 526 | + * @src_index: IRQ source index within the peripheral | |
| 527 | + * @dst_id: IRQ Destination ID. Based on the architecture it can be | |
| 528 | + * IRQ controller or host processor ID. | |
| 529 | + * @dst_host_irq: IRQ number of the destination host IRQ controller | |
| 530 | + * @ia_id: Device ID of the interrupt aggregator in which the | |
| 531 | + * vint resides. | |
| 532 | + * @vint: Virtual interrupt number if the interrupt route | |
| 533 | + * is through an interrupt aggregator. | |
| 534 | + * @global_event: Global event that is to be mapped to interrupt | |
| 535 | + * aggregator virtual interrupt status bit. | |
| 536 | + * @vint_status_bit: Virtual interrupt status bit if the interrupt route | |
| 537 | + * utilizes an interrupt aggregator status bit. | |
| 538 | + * @secondary_host: Host ID of the IRQ destination computing entity. This is | |
| 539 | + * required only when destination host id is different | |
| 540 | + * from ti sci interface host id. | |
| 541 | + * | |
| 542 | + * Request type is TI_SCI_MSG_SET/RELEASE_IRQ. | |
| 543 | + * Response is generic ACK / NACK message. | |
| 544 | + */ | |
| 545 | +struct ti_sci_msg_req_manage_irq { | |
| 546 | + struct ti_sci_msg_hdr hdr; | |
| 547 | +#define MSG_FLAG_DST_ID_VALID TI_SCI_MSG_FLAG(0) | |
| 548 | +#define MSG_FLAG_DST_HOST_IRQ_VALID TI_SCI_MSG_FLAG(1) | |
| 549 | +#define MSG_FLAG_IA_ID_VALID TI_SCI_MSG_FLAG(2) | |
| 550 | +#define MSG_FLAG_VINT_VALID TI_SCI_MSG_FLAG(3) | |
| 551 | +#define MSG_FLAG_GLB_EVNT_VALID TI_SCI_MSG_FLAG(4) | |
| 552 | +#define MSG_FLAG_VINT_STS_BIT_VALID TI_SCI_MSG_FLAG(5) | |
| 553 | +#define MSG_FLAG_SHOST_VALID TI_SCI_MSG_FLAG(31) | |
| 554 | + u32 valid_params; | |
| 555 | + u16 src_id; | |
| 556 | + u16 src_index; | |
| 557 | + u16 dst_id; | |
| 558 | + u16 dst_host_irq; | |
| 559 | + u16 ia_id; | |
| 560 | + u16 vint; | |
| 561 | + u16 global_event; | |
| 562 | + u8 vint_status_bit; | |
| 563 | + u8 secondary_host; | |
| 564 | +} __packed; | |
| 565 | + | |
| 464 | 566 | #endif /* __TI_SCI_H */ |
| @@ -363,22 +363,16 @@ static int thunderx_gpio_irq_request_resources(struct irq_data *data) · 362 unmodified lines | ||
| 363 | 363 | { |
| 364 | 364 | struct thunderx_line *txline = irq_data_get_irq_chip_data(data); |
| 365 | 365 | struct thunderx_gpio *txgpio = txline->txgpio; |
| 366 | − struct irq_data *parent_data = data->parent_data; | |
| 367 | 366 | int r; |
| 368 | 367 | |
| 369 | 368 | r = gpiochip_lock_as_irq(&txgpio->chip, txline->line); |
| 370 | 369 | if (r) |
| 371 | 370 | return r; |
| 372 | 371 | |
| 373 | − if (parent_data && parent_data->chip->irq_request_resources) { | |
| 374 | − r = parent_data->chip->irq_request_resources(parent_data); | |
| 375 | − if (r) | |
| 376 | − goto error; | |
| 377 | − } | |
| 372 | + r = irq_chip_request_resources_parent(data); | |
| 373 | + if (r) | |
| 374 | + gpiochip_unlock_as_irq(&txgpio->chip, txline->line); | |
| 378 | 375 | |
| 379 | − return 0; | |
| 380 | −error: | |
| 381 | − gpiochip_unlock_as_irq(&txgpio->chip, txline->line); | |
| 382 | 376 | return r; |
| 383 | 377 | } |
| 384 | 378 | |
| @@ -386,10 +380,8 @@ static void thunderx_gpio_irq_release_resources(struct irq_data *data) · 1 unmodified line | ||
| 386 | 380 | { |
| 387 | 381 | struct thunderx_line *txline = irq_data_get_irq_chip_data(data); |
| 388 | 382 | struct thunderx_gpio *txgpio = txline->txgpio; |
| 389 | − struct irq_data *parent_data = data->parent_data; | |
| 390 | 383 | |
| 391 | − if (parent_data && parent_data->chip->irq_release_resources) | |
| 392 | − parent_data->chip->irq_release_resources(parent_data); | |
| 384 | + irq_chip_release_resources_parent(data); | |
| 393 | 385 | |
| 394 | 386 | gpiochip_unlock_as_irq(&txgpio->chip, txline->line); |
| 395 | 387 | } |
| @@ -94,6 +94,7 @@ config IOMMU_DMA · 93 unmodified lines | ||
| 94 | 94 | bool |
| 95 | 95 | select IOMMU_API |
| 96 | 96 | select IOMMU_IOVA |
| 97 | + select IRQ_MSI_IOMMU | |
| 97 | 98 | select NEED_SG_DMA_LENGTH |
| 98 | 99 | |
| 99 | 100 | config FSL_PAMU |
| @@ -907,17 +907,18 @@ out_free_page: · 906 unmodified lines | ||
| 907 | 907 | return NULL; |
| 908 | 908 | } |
| 909 | 909 | |
| 910 | −void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) | |
| 910 | +int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr) | |
| 911 | 911 | { |
| 912 | − struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq)); | |
| 912 | + struct device *dev = msi_desc_to_dev(desc); | |
| 913 | 913 | struct iommu_domain *domain = iommu_get_domain_for_dev(dev); |
| 914 | 914 | struct iommu_dma_cookie *cookie; |
| 915 | 915 | struct iommu_dma_msi_page *msi_page; |
| 916 | − phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo; | |
| 917 | 916 | unsigned long flags; |
| 918 | 917 | |
| 919 | − if (!domain || !domain->iova_cookie) | |
| 920 | − return; | |
| 918 | + if (!domain || !domain->iova_cookie) { | |
| 919 | + desc->iommu_cookie = NULL; | |
| 920 | + return 0; | |
| 921 | + } | |
| 921 | 922 | |
| 922 | 923 | cookie = domain->iova_cookie; |
| 923 | 924 | |
| @@ -930,19 +931,26 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) · 6 unmodified lines | ||
| 930 | 931 | msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain); |
| 931 | 932 | spin_unlock_irqrestore(&cookie->msi_lock, flags); |
| 932 | 933 | |
| 933 | − if (WARN_ON(!msi_page)) { | |
| 934 | − /* | |
| 935 | − * We're called from a void callback, so the best we can do is | |
| 936 | − * 'fail' by filling the message with obviously bogus values. | |
| 937 | − * Since we got this far due to an IOMMU being present, it's | |
| 938 | − * not like the existing address would have worked anyway... | |
| 939 | − */ | |
| 940 | − msg->address_hi = ~0U; | |
| 941 | − msg->address_lo = ~0U; | |
| 942 | − msg->data = ~0U; | |
| 943 | − } else { | |
| 944 | − msg->address_hi = upper_32_bits(msi_page->iova); | |
| 945 | − msg->address_lo &= cookie_msi_granule(cookie) - 1; | |
| 946 | − msg->address_lo += lower_32_bits(msi_page->iova); | |
| 947 | − } | |
| 934 | + msi_desc_set_iommu_cookie(desc, msi_page); | |
| 935 | + | |
| 936 | + if (!msi_page) | |
| 937 | + return -ENOMEM; | |
| 938 | + return 0; | |
| 939 | +} | |
| 940 | + | |
| 941 | +void iommu_dma_compose_msi_msg(struct msi_desc *desc, | |
| 942 | + struct msi_msg *msg) | |
| 943 | +{ | |
| 944 | + struct device *dev = msi_desc_to_dev(desc); | |
| 945 | + const struct iommu_domain *domain = iommu_get_domain_for_dev(dev); | |
| 946 | + const struct iommu_dma_msi_page *msi_page; | |
| 947 | + | |
| 948 | + msi_page = msi_desc_get_iommu_cookie(desc); | |
| 949 | + | |
| 950 | + if (!domain || !domain->iova_cookie || WARN_ON(!msi_page)) | |
| 951 | + return; | |
| 952 | + | |
| 953 | + msg->address_hi = upper_32_bits(msi_page->iova); | |
| 954 | + msg->address_lo &= cookie_msi_granule(domain->iova_cookie) - 1; | |
| 955 | + msg->address_lo += lower_32_bits(msi_page->iova); | |
| 948 | 956 | } |
| @@ -6,7 +6,6 @@ config IRQCHIP · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | config ARM_GIC |
| 8 | 8 | bool |
| 9 | − select IRQ_DOMAIN | |
| 10 | 9 | select IRQ_DOMAIN_HIERARCHY |
| 11 | 10 | select GENERIC_IRQ_MULTI_HANDLER |
| 12 | 11 | select GENERIC_IRQ_EFFECTIVE_AFF_MASK |
| @@ -33,7 +32,6 @@ config GIC_NON_BANKED · 20 unmodified lines | ||
| 33 | 32 | |
| 34 | 33 | config ARM_GIC_V3 |
| 35 | 34 | bool |
| 36 | − select IRQ_DOMAIN | |
| 37 | 35 | select GENERIC_IRQ_MULTI_HANDLER |
| 38 | 36 | select IRQ_DOMAIN_HIERARCHY |
| 39 | 37 | select PARTITION_PERCPU |
| @@ -59,7 +57,6 @@ config ARM_GIC_V3_ITS_FSL_MC · 19 unmodified lines | ||
| 59 | 57 | |
| 60 | 58 | config ARM_NVIC |
| 61 | 59 | bool |
| 62 | − select IRQ_DOMAIN | |
| 63 | 60 | select IRQ_DOMAIN_HIERARCHY |
| 64 | 61 | select GENERIC_IRQ_CHIP |
| 65 | 62 | |
| @@ -358,7 +355,6 @@ config STM32_EXTI · 292 unmodified lines | ||
| 358 | 355 | config QCOM_IRQ_COMBINER |
| 359 | 356 | bool "QCOM IRQ combiner support" |
| 360 | 357 | depends on ARCH_QCOM && ACPI |
| 361 | − select IRQ_DOMAIN | |
| 362 | 358 | select IRQ_DOMAIN_HIERARCHY |
| 363 | 359 | help |
| 364 | 360 | Say yes here to add support for the IRQ combiner devices embedded |
| @@ -375,7 +371,6 @@ config IRQ_UNIPHIER_AIDET · 10 unmodified lines | ||
| 375 | 371 | config MESON_IRQ_GPIO |
| 376 | 372 | bool "Meson GPIO Interrupt Multiplexer" |
| 377 | 373 | depends on ARCH_MESON |
| 378 | − select IRQ_DOMAIN | |
| 379 | 374 | select IRQ_DOMAIN_HIERARCHY |
| 380 | 375 | help |
| 381 | 376 | Support Meson SoC Family GPIO Interrupt Multiplexer |
| @@ -391,7 +386,6 @@ config GOLDFISH_PIC · 9 unmodified lines | ||
| 391 | 386 | config QCOM_PDC |
| 392 | 387 | bool "QCOM PDC" |
| 393 | 388 | depends on ARCH_QCOM |
| 394 | − select IRQ_DOMAIN | |
| 395 | 389 | select IRQ_DOMAIN_HIERARCHY |
| 396 | 390 | help |
| 397 | 391 | Power Domain Controller driver to manage and configure wakeup |
| @@ -431,6 +425,27 @@ config LS1X_IRQ · 33 unmodified lines | ||
| 431 | 425 | help |
| 432 | 426 | Support for the Loongson-1 platform Interrupt Controller. |
| 433 | 427 | |
| 428 | +config TI_SCI_INTR_IRQCHIP | |
| 429 | + bool | |
| 430 | + depends on TI_SCI_PROTOCOL | |
| 431 | + select IRQ_DOMAIN_HIERARCHY | |
| 432 | + help | |
| 433 | + This enables the irqchip driver support for K3 Interrupt router | |
| 434 | + over TI System Control Interface available on some new TI's SoCs. | |
| 435 | + If you wish to use interrupt router irq resources managed by the | |
| 436 | + TI System Controller, say Y here. Otherwise, say N. | |
| 437 | + | |
| 438 | +config TI_SCI_INTA_IRQCHIP | |
| 439 | + bool | |
| 440 | + depends on TI_SCI_PROTOCOL | |
| 441 | + select IRQ_DOMAIN_HIERARCHY | |
| 442 | + select TI_SCI_INTA_MSI_DOMAIN | |
| 443 | + help | |
| 444 | + This enables the irqchip driver support for K3 Interrupt aggregator | |
| 445 | + over TI System Control Interface available on some new TI's SoCs. | |
| 446 | + If you wish to use interrupt aggregator irq resources managed by the | |
| 447 | + TI System Controller, say Y here. Otherwise, say N. | |
| 448 | + | |
| 434 | 449 | endmenu |
| 435 | 450 | |
| 436 | 451 | config SIFIVE_PLIC |
| @@ -98,3 +98,5 @@ obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o · 97 unmodified lines | ||
| 98 | 98 | obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o |
| 99 | 99 | obj-$(CONFIG_MADERA_IRQ) += irq-madera.o |
| 100 | 100 | obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o |
| 101 | +obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o | |
| 102 | +obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o | |
| @@ -343,6 +343,9 @@ int __init bcm7038_l1_of_init(struct device_node *dn, · 342 unmodified lines | ||
| 343 | 343 | goto out_unmap; |
| 344 | 344 | } |
| 345 | 345 | |
| 346 | + pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n", | |
| 347 | + dn, IRQS_PER_WORD * intc->n_words); | |
| 348 | + | |
| 346 | 349 | return 0; |
| 347 | 350 | |
| 348 | 351 | out_unmap: |
| @@ -318,6 +318,9 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn, · 317 unmodified lines | ||
| 318 | 318 | } |
| 319 | 319 | } |
| 320 | 320 | |
| 321 | + pr_info("registered %s intc (%pOF, parent IRQ(s): %d)\n", | |
| 322 | + intc_name, dn, data->num_parent_irqs); | |
| 323 | + | |
| 321 | 324 | return 0; |
| 322 | 325 | |
| 323 | 326 | out_free_domain: |
| @@ -264,6 +264,8 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, · 263 unmodified lines | ||
| 264 | 264 | ct->chip.irq_set_wake = irq_gc_set_wake; |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | + pr_info("registered L2 intc (%pOF, parent irq: %d)\n", np, parent_irq); | |
| 268 | + | |
| 267 | 269 | return 0; |
| 268 | 270 | |
| 269 | 271 | out_free_domain: |
| @@ -19,7 +19,6 @@ · 18 unmodified lines | ||
| 19 | 19 | #include <linux/of_irq.h> |
| 20 | 20 | #include <linux/irqchip/arm-gic.h> |
| 21 | 21 | #include <linux/platform_device.h> |
| 22 | −#include <linux/pm_clock.h> | |
| 23 | 22 | #include <linux/pm_runtime.h> |
| 24 | 23 | #include <linux/slab.h> |
| 25 | 24 | |
| @@ -28,17 +27,27 @@ struct gic_clk_data { · 2 unmodified lines | ||
| 28 | 27 | const char *const *clocks; |
| 29 | 28 | }; |
| 30 | 29 | |
| 30 | +struct gic_chip_pm { | |
| 31 | + struct gic_chip_data *chip_data; | |
| 32 | + const struct gic_clk_data *clk_data; | |
| 33 | + struct clk_bulk_data *clks; | |
| 34 | +}; | |
| 35 | + | |
| 31 | 36 | static int gic_runtime_resume(struct device *dev) |
| 32 | 37 | { |
| 33 | − struct gic_chip_data *gic = dev_get_drvdata(dev); | |
| 38 | + struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); | |
| 39 | + struct gic_chip_data *gic = chip_pm->chip_data; | |
| 40 | + const struct gic_clk_data *data = chip_pm->clk_data; | |
| 34 | 41 | int ret; |
| 35 | 42 | |
| 36 | − ret = pm_clk_resume(dev); | |
| 37 | − if (ret) | |
| 43 | + ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks); | |
| 44 | + if (ret) { | |
| 45 | + dev_err(dev, "clk_enable failed: %d\n", ret); | |
| 38 | 46 | return ret; |
| 47 | + } | |
| 39 | 48 | |
| 40 | 49 | /* |
| 41 | − * On the very first resume, the pointer to the driver data | |
| 50 | + * On the very first resume, the pointer to chip_pm->chip_data | |
| 42 | 51 | * will be NULL and this is intentional, because we do not |
| 43 | 52 | * want to restore the GIC on the very first resume. So if |
| 44 | 53 | * the pointer is not valid just return. |
| @@ -54,35 +63,14 @@ static int gic_runtime_resume(struct device *dev) · 9 unmodified lines | ||
| 54 | 63 | |
| 55 | 64 | static int gic_runtime_suspend(struct device *dev) |
| 56 | 65 | { |
| 57 | − struct gic_chip_data *gic = dev_get_drvdata(dev); | |
| 66 | + struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); | |
| 67 | + struct gic_chip_data *gic = chip_pm->chip_data; | |
| 68 | + const struct gic_clk_data *data = chip_pm->clk_data; | |
| 58 | 69 | |
| 59 | 70 | gic_dist_save(gic); |
| 60 | 71 | gic_cpu_save(gic); |
| 61 | 72 | |
| 62 | − return pm_clk_suspend(dev); | |
| 63 | −} | |
| 64 | − | |
| 65 | −static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data) | |
| 66 | −{ | |
| 67 | − unsigned int i; | |
| 68 | − int ret; | |
| 69 | − | |
| 70 | − if (!dev || !data) | |
| 71 | − return -EINVAL; | |
| 72 | − | |
| 73 | − ret = pm_clk_create(dev); | |
| 74 | − if (ret) | |
| 75 | − return ret; | |
| 76 | − | |
| 77 | − for (i = 0; i < data->num_clocks; i++) { | |
| 78 | − ret = of_pm_clk_add_clk(dev, data->clocks[i]); | |
| 79 | − if (ret) { | |
| 80 | − dev_err(dev, "failed to add clock %s\n", | |
| 81 | − data->clocks[i]); | |
| 82 | − pm_clk_destroy(dev); | |
| 83 | − return ret; | |
| 84 | − } | |
| 85 | − } | |
| 73 | + clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks); | |
| 86 | 74 | |
| 87 | 75 | return 0; |
| 88 | 76 | } |
| @@ -91,8 +79,8 @@ static int gic_probe(struct platform_device *pdev) · 2 unmodified lines | ||
| 91 | 79 | { |
| 92 | 80 | struct device *dev = &pdev->dev; |
| 93 | 81 | const struct gic_clk_data *data; |
| 94 | − struct gic_chip_data *gic; | |
| 95 | − int ret, irq; | |
| 82 | + struct gic_chip_pm *chip_pm; | |
| 83 | + int ret, irq, i; | |
| 96 | 84 | |
| 97 | 85 | data = of_device_get_match_data(&pdev->dev); |
| 98 | 86 | if (!data) { |
| @@ -100,28 +88,41 @@ static int gic_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 100 | 88 | return -ENODEV; |
| 101 | 89 | } |
| 102 | 90 | |
| 91 | + chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL); | |
| 92 | + if (!chip_pm) | |
| 93 | + return -ENOMEM; | |
| 94 | + | |
| 103 | 95 | irq = irq_of_parse_and_map(dev->of_node, 0); |
| 104 | 96 | if (!irq) { |
| 105 | 97 | dev_err(dev, "no parent interrupt found!\n"); |
| 106 | 98 | return -EINVAL; |
| 107 | 99 | } |
| 108 | 100 | |
| 109 | − ret = gic_get_clocks(dev, data); | |
| 101 | + chip_pm->clks = devm_kcalloc(dev, data->num_clocks, | |
| 102 | + sizeof(*chip_pm->clks), GFP_KERNEL); | |
| 103 | + if (!chip_pm->clks) | |
| 104 | + return -ENOMEM; | |
| 105 | + | |
| 106 | + for (i = 0; i < data->num_clocks; i++) | |
| 107 | + chip_pm->clks[i].id = data->clocks[i]; | |
| 108 | + | |
| 109 | + ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks); | |
| 110 | 110 | if (ret) |
| 111 | 111 | goto irq_dispose; |
| 112 | 112 | |
| 113 | + chip_pm->clk_data = data; | |
| 114 | + dev_set_drvdata(dev, chip_pm); | |
| 115 | + | |
| 113 | 116 | pm_runtime_enable(dev); |
| 114 | 117 | |
| 115 | 118 | ret = pm_runtime_get_sync(dev); |
| 116 | 119 | if (ret < 0) |
| 117 | 120 | goto rpm_disable; |
| 118 | 121 | |
| 119 | − ret = gic_of_init_child(dev, &gic, irq); | |
| 122 | + ret = gic_of_init_child(dev, &chip_pm->chip_data, irq); | |
| 120 | 123 | if (ret) |
| 121 | 124 | goto rpm_put; |
| 122 | 125 | |
| 123 | − platform_set_drvdata(pdev, gic); | |
| 124 | − | |
| 125 | 126 | pm_runtime_put(dev); |
| 126 | 127 | |
| 127 | 128 | dev_info(dev, "GIC IRQ controller registered\n"); |
| @@ -132,7 +133,6 @@ rpm_put: · 4 unmodified lines | ||
| 132 | 133 | pm_runtime_put_sync(dev); |
| 133 | 134 | rpm_disable: |
| 134 | 135 | pm_runtime_disable(dev); |
| 135 | − pm_clk_destroy(dev); | |
| 136 | 136 | irq_dispose: |
| 137 | 137 | irq_dispose_mapping(irq); |
| 138 | 138 | |
| @@ -142,6 +142,8 @@ irq_dispose: · 3 unmodified lines | ||
| 142 | 142 | static const struct dev_pm_ops gic_pm_ops = { |
| 143 | 143 | SET_RUNTIME_PM_OPS(gic_runtime_suspend, |
| 144 | 144 | gic_runtime_resume, NULL) |
| 145 | + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
| 146 | + pm_runtime_force_resume) | |
| 145 | 147 | }; |
| 146 | 148 | |
| 147 | 149 | static const char * const gic400_clocks[] = { |
| @@ -110,7 +110,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) · 109 unmodified lines | ||
| 110 | 110 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) |
| 111 | 111 | msg->data -= v2m->spi_offset; |
| 112 | 112 | |
| 113 | − iommu_dma_map_msi_msg(data->irq, msg); | |
| 113 | + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); | |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | 116 | static struct irq_chip gicv2m_irq_chip = { |
| @@ -167,6 +167,7 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq, · 50 unmodified lines | ||
| 167 | 167 | static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
| 168 | 168 | unsigned int nr_irqs, void *args) |
| 169 | 169 | { |
| 170 | + msi_alloc_info_t *info = args; | |
| 170 | 171 | struct v2m_data *v2m = NULL, *tmp; |
| 171 | 172 | int hwirq, offset, i, err = 0; |
| 172 | 173 | |
| @@ -186,6 +187,11 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, · 13 unmodified lines | ||
| 186 | 187 | |
| 187 | 188 | hwirq = v2m->spi_start + offset; |
| 188 | 189 | |
| 190 | + err = iommu_dma_prepare_msi(info->desc, | |
| 191 | + v2m->res.start + V2M_MSI_SETSPI_NS); | |
| 192 | + if (err) | |
| 193 | + return err; | |
| 194 | + | |
| 189 | 195 | for (i = 0; i < nr_irqs; i++) { |
| 190 | 196 | err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
| 191 | 197 | if (err) |
| @@ -26,7 +26,6 @@ · 25 unmodified lines | ||
| 26 | 26 | #include <linux/interrupt.h> |
| 27 | 27 | #include <linux/irqdomain.h> |
| 28 | 28 | #include <linux/list.h> |
| 29 | −#include <linux/list_sort.h> | |
| 30 | 29 | #include <linux/log2.h> |
| 31 | 30 | #include <linux/memblock.h> |
| 32 | 31 | #include <linux/mm.h> |
| @@ -1179,7 +1178,7 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) · 1146 unmodified lines | ||
| 1179 | 1178 | msg->address_hi = upper_32_bits(addr); |
| 1180 | 1179 | msg->data = its_get_event_id(d); |
| 1181 | 1180 | |
| 1182 | − iommu_dma_map_msi_msg(d->irq, msg); | |
| 1181 | + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg); | |
| 1183 | 1182 | } |
| 1184 | 1183 | |
| 1185 | 1184 | static int its_irq_set_irqchip_state(struct irq_data *d, |
| @@ -1465,9 +1464,8 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span) · 279 unmodified lines | ||
| 1465 | 1464 | { |
| 1466 | 1465 | struct lpi_range *range; |
| 1467 | 1466 | |
| 1468 | − range = kzalloc(sizeof(*range), GFP_KERNEL); | |
| 1467 | + range = kmalloc(sizeof(*range), GFP_KERNEL); | |
| 1469 | 1468 | if (range) { |
| 1470 | − INIT_LIST_HEAD(&range->entry); | |
| 1471 | 1469 | range->base_id = base; |
| 1472 | 1470 | range->span = span; |
| 1473 | 1471 | } |
| @@ -1475,31 +1473,6 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span) · 1 unmodified line | ||
| 1475 | 1473 | return range; |
| 1476 | 1474 | } |
| 1477 | 1475 | |
| 1478 | −static int lpi_range_cmp(void *priv, struct list_head *a, struct list_head *b) | |
| 1479 | −{ | |
| 1480 | − struct lpi_range *ra, *rb; | |
| 1481 | − | |
| 1482 | − ra = container_of(a, struct lpi_range, entry); | |
| 1483 | − rb = container_of(b, struct lpi_range, entry); | |
| 1484 | − | |
| 1485 | − return ra->base_id - rb->base_id; | |
| 1486 | −} | |
| 1487 | − | |
| 1488 | −static void merge_lpi_ranges(void) | |
| 1489 | −{ | |
| 1490 | − struct lpi_range *range, *tmp; | |
| 1491 | − | |
| 1492 | − list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) { | |
| 1493 | − if (!list_is_last(&range->entry, &lpi_range_list) && | |
| 1494 | − (tmp->base_id == (range->base_id + range->span))) { | |
| 1495 | − tmp->base_id = range->base_id; | |
| 1496 | − tmp->span += range->span; | |
| 1497 | − list_del(&range->entry); | |
| 1498 | − kfree(range); | |
| 1499 | − } | |
| 1500 | − } | |
| 1501 | −} | |
| 1502 | − | |
| 1503 | 1476 | static int alloc_lpi_range(u32 nr_lpis, u32 *base) |
| 1504 | 1477 | { |
| 1505 | 1478 | struct lpi_range *range, *tmp; |
| @@ -1529,25 +1502,49 @@ static int alloc_lpi_range(u32 nr_lpis, u32 *base) · 23 unmodified lines | ||
| 1529 | 1502 | return err; |
| 1530 | 1503 | } |
| 1531 | 1504 | |
| 1505 | +static void merge_lpi_ranges(struct lpi_range *a, struct lpi_range *b) | |
| 1506 | +{ | |
| 1507 | + if (&a->entry == &lpi_range_list || &b->entry == &lpi_range_list) | |
| 1508 | + return; | |
| 1509 | + if (a->base_id + a->span != b->base_id) | |
| 1510 | + return; | |
| 1511 | + b->base_id = a->base_id; | |
| 1512 | + b->span += a->span; | |
| 1513 | + list_del(&a->entry); | |
| 1514 | + kfree(a); | |
| 1515 | +} | |
| 1516 | + | |
| 1532 | 1517 | static int free_lpi_range(u32 base, u32 nr_lpis) |
| 1533 | 1518 | { |
| 1534 | − struct lpi_range *new; | |
| 1535 | − int err = 0; | |
| 1519 | + struct lpi_range *new, *old; | |
| 1520 | + | |
| 1521 | + new = mk_lpi_range(base, nr_lpis); | |
| 1522 | + if (!new) | |
| 1523 | + return -ENOMEM; | |
| 1536 | 1524 | |
| 1537 | 1525 | mutex_lock(&lpi_range_lock); |
| 1538 | 1526 | |
| 1539 | − new = mk_lpi_range(base, nr_lpis); | |
| 1540 | − if (!new) { | |
| 1541 | − err = -ENOMEM; | |
| 1542 | − goto out; | |
| 1527 | + list_for_each_entry_reverse(old, &lpi_range_list, entry) { | |
| 1528 | + if (old->base_id < base) | |
| 1529 | + break; | |
| 1543 | 1530 | } |
| 1531 | + /* | |
| 1532 | + * old is the last element with ->base_id smaller than base, | |
| 1533 | + * so new goes right after it. If there are no elements with | |
| 1534 | + * ->base_id smaller than base, &old->entry ends up pointing | |
| 1535 | + * at the head of the list, and inserting new it the start of | |
| 1536 | + * the list is the right thing to do in that case as well. | |
| 1537 | + */ | |
| 1538 | + list_add(&new->entry, &old->entry); | |
| 1539 | + /* | |
| 1540 | + * Now check if we can merge with the preceding and/or | |
| 1541 | + * following ranges. | |
| 1542 | + */ | |
| 1543 | + merge_lpi_ranges(old, new); | |
| 1544 | + merge_lpi_ranges(new, list_next_entry(new, entry)); | |
| 1544 | 1545 | |
| 1545 | − list_add(&new->entry, &lpi_range_list); | |
| 1546 | − list_sort(NULL, &lpi_range_list, lpi_range_cmp); | |
| 1547 | − merge_lpi_ranges(); | |
| 1548 | −out: | |
| 1549 | 1546 | mutex_unlock(&lpi_range_lock); |
| 1550 | − return err; | |
| 1547 | + return 0; | |
| 1551 | 1548 | } |
| 1552 | 1549 | |
| 1553 | 1550 | static int __init its_lpi_init(u32 id_bits) |
| @@ -2487,7 +2484,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, · 933 unmodified lines | ||
| 2487 | 2484 | int err = 0; |
| 2488 | 2485 | |
| 2489 | 2486 | /* |
| 2490 | − * We ignore "dev" entierely, and rely on the dev_id that has | |
| 2487 | + * We ignore "dev" entirely, and rely on the dev_id that has | |
| 2491 | 2488 | * been passed via the scratchpad. This limits this domain's |
| 2492 | 2489 | * usefulness to upper layers that definitely know that they |
| 2493 | 2490 | * are built on top of the ITS. |
| @@ -2566,6 +2563,7 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, · 72 unmodified lines | ||
| 2566 | 2563 | { |
| 2567 | 2564 | msi_alloc_info_t *info = args; |
| 2568 | 2565 | struct its_device *its_dev = info->scratchpad[0].ptr; |
| 2566 | + struct its_node *its = its_dev->its; | |
| 2569 | 2567 | irq_hw_number_t hwirq; |
| 2570 | 2568 | int err; |
| 2571 | 2569 | int i; |
| @@ -2574,6 +2572,10 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, · 2 unmodified lines | ||
| 2574 | 2572 | if (err) |
| 2575 | 2573 | return err; |
| 2576 | 2574 | |
| 2575 | + err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev)); | |
| 2576 | + if (err) | |
| 2577 | + return err; | |
| 2578 | + | |
| 2577 | 2579 | for (i = 0; i < nr_irqs; i++) { |
| 2578 | 2580 | err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
| 2579 | 2581 | if (err) |
| @@ -84,6 +84,7 @@ static void mbi_free_msi(struct mbi_range *mbi, unsigned int hwirq, · 83 unmodified lines | ||
| 84 | 84 | static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
| 85 | 85 | unsigned int nr_irqs, void *args) |
| 86 | 86 | { |
| 87 | + msi_alloc_info_t *info = args; | |
| 87 | 88 | struct mbi_range *mbi = NULL; |
| 88 | 89 | int hwirq, offset, i, err = 0; |
| 89 | 90 | |
| @@ -104,6 +105,11 @@ static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, · 14 unmodified lines | ||
| 104 | 105 | |
| 105 | 106 | hwirq = mbi->spi_start + offset; |
| 106 | 107 | |
| 108 | + err = iommu_dma_prepare_msi(info->desc, | |
| 109 | + mbi_phys_base + GICD_SETSPI_NSR); | |
| 110 | + if (err) | |
| 111 | + return err; | |
| 112 | + | |
| 107 | 113 | for (i = 0; i < nr_irqs; i++) { |
| 108 | 114 | err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
| 109 | 115 | if (err) |
| @@ -142,7 +148,7 @@ static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) · 32 unmodified lines | ||
| 142 | 148 | msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); |
| 143 | 149 | msg[0].data = data->parent_data->hwirq; |
| 144 | 150 | |
| 145 | − iommu_dma_map_msi_msg(data->irq, msg); | |
| 151 | + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); | |
| 146 | 152 | } |
| 147 | 153 | |
| 148 | 154 | #ifdef CONFIG_PCI_MSI |
| @@ -202,7 +208,7 @@ static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) · 53 unmodified lines | ||
| 202 | 208 | msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); |
| 203 | 209 | msg[1].data = data->parent_data->hwirq; |
| 204 | 210 | |
| 205 | − iommu_dma_map_msi_msg(data->irq, &msg[1]); | |
| 211 | + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]); | |
| 206 | 212 | } |
| 207 | 213 | |
| 208 | 214 | /* Platform-MSI specific irqchip */ |
| @@ -144,7 +144,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) · 143 unmodified lines | ||
| 144 | 144 | { |
| 145 | 145 | struct device_node *np = pdev->dev.of_node; |
| 146 | 146 | struct irqsteer_data *data; |
| 147 | − struct resource *res; | |
| 148 | 147 | u32 irqs_num; |
| 149 | 148 | int i, ret; |
| 150 | 149 | |
| @@ -152,8 +151,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 152 | 151 | if (!data) |
| 153 | 152 | return -ENOMEM; |
| 154 | 153 | |
| 155 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 156 | − data->regs = devm_ioremap_resource(&pdev->dev, res); | |
| 154 | + data->regs = devm_platform_ioremap_resource(pdev, 0); | |
| 157 | 155 | if (IS_ERR(data->regs)) { |
| 158 | 156 | dev_err(&pdev->dev, "failed to initialize reg\n"); |
| 159 | 157 | return PTR_ERR(data->regs); |
| @@ -100,7 +100,7 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) · 99 unmodified lines | ||
| 100 | 100 | msg->data |= cpumask_first(mask); |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | − iommu_dma_map_msi_msg(data->irq, msg); | |
| 103 | + iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); | |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | 106 | static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, |
| @@ -141,6 +141,7 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, · 34 unmodified lines | ||
| 141 | 141 | unsigned int nr_irqs, |
| 142 | 142 | void *args) |
| 143 | 143 | { |
| 144 | + msi_alloc_info_t *info = args; | |
| 144 | 145 | struct ls_scfg_msi *msi_data = domain->host_data; |
| 145 | 146 | int pos, err = 0; |
| 146 | 147 | |
| @@ -154,6 +155,10 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, · 7 unmodified lines | ||
| 154 | 155 | err = -ENOSPC; |
| 155 | 156 | spin_unlock(&msi_data->lock); |
| 156 | 157 | |
| 158 | + if (err) | |
| 159 | + return err; | |
| 160 | + | |
| 161 | + err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr); | |
| 157 | 162 | if (err) |
| 158 | 163 | return err; |
| 159 | 164 | |
| @@ -389,10 +389,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) · 388 unmodified lines | ||
| 389 | 389 | int k; |
| 390 | 390 | |
| 391 | 391 | p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); |
| 392 | − if (!p) { | |
| 393 | − dev_err(dev, "failed to allocate driver data\n"); | |
| 392 | + if (!p) | |
| 394 | 393 | return -ENOMEM; |
| 395 | − } | |
| 396 | 394 | |
| 397 | 395 | /* deal with driver instance configuration */ |
| 398 | 396 | of_property_read_u32(dev->of_node, "sense-bitfield-width", |
| @@ -14,8 +14,10 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <linux/irqchip.h> |
| 15 | 15 | #include <linux/irqchip/chained_irq.h> |
| 16 | 16 | #include <linux/irqdomain.h> |
| 17 | +#include <linux/module.h> | |
| 17 | 18 | #include <linux/of_address.h> |
| 18 | 19 | #include <linux/of_irq.h> |
| 20 | +#include <linux/of_platform.h> | |
| 19 | 21 | #include <linux/syscore_ops.h> |
| 20 | 22 | |
| 21 | 23 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| @@ -37,12 +39,6 @@ struct stm32_exti_bank { · 15 unmodified lines | ||
| 37 | 39 | |
| 38 | 40 | #define UNDEF_REG ~0 |
| 39 | 41 | |
| 40 | −enum stm32_exti_hwspinlock { | |
| 41 | − HWSPINLOCK_UNKNOWN, | |
| 42 | − HWSPINLOCK_NONE, | |
| 43 | − HWSPINLOCK_READY, | |
| 44 | −}; | |
| 45 | − | |
| 46 | 42 | struct stm32_desc_irq { |
| 47 | 43 | u32 exti; |
| 48 | 44 | u32 irq_parent; |
| @@ -69,8 +65,6 @@ struct stm32_exti_host_data { · 20 unmodified lines | ||
| 69 | 65 | void __iomem *base; |
| 70 | 66 | struct stm32_exti_chip_data *chips_data; |
| 71 | 67 | const struct stm32_exti_drv_data *drv_data; |
| 72 | − struct device_node *node; | |
| 73 | − enum stm32_exti_hwspinlock hwlock_state; | |
| 74 | 68 | struct hwspinlock *hwlock; |
| 75 | 69 | }; |
| 76 | 70 | |
| @@ -285,49 +279,27 @@ static int stm32_exti_set_type(struct irq_data *d, · 208 unmodified lines | ||
| 285 | 279 | |
| 286 | 280 | static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) |
| 287 | 281 | { |
| 288 | − struct stm32_exti_host_data *host_data = chip_data->host_data; | |
| 289 | − struct hwspinlock *hwlock; | |
| 290 | − int id, ret = 0, timeout = 0; | |
| 291 | − | |
| 292 | − /* first time, check for hwspinlock availability */ | |
| 293 | − if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { | |
| 294 | − id = of_hwspin_lock_get_id(host_data->node, 0); | |
| 295 | − if (id >= 0) { | |
| 296 | − hwlock = hwspin_lock_request_specific(id); | |
| 297 | − if (hwlock) { | |
| 298 | − /* found valid hwspinlock */ | |
| 299 | − host_data->hwlock_state = HWSPINLOCK_READY; | |
| 300 | − host_data->hwlock = hwlock; | |
| 301 | − pr_debug("%s hwspinlock = %d\n", __func__, id); | |
| 302 | − } else { | |
| 303 | − host_data->hwlock_state = HWSPINLOCK_NONE; | |
| 304 | − } | |
| 305 | − } else if (id != -EPROBE_DEFER) { | |
| 306 | − host_data->hwlock_state = HWSPINLOCK_NONE; | |
| 307 | − } else { | |
| 308 | − /* hwspinlock driver shall be ready at that stage */ | |
| 309 | − ret = -EPROBE_DEFER; | |
| 310 | − } | |
| 311 | − } | |
| 282 | + int ret, timeout = 0; | |
| 312 | 283 | |
| 313 | − if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { | |
| 314 | − /* | |
| 315 | − * Use the x_raw API since we are under spin_lock protection. | |
| 316 | − * Do not use the x_timeout API because we are under irq_disable | |
| 317 | − * mode (see __setup_irq()) | |
| 318 | − */ | |
| 319 | − do { | |
| 320 | − ret = hwspin_trylock_raw(host_data->hwlock); | |
| 321 | − if (!ret) | |
| 322 | − return 0; | |
| 323 | − | |
| 324 | − udelay(HWSPNLCK_RETRY_DELAY); | |
| 325 | − timeout += HWSPNLCK_RETRY_DELAY; | |
| 326 | − } while (timeout < HWSPNLCK_TIMEOUT); | |
| 327 | − | |
| 328 | − if (ret == -EBUSY) | |
| 329 | − ret = -ETIMEDOUT; | |
| 330 | − } | |
| 284 | + if (!chip_data->host_data->hwlock) | |
| 285 | + return 0; | |
| 286 | + | |
| 287 | + /* | |
| 288 | + * Use the x_raw API since we are under spin_lock protection. | |
| 289 | + * Do not use the x_timeout API because we are under irq_disable | |
| 290 | + * mode (see __setup_irq()) | |
| 291 | + */ | |
| 292 | + do { | |
| 293 | + ret = hwspin_trylock_raw(chip_data->host_data->hwlock); | |
| 294 | + if (!ret) | |
| 295 | + return 0; | |
| 296 | + | |
| 297 | + udelay(HWSPNLCK_RETRY_DELAY); | |
| 298 | + timeout += HWSPNLCK_RETRY_DELAY; | |
| 299 | + } while (timeout < HWSPNLCK_TIMEOUT); | |
| 300 | + | |
| 301 | + if (ret == -EBUSY) | |
| 302 | + ret = -ETIMEDOUT; | |
| 331 | 303 | |
| 332 | 304 | if (ret) |
| 333 | 305 | pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); |
| @@ -337,7 +309,7 @@ static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) · 3 unmodified lines | ||
| 337 | 309 | |
| 338 | 310 | static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) |
| 339 | 311 | { |
| 340 | − if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) | |
| 312 | + if (chip_data->host_data->hwlock) | |
| 341 | 313 | hwspin_unlock_raw(chip_data->host_data->hwlock); |
| 342 | 314 | } |
| 343 | 315 | |
| @@ -586,8 +558,7 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, · 242 unmodified lines | ||
| 586 | 558 | return -EINVAL; |
| 587 | 559 | } |
| 588 | 560 | |
| 589 | −#ifdef CONFIG_PM | |
| 590 | −static int stm32_exti_h_suspend(void) | |
| 561 | +static int __maybe_unused stm32_exti_h_suspend(void) | |
| 591 | 562 | { |
| 592 | 563 | struct stm32_exti_chip_data *chip_data; |
| 593 | 564 | int i; |
| @@ -602,7 +573,7 @@ static int stm32_exti_h_suspend(void) · 8 unmodified lines | ||
| 602 | 573 | return 0; |
| 603 | 574 | } |
| 604 | 575 | |
| 605 | −static void stm32_exti_h_resume(void) | |
| 576 | +static void __maybe_unused stm32_exti_h_resume(void) | |
| 606 | 577 | { |
| 607 | 578 | struct stm32_exti_chip_data *chip_data; |
| 608 | 579 | int i; |
| @@ -616,17 +587,22 @@ static void stm32_exti_h_resume(void) · 7 unmodified lines | ||
| 616 | 587 | } |
| 617 | 588 | |
| 618 | 589 | static struct syscore_ops stm32_exti_h_syscore_ops = { |
| 590 | +#ifdef CONFIG_PM_SLEEP | |
| 619 | 591 | .suspend = stm32_exti_h_suspend, |
| 620 | 592 | .resume = stm32_exti_h_resume, |
| 593 | +#endif | |
| 621 | 594 | }; |
| 622 | 595 | |
| 623 | −static void stm32_exti_h_syscore_init(void) | |
| 596 | +static void stm32_exti_h_syscore_init(struct stm32_exti_host_data *host_data) | |
| 624 | 597 | { |
| 598 | + stm32_host_data = host_data; | |
| 625 | 599 | register_syscore_ops(&stm32_exti_h_syscore_ops); |
| 626 | 600 | } |
| 627 | −#else | |
| 628 | −static inline void stm32_exti_h_syscore_init(void) {} | |
| 629 | −#endif | |
| 601 | + | |
| 602 | +static void stm32_exti_h_syscore_deinit(void) | |
| 603 | +{ | |
| 604 | + unregister_syscore_ops(&stm32_exti_h_syscore_ops); | |
| 605 | +} | |
| 630 | 606 | |
| 631 | 607 | static struct irq_chip stm32_exti_h_chip = { |
| 632 | 608 | .name = "stm32-exti-h", |
| @@ -683,8 +659,6 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, · 50 unmodified lines | ||
| 683 | 659 | return NULL; |
| 684 | 660 | |
| 685 | 661 | host_data->drv_data = dd; |
| 686 | − host_data->node = node; | |
| 687 | − host_data->hwlock_state = HWSPINLOCK_UNKNOWN; | |
| 688 | 662 | host_data->chips_data = kcalloc(dd->bank_nr, |
| 689 | 663 | sizeof(struct stm32_exti_chip_data), |
| 690 | 664 | GFP_KERNEL); |
| @@ -711,7 +685,8 @@ free_host_data: · 20 unmodified lines | ||
| 711 | 685 | |
| 712 | 686 | static struct |
| 713 | 687 | stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, |
| 714 | − u32 bank_idx) | |
| 688 | + u32 bank_idx, | |
| 689 | + struct device_node *node) | |
| 715 | 690 | { |
| 716 | 691 | const struct stm32_exti_bank *stm32_bank; |
| 717 | 692 | struct stm32_exti_chip_data *chip_data; |
| @@ -731,7 +706,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, · 13 unmodified lines | ||
| 731 | 706 | writel_relaxed(0, base + stm32_bank->imr_ofst); |
| 732 | 707 | writel_relaxed(0, base + stm32_bank->emr_ofst); |
| 733 | 708 | |
| 734 | − pr_info("%pOF: bank%d\n", h_data->node, bank_idx); | |
| 709 | + pr_info("%pOF: bank%d\n", node, bank_idx); | |
| 735 | 710 | |
| 736 | 711 | return chip_data; |
| 737 | 712 | } |
| @@ -771,7 +746,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, · 33 unmodified lines | ||
| 771 | 746 | struct stm32_exti_chip_data *chip_data; |
| 772 | 747 | |
| 773 | 748 | stm32_bank = drv_data->exti_banks[i]; |
| 774 | − chip_data = stm32_exti_chip_init(host_data, i); | |
| 749 | + chip_data = stm32_exti_chip_init(host_data, i, node); | |
| 775 | 750 | |
| 776 | 751 | gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); |
| 777 | 752 | |
| @@ -815,50 +790,130 @@ static const struct irq_domain_ops stm32_exti_h_domain_ops = { · 37 unmodified lines | ||
| 815 | 790 | .xlate = irq_domain_xlate_twocell, |
| 816 | 791 | }; |
| 817 | 792 | |
| 818 | −static int | |
| 819 | −__init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, | |
| 820 | − struct device_node *node, | |
| 821 | − struct device_node *parent) | |
| 793 | +static void stm32_exti_remove_irq(void *data) | |
| 794 | +{ | |
| 795 | + struct irq_domain *domain = data; | |
| 796 | + | |
| 797 | + irq_domain_remove(domain); | |
| 798 | +} | |
| 799 | + | |
| 800 | +static int stm32_exti_remove(struct platform_device *pdev) | |
| 801 | +{ | |
| 802 | + stm32_exti_h_syscore_deinit(); | |
| 803 | + return 0; | |
| 804 | +} | |
| 805 | + | |
| 806 | +static int stm32_exti_probe(struct platform_device *pdev) | |
| 822 | 807 | { |
| 808 | + int ret, i; | |
| 809 | + struct device *dev = &pdev->dev; | |
| 810 | + struct device_node *np = dev->of_node; | |
| 823 | 811 | struct irq_domain *parent_domain, *domain; |
| 824 | 812 | struct stm32_exti_host_data *host_data; |
| 825 | − int ret, i; | |
| 813 | + const struct stm32_exti_drv_data *drv_data; | |
| 814 | + struct resource *res; | |
| 826 | 815 | |
| 827 | − parent_domain = irq_find_host(parent); | |
| 828 | − if (!parent_domain) { | |
| 829 | − pr_err("interrupt-parent not found\n"); | |
| 830 | − return -EINVAL; | |
| 816 | + host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); | |
| 817 | + if (!host_data) | |
| 818 | + return -ENOMEM; | |
| 819 | + | |
| 820 | + /* check for optional hwspinlock which may be not available yet */ | |
| 821 | + ret = of_hwspin_lock_get_id(np, 0); | |
| 822 | + if (ret == -EPROBE_DEFER) | |
| 823 | + /* hwspinlock framework not yet ready */ | |
| 824 | + return ret; | |
| 825 | + | |
| 826 | + if (ret >= 0) { | |
| 827 | + host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); | |
| 828 | + if (!host_data->hwlock) { | |
| 829 | + dev_err(dev, "Failed to request hwspinlock\n"); | |
| 830 | + return -EINVAL; | |
| 831 | + } | |
| 832 | + } else if (ret != -ENOENT) { | |
| 833 | + /* note: ENOENT is a valid case (means 'no hwspinlock') */ | |
| 834 | + dev_err(dev, "Failed to get hwspinlock\n"); | |
| 835 | + return ret; | |
| 831 | 836 | } |
| 832 | 837 | |
| 833 | − host_data = stm32_exti_host_init(drv_data, node); | |
| 834 | − if (!host_data) | |
| 838 | + /* initialize host_data */ | |
| 839 | + drv_data = of_device_get_match_data(dev); | |
| 840 | + if (!drv_data) { | |
| 841 | + dev_err(dev, "no of match data\n"); | |
| 842 | + return -ENODEV; | |
| 843 | + } | |
| 844 | + host_data->drv_data = drv_data; | |
| 845 | + | |
| 846 | + host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, | |
| 847 | + sizeof(*host_data->chips_data), | |
| 848 | + GFP_KERNEL); | |
| 849 | + if (!host_data->chips_data) | |
| 835 | 850 | return -ENOMEM; |
| 836 | 851 | |
| 852 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 853 | + host_data->base = devm_ioremap_resource(dev, res); | |
| 854 | + if (IS_ERR(host_data->base)) { | |
| 855 | + dev_err(dev, "Unable to map registers\n"); | |
| 856 | + return PTR_ERR(host_data->base); | |
| 857 | + } | |
| 858 | + | |
| 837 | 859 | for (i = 0; i < drv_data->bank_nr; i++) |
| 838 | − stm32_exti_chip_init(host_data, i); | |
| 860 | + stm32_exti_chip_init(host_data, i, np); | |
| 861 | + | |
| 862 | + parent_domain = irq_find_host(of_irq_find_parent(np)); | |
| 863 | + if (!parent_domain) { | |
| 864 | + dev_err(dev, "GIC interrupt-parent not found\n"); | |
| 865 | + return -EINVAL; | |
| 866 | + } | |
| 839 | 867 | |
| 840 | 868 | domain = irq_domain_add_hierarchy(parent_domain, 0, |
| 841 | 869 | drv_data->bank_nr * IRQS_PER_BANK, |
| 842 | − node, &stm32_exti_h_domain_ops, | |
| 870 | + np, &stm32_exti_h_domain_ops, | |
| 843 | 871 | host_data); |
| 844 | 872 | |
| 845 | 873 | if (!domain) { |
| 846 | − pr_err("%pOFn: Could not register exti domain.\n", node); | |
| 847 | − ret = -ENOMEM; | |
| 848 | − goto out_unmap; | |
| 874 | + dev_err(dev, "Could not register exti domain\n"); | |
| 875 | + return -ENOMEM; | |
| 849 | 876 | } |
| 850 | 877 | |
| 851 | − stm32_exti_h_syscore_init(); | |
| 878 | + ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); | |
| 879 | + if (ret) | |
| 880 | + return ret; | |
| 881 | + | |
| 882 | + stm32_exti_h_syscore_init(host_data); | |
| 852 | 883 | |
| 853 | 884 | return 0; |
| 885 | +} | |
| 854 | 886 | |
| 855 | −out_unmap: | |
| 856 | − iounmap(host_data->base); | |
| 857 | − kfree(host_data->chips_data); | |
| 858 | − kfree(host_data); | |
| 859 | − return ret; | |
| 887 | +/* platform driver only for MP1 */ | |
| 888 | +static const struct of_device_id stm32_exti_ids[] = { | |
| 889 | + { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, | |
| 890 | + {}, | |
| 891 | +}; | |
| 892 | +MODULE_DEVICE_TABLE(of, stm32_exti_ids); | |
| 893 | + | |
| 894 | +static struct platform_driver stm32_exti_driver = { | |
| 895 | + .probe = stm32_exti_probe, | |
| 896 | + .remove = stm32_exti_remove, | |
| 897 | + .driver = { | |
| 898 | + .name = "stm32_exti", | |
| 899 | + .of_match_table = stm32_exti_ids, | |
| 900 | + }, | |
| 901 | +}; | |
| 902 | + | |
| 903 | +static int __init stm32_exti_arch_init(void) | |
| 904 | +{ | |
| 905 | + return platform_driver_register(&stm32_exti_driver); | |
| 860 | 906 | } |
| 861 | 907 | |
| 908 | +static void __exit stm32_exti_arch_exit(void) | |
| 909 | +{ | |
| 910 | + return platform_driver_unregister(&stm32_exti_driver); | |
| 911 | +} | |
| 912 | + | |
| 913 | +arch_initcall(stm32_exti_arch_init); | |
| 914 | +module_exit(stm32_exti_arch_exit); | |
| 915 | + | |
| 916 | +/* no platform driver for F4 and H7 */ | |
| 862 | 917 | static int __init stm32f4_exti_of_init(struct device_node *np, |
| 863 | 918 | struct device_node *parent) |
| 864 | 919 | { |
| @@ -874,11 +929,3 @@ static int __init stm32h7_exti_of_init(struct device_node *np, · 9 unmodified lines | ||
| 874 | 929 | } |
| 875 | 930 | |
| 876 | 931 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); |
| 877 | − | |
| 878 | −static int __init stm32mp1_exti_of_init(struct device_node *np, | |
| 879 | − struct device_node *parent) | |
| 880 | −{ | |
| 881 | − return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); | |
| 882 | −} | |
| 883 | − | |
| 884 | −IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); | |
| @@ -0,0 +1,615 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Texas Instruments' K3 Interrupt Aggregator irqchip driver | |
| 4 | + * | |
| 5 | + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | |
| 6 | + * Lokesh Vutla <lokeshvutla@ti.com> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/io.h> | |
| 11 | +#include <linux/irqchip.h> | |
| 12 | +#include <linux/irqdomain.h> | |
| 13 | +#include <linux/interrupt.h> | |
| 14 | +#include <linux/msi.h> | |
| 15 | +#include <linux/module.h> | |
| 16 | +#include <linux/moduleparam.h> | |
| 17 | +#include <linux/of_address.h> | |
| 18 | +#include <linux/of_irq.h> | |
| 19 | +#include <linux/of_platform.h> | |
| 20 | +#include <linux/irqchip/chained_irq.h> | |
| 21 | +#include <linux/soc/ti/ti_sci_inta_msi.h> | |
| 22 | +#include <linux/soc/ti/ti_sci_protocol.h> | |
| 23 | +#include <asm-generic/msi.h> | |
| 24 | + | |
| 25 | +#define TI_SCI_DEV_ID_MASK 0xffff | |
| 26 | +#define TI_SCI_DEV_ID_SHIFT 16 | |
| 27 | +#define TI_SCI_IRQ_ID_MASK 0xffff | |
| 28 | +#define TI_SCI_IRQ_ID_SHIFT 0 | |
| 29 | +#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ | |
| 30 | + (TI_SCI_DEV_ID_MASK)) | |
| 31 | +#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) | |
| 32 | +#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ | |
| 33 | + TI_SCI_DEV_ID_SHIFT) | \ | |
| 34 | + ((index) & TI_SCI_IRQ_ID_MASK)) | |
| 35 | + | |
| 36 | +#define MAX_EVENTS_PER_VINT 64 | |
| 37 | +#define VINT_ENABLE_SET_OFFSET 0x0 | |
| 38 | +#define VINT_ENABLE_CLR_OFFSET 0x8 | |
| 39 | +#define VINT_STATUS_OFFSET 0x18 | |
| 40 | + | |
| 41 | +/** | |
| 42 | + * struct ti_sci_inta_event_desc - Description of an event coming to | |
| 43 | + * Interrupt Aggregator. This serves | |
| 44 | + * as a mapping table for global event, | |
| 45 | + * hwirq and vint bit. | |
| 46 | + * @global_event: Global event number corresponding to this event | |
| 47 | + * @hwirq: Hwirq of the incoming interrupt | |
| 48 | + * @vint_bit: Corresponding vint bit to which this event is attached. | |
| 49 | + */ | |
| 50 | +struct ti_sci_inta_event_desc { | |
| 51 | + u16 global_event; | |
| 52 | + u32 hwirq; | |
| 53 | + u8 vint_bit; | |
| 54 | +}; | |
| 55 | + | |
| 56 | +/** | |
| 57 | + * struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out | |
| 58 | + * of Interrupt Aggregator. | |
| 59 | + * @domain: Pointer to IRQ domain to which this vint belongs. | |
| 60 | + * @list: List entry for the vint list | |
| 61 | + * @event_map: Bitmap to manage the allocation of events to vint. | |
| 62 | + * @events: Array of event descriptors assigned to this vint. | |
| 63 | + * @parent_virq: Linux IRQ number that gets attached to parent | |
| 64 | + * @vint_id: TISCI vint ID | |
| 65 | + */ | |
| 66 | +struct ti_sci_inta_vint_desc { | |
| 67 | + struct irq_domain *domain; | |
| 68 | + struct list_head list; | |
| 69 | + DECLARE_BITMAP(event_map, MAX_EVENTS_PER_VINT); | |
| 70 | + struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT]; | |
| 71 | + unsigned int parent_virq; | |
| 72 | + u16 vint_id; | |
| 73 | +}; | |
| 74 | + | |
| 75 | +/** | |
| 76 | + * struct ti_sci_inta_irq_domain - Structure representing a TISCI based | |
| 77 | + * Interrupt Aggregator IRQ domain. | |
| 78 | + * @sci: Pointer to TISCI handle | |
| 79 | + * @vint: TISCI resource pointer representing IA inerrupts. | |
| 80 | + * @global_event: TISCI resource pointer representing global events. | |
| 81 | + * @vint_list: List of the vints active in the system | |
| 82 | + * @vint_mutex: Mutex to protect vint_list | |
| 83 | + * @base: Base address of the memory mapped IO registers | |
| 84 | + * @pdev: Pointer to platform device. | |
| 85 | + */ | |
| 86 | +struct ti_sci_inta_irq_domain { | |
| 87 | + const struct ti_sci_handle *sci; | |
| 88 | + struct ti_sci_resource *vint; | |
| 89 | + struct ti_sci_resource *global_event; | |
| 90 | + struct list_head vint_list; | |
| 91 | + /* Mutex to protect vint list */ | |
| 92 | + struct mutex vint_mutex; | |
| 93 | + void __iomem *base; | |
| 94 | + struct platform_device *pdev; | |
| 95 | +}; | |
| 96 | + | |
| 97 | +#define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \ | |
| 98 | + events[i]) | |
| 99 | + | |
| 100 | +/** | |
| 101 | + * ti_sci_inta_irq_handler() - Chained IRQ handler for the vint irqs | |
| 102 | + * @desc: Pointer to irq_desc corresponding to the irq | |
| 103 | + */ | |
| 104 | +static void ti_sci_inta_irq_handler(struct irq_desc *desc) | |
| 105 | +{ | |
| 106 | + struct ti_sci_inta_vint_desc *vint_desc; | |
| 107 | + struct ti_sci_inta_irq_domain *inta; | |
| 108 | + struct irq_domain *domain; | |
| 109 | + unsigned int virq, bit; | |
| 110 | + unsigned long val; | |
| 111 | + | |
| 112 | + vint_desc = irq_desc_get_handler_data(desc); | |
| 113 | + domain = vint_desc->domain; | |
| 114 | + inta = domain->host_data; | |
| 115 | + | |
| 116 | + chained_irq_enter(irq_desc_get_chip(desc), desc); | |
| 117 | + | |
| 118 | + val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 + | |
| 119 | + VINT_STATUS_OFFSET); | |
| 120 | + | |
| 121 | + for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) { | |
| 122 | + virq = irq_find_mapping(domain, vint_desc->events[bit].hwirq); | |
| 123 | + if (virq) | |
| 124 | + generic_handle_irq(virq); | |
| 125 | + } | |
| 126 | + | |
| 127 | + chained_irq_exit(irq_desc_get_chip(desc), desc); | |
| 128 | +} | |
| 129 | + | |
| 130 | +/** | |
| 131 | + * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator | |
| 132 | + * @domain: IRQ domain corresponding to Interrupt Aggregator | |
| 133 | + * | |
| 134 | + * Return 0 if all went well else corresponding error value. | |
| 135 | + */ | |
| 136 | +static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain) | |
| 137 | +{ | |
| 138 | + struct ti_sci_inta_irq_domain *inta = domain->host_data; | |
| 139 | + struct ti_sci_inta_vint_desc *vint_desc; | |
| 140 | + struct irq_fwspec parent_fwspec; | |
| 141 | + unsigned int parent_virq; | |
| 142 | + u16 vint_id; | |
| 143 | + | |
| 144 | + vint_id = ti_sci_get_free_resource(inta->vint); | |
| 145 | + if (vint_id == TI_SCI_RESOURCE_NULL) | |
| 146 | + return ERR_PTR(-EINVAL); | |
| 147 | + | |
| 148 | + vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); | |
| 149 | + if (!vint_desc) | |
| 150 | + return ERR_PTR(-ENOMEM); | |
| 151 | + | |
| 152 | + vint_desc->domain = domain; | |
| 153 | + vint_desc->vint_id = vint_id; | |
| 154 | + INIT_LIST_HEAD(&vint_desc->list); | |
| 155 | + | |
| 156 | + parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev))); | |
| 157 | + parent_fwspec.param_count = 2; | |
| 158 | + parent_fwspec.param[0] = inta->pdev->id; | |
| 159 | + parent_fwspec.param[1] = vint_desc->vint_id; | |
| 160 | + | |
| 161 | + parent_virq = irq_create_fwspec_mapping(&parent_fwspec); | |
| 162 | + if (parent_virq <= 0) { | |
| 163 | + kfree(vint_desc); | |
| 164 | + return ERR_PTR(parent_virq); | |
| 165 | + } | |
| 166 | + vint_desc->parent_virq = parent_virq; | |
| 167 | + | |
| 168 | + list_add_tail(&vint_desc->list, &inta->vint_list); | |
| 169 | + irq_set_chained_handler_and_data(vint_desc->parent_virq, | |
| 170 | + ti_sci_inta_irq_handler, vint_desc); | |
| 171 | + | |
| 172 | + return vint_desc; | |
| 173 | +} | |
| 174 | + | |
| 175 | +/** | |
| 176 | + * ti_sci_inta_alloc_event() - Attach an event to a IA vint. | |
| 177 | + * @vint_desc: Pointer to vint_desc to which the event gets attached | |
| 178 | + * @free_bit: Bit inside vint to which event gets attached | |
| 179 | + * @hwirq: hwirq of the input event | |
| 180 | + * | |
| 181 | + * Return event_desc pointer if all went ok else appropriate error value. | |
| 182 | + */ | |
| 183 | +static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_event(struct ti_sci_inta_vint_desc *vint_desc, | |
| 184 | + u16 free_bit, | |
| 185 | + u32 hwirq) | |
| 186 | +{ | |
| 187 | + struct ti_sci_inta_irq_domain *inta = vint_desc->domain->host_data; | |
| 188 | + struct ti_sci_inta_event_desc *event_desc; | |
| 189 | + u16 dev_id, dev_index; | |
| 190 | + int err; | |
| 191 | + | |
| 192 | + dev_id = HWIRQ_TO_DEVID(hwirq); | |
| 193 | + dev_index = HWIRQ_TO_IRQID(hwirq); | |
| 194 | + | |
| 195 | + event_desc = &vint_desc->events[free_bit]; | |
| 196 | + event_desc->hwirq = hwirq; | |
| 197 | + event_desc->vint_bit = free_bit; | |
| 198 | + event_desc->global_event = ti_sci_get_free_resource(inta->global_event); | |
| 199 | + if (event_desc->global_event == TI_SCI_RESOURCE_NULL) | |
| 200 | + return ERR_PTR(-EINVAL); | |
| 201 | + | |
| 202 | + err = inta->sci->ops.rm_irq_ops.set_event_map(inta->sci, | |
| 203 | + dev_id, dev_index, | |
| 204 | + inta->pdev->id, | |
| 205 | + vint_desc->vint_id, | |
| 206 | + event_desc->global_event, | |
| 207 | + free_bit); | |
| 208 | + if (err) | |
| 209 | + goto free_global_event; | |
| 210 | + | |
| 211 | + return event_desc; | |
| 212 | +free_global_event: | |
| 213 | + ti_sci_release_resource(inta->global_event, event_desc->global_event); | |
| 214 | + return ERR_PTR(err); | |
| 215 | +} | |
| 216 | + | |
| 217 | +/** | |
| 218 | + * ti_sci_inta_alloc_irq() - Allocate an irq within INTA domain | |
| 219 | + * @domain: irq_domain pointer corresponding to INTA | |
| 220 | + * @hwirq: hwirq of the input event | |
| 221 | + * | |
| 222 | + * Note: Allocation happens in the following manner: | |
| 223 | + * - Find a free bit available in any of the vints available in the list. | |
| 224 | + * - If not found, allocate a vint from the vint pool | |
| 225 | + * - Attach the free bit to input hwirq. | |
| 226 | + * Return event_desc if all went ok else appropriate error value. | |
| 227 | + */ | |
| 228 | +static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *domain, | |
| 229 | + u32 hwirq) | |
| 230 | +{ | |
| 231 | + struct ti_sci_inta_irq_domain *inta = domain->host_data; | |
| 232 | + struct ti_sci_inta_vint_desc *vint_desc = NULL; | |
| 233 | + struct ti_sci_inta_event_desc *event_desc; | |
| 234 | + u16 free_bit; | |
| 235 | + | |
| 236 | + mutex_lock(&inta->vint_mutex); | |
| 237 | + list_for_each_entry(vint_desc, &inta->vint_list, list) { | |
| 238 | + free_bit = find_first_zero_bit(vint_desc->event_map, | |
| 239 | + MAX_EVENTS_PER_VINT); | |
| 240 | + if (free_bit != MAX_EVENTS_PER_VINT) { | |
| 241 | + set_bit(free_bit, vint_desc->event_map); | |
| 242 | + goto alloc_event; | |
| 243 | + } | |
| 244 | + } | |
| 245 | + | |
| 246 | + /* No free bits available. Allocate a new vint */ | |
| 247 | + vint_desc = ti_sci_inta_alloc_parent_irq(domain); | |
| 248 | + if (IS_ERR(vint_desc)) { | |
| 249 | + mutex_unlock(&inta->vint_mutex); | |
| 250 | + return ERR_PTR(PTR_ERR(vint_desc)); | |
| 251 | + } | |
| 252 | + | |
| 253 | + free_bit = find_first_zero_bit(vint_desc->event_map, | |
| 254 | + MAX_EVENTS_PER_VINT); | |
| 255 | + set_bit(free_bit, vint_desc->event_map); | |
| 256 | + | |
| 257 | +alloc_event: | |
| 258 | + event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq); | |
| 259 | + if (IS_ERR(event_desc)) | |
| 260 | + clear_bit(free_bit, vint_desc->event_map); | |
| 261 | + | |
| 262 | + mutex_unlock(&inta->vint_mutex); | |
| 263 | + return event_desc; | |
| 264 | +} | |
| 265 | + | |
| 266 | +/** | |
| 267 | + * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA | |
| 268 | + * @inta: Pointer to inta domain. | |
| 269 | + * @vint_desc: Pointer to vint_desc that needs to be freed. | |
| 270 | + */ | |
| 271 | +static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta, | |
| 272 | + struct ti_sci_inta_vint_desc *vint_desc) | |
| 273 | +{ | |
| 274 | + if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) { | |
| 275 | + list_del(&vint_desc->list); | |
| 276 | + ti_sci_release_resource(inta->vint, vint_desc->vint_id); | |
| 277 | + irq_dispose_mapping(vint_desc->parent_virq); | |
| 278 | + kfree(vint_desc); | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +/** | |
| 283 | + * ti_sci_inta_free_irq() - Free an IRQ within INTA domain | |
| 284 | + * @event_desc: Pointer to event_desc that needs to be freed. | |
| 285 | + * @hwirq: Hwirq number within INTA domain that needs to be freed | |
| 286 | + */ | |
| 287 | +static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc, | |
| 288 | + u32 hwirq) | |
| 289 | +{ | |
| 290 | + struct ti_sci_inta_vint_desc *vint_desc; | |
| 291 | + struct ti_sci_inta_irq_domain *inta; | |
| 292 | + | |
| 293 | + vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); | |
| 294 | + inta = vint_desc->domain->host_data; | |
| 295 | + /* free event irq */ | |
| 296 | + mutex_lock(&inta->vint_mutex); | |
| 297 | + inta->sci->ops.rm_irq_ops.free_event_map(inta->sci, | |
| 298 | + HWIRQ_TO_DEVID(hwirq), | |
| 299 | + HWIRQ_TO_IRQID(hwirq), | |
| 300 | + inta->pdev->id, | |
| 301 | + vint_desc->vint_id, | |
| 302 | + event_desc->global_event, | |
| 303 | + event_desc->vint_bit); | |
| 304 | + | |
| 305 | + clear_bit(event_desc->vint_bit, vint_desc->event_map); | |
| 306 | + ti_sci_release_resource(inta->global_event, event_desc->global_event); | |
| 307 | + event_desc->global_event = TI_SCI_RESOURCE_NULL; | |
| 308 | + event_desc->hwirq = 0; | |
| 309 | + | |
| 310 | + ti_sci_inta_free_parent_irq(inta, vint_desc); | |
| 311 | + mutex_unlock(&inta->vint_mutex); | |
| 312 | +} | |
| 313 | + | |
| 314 | +/** | |
| 315 | + * ti_sci_inta_request_resources() - Allocate resources for input irq | |
| 316 | + * @data: Pointer to corresponding irq_data | |
| 317 | + * | |
| 318 | + * Note: This is the core api where the actual allocation happens for input | |
| 319 | + * hwirq. This allocation involves creating a parent irq for vint. | |
| 320 | + * If this is done in irq_domain_ops.alloc() then a deadlock is reached | |
| 321 | + * for allocation. So this allocation is being done in request_resources() | |
| 322 | + * | |
| 323 | + * Return: 0 if all went well else corresponding error. | |
| 324 | + */ | |
| 325 | +static int ti_sci_inta_request_resources(struct irq_data *data) | |
| 326 | +{ | |
| 327 | + struct ti_sci_inta_event_desc *event_desc; | |
| 328 | + | |
| 329 | + event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq); | |
| 330 | + if (IS_ERR(event_desc)) | |
| 331 | + return PTR_ERR(event_desc); | |
| 332 | + | |
| 333 | + data->chip_data = event_desc; | |
| 334 | + | |
| 335 | + return 0; | |
| 336 | +} | |
| 337 | + | |
| 338 | +/** | |
| 339 | + * ti_sci_inta_release_resources - Release resources for input irq | |
| 340 | + * @data: Pointer to corresponding irq_data | |
| 341 | + * | |
| 342 | + * Note: Corresponding to request_resources(), all the unmapping and deletion | |
| 343 | + * of parent vint irqs happens in this api. | |
| 344 | + */ | |
| 345 | +static void ti_sci_inta_release_resources(struct irq_data *data) | |
| 346 | +{ | |
| 347 | + struct ti_sci_inta_event_desc *event_desc; | |
| 348 | + | |
| 349 | + event_desc = irq_data_get_irq_chip_data(data); | |
| 350 | + ti_sci_inta_free_irq(event_desc, data->hwirq); | |
| 351 | +} | |
| 352 | + | |
| 353 | +/** | |
| 354 | + * ti_sci_inta_manage_event() - Control the event based on the offset | |
| 355 | + * @data: Pointer to corresponding irq_data | |
| 356 | + * @offset: register offset using which event is controlled. | |
| 357 | + */ | |
| 358 | +static void ti_sci_inta_manage_event(struct irq_data *data, u32 offset) | |
| 359 | +{ | |
| 360 | + struct ti_sci_inta_event_desc *event_desc; | |
| 361 | + struct ti_sci_inta_vint_desc *vint_desc; | |
| 362 | + struct ti_sci_inta_irq_domain *inta; | |
| 363 | + | |
| 364 | + event_desc = irq_data_get_irq_chip_data(data); | |
| 365 | + vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); | |
| 366 | + inta = data->domain->host_data; | |
| 367 | + | |
| 368 | + writeq_relaxed(BIT(event_desc->vint_bit), | |
| 369 | + inta->base + vint_desc->vint_id * 0x1000 + offset); | |
| 370 | +} | |
| 371 | + | |
| 372 | +/** | |
| 373 | + * ti_sci_inta_mask_irq() - Mask an event | |
| 374 | + * @data: Pointer to corresponding irq_data | |
| 375 | + */ | |
| 376 | +static void ti_sci_inta_mask_irq(struct irq_data *data) | |
| 377 | +{ | |
| 378 | + ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET); | |
| 379 | +} | |
| 380 | + | |
| 381 | +/** | |
| 382 | + * ti_sci_inta_unmask_irq() - Unmask an event | |
| 383 | + * @data: Pointer to corresponding irq_data | |
| 384 | + */ | |
| 385 | +static void ti_sci_inta_unmask_irq(struct irq_data *data) | |
| 386 | +{ | |
| 387 | + ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET); | |
| 388 | +} | |
| 389 | + | |
| 390 | +/** | |
| 391 | + * ti_sci_inta_ack_irq() - Ack an event | |
| 392 | + * @data: Pointer to corresponding irq_data | |
| 393 | + */ | |
| 394 | +static void ti_sci_inta_ack_irq(struct irq_data *data) | |
| 395 | +{ | |
| 396 | + /* | |
| 397 | + * Do not clear the event if hardware is capable of sending | |
| 398 | + * a down event. | |
| 399 | + */ | |
| 400 | + if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH) | |
| 401 | + ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET); | |
| 402 | +} | |
| 403 | + | |
| 404 | +static int ti_sci_inta_set_affinity(struct irq_data *d, | |
| 405 | + const struct cpumask *mask_val, bool force) | |
| 406 | +{ | |
| 407 | + return -EINVAL; | |
| 408 | +} | |
| 409 | + | |
| 410 | +/** | |
| 411 | + * ti_sci_inta_set_type() - Update the trigger type of the irq. | |
| 412 | + * @data: Pointer to corresponding irq_data | |
| 413 | + * @type: Trigger type as specified by user | |
| 414 | + * | |
| 415 | + * Note: This updates the handle_irq callback for level msi. | |
| 416 | + * | |
| 417 | + * Return 0 if all went well else appropriate error. | |
| 418 | + */ | |
| 419 | +static int ti_sci_inta_set_type(struct irq_data *data, unsigned int type) | |
| 420 | +{ | |
| 421 | + /* | |
| 422 | + * .alloc default sets handle_edge_irq. But if the user specifies | |
| 423 | + * that IRQ is level MSI, then update the handle to handle_level_irq | |
| 424 | + */ | |
| 425 | + switch (type & IRQ_TYPE_SENSE_MASK) { | |
| 426 | + case IRQF_TRIGGER_HIGH: | |
| 427 | + irq_set_handler_locked(data, handle_level_irq); | |
| 428 | + return 0; | |
| 429 | + case IRQF_TRIGGER_RISING: | |
| 430 | + return 0; | |
| 431 | + default: | |
| 432 | + return -EINVAL; | |
| 433 | + } | |
| 434 | + | |
| 435 | + return -EINVAL; | |
| 436 | +} | |
| 437 | + | |
| 438 | +static struct irq_chip ti_sci_inta_irq_chip = { | |
| 439 | + .name = "INTA", | |
| 440 | + .irq_ack = ti_sci_inta_ack_irq, | |
| 441 | + .irq_mask = ti_sci_inta_mask_irq, | |
| 442 | + .irq_set_type = ti_sci_inta_set_type, | |
| 443 | + .irq_unmask = ti_sci_inta_unmask_irq, | |
| 444 | + .irq_set_affinity = ti_sci_inta_set_affinity, | |
| 445 | + .irq_request_resources = ti_sci_inta_request_resources, | |
| 446 | + .irq_release_resources = ti_sci_inta_release_resources, | |
| 447 | +}; | |
| 448 | + | |
| 449 | +/** | |
| 450 | + * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain | |
| 451 | + * @domain: Domain to which the irqs belong | |
| 452 | + * @virq: base linux virtual IRQ to be freed. | |
| 453 | + * @nr_irqs: Number of continuous irqs to be freed | |
| 454 | + */ | |
| 455 | +static void ti_sci_inta_irq_domain_free(struct irq_domain *domain, | |
| 456 | + unsigned int virq, unsigned int nr_irqs) | |
| 457 | +{ | |
| 458 | + struct irq_data *data = irq_domain_get_irq_data(domain, virq); | |
| 459 | + | |
| 460 | + irq_domain_reset_irq_data(data); | |
| 461 | +} | |
| 462 | + | |
| 463 | +/** | |
| 464 | + * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs | |
| 465 | + * @domain: Point to the interrupt aggregator IRQ domain | |
| 466 | + * @virq: Corresponding Linux virtual IRQ number | |
| 467 | + * @nr_irqs: Continuous irqs to be allocated | |
| 468 | + * @data: Pointer to firmware specifier | |
| 469 | + * | |
| 470 | + * No actual allocation happens here. | |
| 471 | + * | |
| 472 | + * Return 0 if all went well else appropriate error value. | |
| 473 | + */ | |
| 474 | +static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain, | |
| 475 | + unsigned int virq, unsigned int nr_irqs, | |
| 476 | + void *data) | |
| 477 | +{ | |
| 478 | + msi_alloc_info_t *arg = data; | |
| 479 | + | |
| 480 | + irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip, | |
| 481 | + NULL, handle_edge_irq, NULL, NULL); | |
| 482 | + | |
| 483 | + return 0; | |
| 484 | +} | |
| 485 | + | |
| 486 | +static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { | |
| 487 | + .free = ti_sci_inta_irq_domain_free, | |
| 488 | + .alloc = ti_sci_inta_irq_domain_alloc, | |
| 489 | +}; | |
| 490 | + | |
| 491 | +static struct irq_chip ti_sci_inta_msi_irq_chip = { | |
| 492 | + .name = "MSI-INTA", | |
| 493 | + .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, | |
| 494 | +}; | |
| 495 | + | |
| 496 | +static void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg, | |
| 497 | + struct msi_desc *desc) | |
| 498 | +{ | |
| 499 | + struct platform_device *pdev = to_platform_device(desc->dev); | |
| 500 | + | |
| 501 | + arg->desc = desc; | |
| 502 | + arg->hwirq = TO_HWIRQ(pdev->id, desc->inta.dev_index); | |
| 503 | +} | |
| 504 | + | |
| 505 | +static struct msi_domain_ops ti_sci_inta_msi_ops = { | |
| 506 | + .set_desc = ti_sci_inta_msi_set_desc, | |
| 507 | +}; | |
| 508 | + | |
| 509 | +static struct msi_domain_info ti_sci_inta_msi_domain_info = { | |
| 510 | + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | |
| 511 | + MSI_FLAG_LEVEL_CAPABLE), | |
| 512 | + .ops = &ti_sci_inta_msi_ops, | |
| 513 | + .chip = &ti_sci_inta_msi_irq_chip, | |
| 514 | +}; | |
| 515 | + | |
| 516 | +static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) | |
| 517 | +{ | |
| 518 | + struct irq_domain *parent_domain, *domain, *msi_domain; | |
| 519 | + struct device_node *parent_node, *node; | |
| 520 | + struct ti_sci_inta_irq_domain *inta; | |
| 521 | + struct device *dev = &pdev->dev; | |
| 522 | + struct resource *res; | |
| 523 | + int ret; | |
| 524 | + | |
| 525 | + node = dev_of_node(dev); | |
| 526 | + parent_node = of_irq_find_parent(node); | |
| 527 | + if (!parent_node) { | |
| 528 | + dev_err(dev, "Failed to get IRQ parent node\n"); | |
| 529 | + return -ENODEV; | |
| 530 | + } | |
| 531 | + | |
| 532 | + parent_domain = irq_find_host(parent_node); | |
| 533 | + if (!parent_domain) | |
| 534 | + return -EPROBE_DEFER; | |
| 535 | + | |
| 536 | + inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL); | |
| 537 | + if (!inta) | |
| 538 | + return -ENOMEM; | |
| 539 | + | |
| 540 | + inta->pdev = pdev; | |
| 541 | + inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); | |
| 542 | + if (IS_ERR(inta->sci)) { | |
| 543 | + ret = PTR_ERR(inta->sci); | |
| 544 | + if (ret != -EPROBE_DEFER) | |
| 545 | + dev_err(dev, "ti,sci read fail %d\n", ret); | |
| 546 | + inta->sci = NULL; | |
| 547 | + return ret; | |
| 548 | + } | |
| 549 | + | |
| 550 | + ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &pdev->id); | |
| 551 | + if (ret) { | |
| 552 | + dev_err(dev, "missing 'ti,sci-dev-id' property\n"); | |
| 553 | + return -EINVAL; | |
| 554 | + } | |
| 555 | + | |
| 556 | + inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, | |
| 557 | + "ti,sci-rm-range-vint"); | |
| 558 | + if (IS_ERR(inta->vint)) { | |
| 559 | + dev_err(dev, "VINT resource allocation failed\n"); | |
| 560 | + return PTR_ERR(inta->vint); | |
| 561 | + } | |
| 562 | + | |
| 563 | + inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, | |
| 564 | + "ti,sci-rm-range-global-event"); | |
| 565 | + if (IS_ERR(inta->global_event)) { | |
| 566 | + dev_err(dev, "Global event resource allocation failed\n"); | |
| 567 | + return PTR_ERR(inta->global_event); | |
| 568 | + } | |
| 569 | + | |
| 570 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 571 | + inta->base = devm_ioremap_resource(dev, res); | |
| 572 | + if (IS_ERR(inta->base)) | |
| 573 | + return -ENODEV; | |
| 574 | + | |
| 575 | + domain = irq_domain_add_linear(dev_of_node(dev), | |
| 576 | + ti_sci_get_num_resources(inta->vint), | |
| 577 | + &ti_sci_inta_irq_domain_ops, inta); | |
| 578 | + if (!domain) { | |
| 579 | + dev_err(dev, "Failed to allocate IRQ domain\n"); | |
| 580 | + return -ENOMEM; | |
| 581 | + } | |
| 582 | + | |
| 583 | + msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), | |
| 584 | + &ti_sci_inta_msi_domain_info, | |
| 585 | + domain); | |
| 586 | + if (!msi_domain) { | |
| 587 | + irq_domain_remove(domain); | |
| 588 | + dev_err(dev, "Failed to allocate msi domain\n"); | |
| 589 | + return -ENOMEM; | |
| 590 | + } | |
| 591 | + | |
| 592 | + INIT_LIST_HEAD(&inta->vint_list); | |
| 593 | + mutex_init(&inta->vint_mutex); | |
| 594 | + | |
| 595 | + return 0; | |
| 596 | +} | |
| 597 | + | |
| 598 | +static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = { | |
| 599 | + { .compatible = "ti,sci-inta", }, | |
| 600 | + { /* sentinel */ }, | |
| 601 | +}; | |
| 602 | +MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match); | |
| 603 | + | |
| 604 | +static struct platform_driver ti_sci_inta_irq_domain_driver = { | |
| 605 | + .probe = ti_sci_inta_irq_domain_probe, | |
| 606 | + .driver = { | |
| 607 | + .name = "ti-sci-inta", | |
| 608 | + .of_match_table = ti_sci_inta_irq_domain_of_match, | |
| 609 | + }, | |
| 610 | +}; | |
| 611 | +module_platform_driver(ti_sci_inta_irq_domain_driver); | |
| 612 | + | |
| 613 | +MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); | |
| 614 | +MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); | |
| 615 | +MODULE_LICENSE("GPL v2"); | |
| @@ -0,0 +1,275 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Texas Instruments' K3 Interrupt Router irqchip driver | |
| 4 | + * | |
| 5 | + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | |
| 6 | + * Lokesh Vutla <lokeshvutla@ti.com> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/module.h> | |
| 11 | +#include <linux/moduleparam.h> | |
| 12 | +#include <linux/io.h> | |
| 13 | +#include <linux/irqchip.h> | |
| 14 | +#include <linux/irqdomain.h> | |
| 15 | +#include <linux/of_platform.h> | |
| 16 | +#include <linux/of_address.h> | |
| 17 | +#include <linux/of_irq.h> | |
| 18 | +#include <linux/soc/ti/ti_sci_protocol.h> | |
| 19 | + | |
| 20 | +#define TI_SCI_DEV_ID_MASK 0xffff | |
| 21 | +#define TI_SCI_DEV_ID_SHIFT 16 | |
| 22 | +#define TI_SCI_IRQ_ID_MASK 0xffff | |
| 23 | +#define TI_SCI_IRQ_ID_SHIFT 0 | |
| 24 | +#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ | |
| 25 | + (TI_SCI_DEV_ID_MASK)) | |
| 26 | +#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) | |
| 27 | +#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ | |
| 28 | + TI_SCI_DEV_ID_SHIFT) | \ | |
| 29 | + ((index) & TI_SCI_IRQ_ID_MASK)) | |
| 30 | + | |
| 31 | +/** | |
| 32 | + * struct ti_sci_intr_irq_domain - Structure representing a TISCI based | |
| 33 | + * Interrupt Router IRQ domain. | |
| 34 | + * @sci: Pointer to TISCI handle | |
| 35 | + * @dst_irq: TISCI resource pointer representing GIC irq controller. | |
| 36 | + * @dst_id: TISCI device ID of the GIC irq controller. | |
| 37 | + * @type: Specifies the trigger type supported by this Interrupt Router | |
| 38 | + */ | |
| 39 | +struct ti_sci_intr_irq_domain { | |
| 40 | + const struct ti_sci_handle *sci; | |
| 41 | + struct ti_sci_resource *dst_irq; | |
| 42 | + u32 dst_id; | |
| 43 | + u32 type; | |
| 44 | +}; | |
| 45 | + | |
| 46 | +static struct irq_chip ti_sci_intr_irq_chip = { | |
| 47 | + .name = "INTR", | |
| 48 | + .irq_eoi = irq_chip_eoi_parent, | |
| 49 | + .irq_mask = irq_chip_mask_parent, | |
| 50 | + .irq_unmask = irq_chip_unmask_parent, | |
| 51 | + .irq_set_type = irq_chip_set_type_parent, | |
| 52 | + .irq_retrigger = irq_chip_retrigger_hierarchy, | |
| 53 | + .irq_set_affinity = irq_chip_set_affinity_parent, | |
| 54 | +}; | |
| 55 | + | |
| 56 | +/** | |
| 57 | + * ti_sci_intr_irq_domain_translate() - Retrieve hwirq and type from | |
| 58 | + * IRQ firmware specific handler. | |
| 59 | + * @domain: Pointer to IRQ domain | |
| 60 | + * @fwspec: Pointer to IRQ specific firmware structure | |
| 61 | + * @hwirq: IRQ number identified by hardware | |
| 62 | + * @type: IRQ type | |
| 63 | + * | |
| 64 | + * Return 0 if all went ok else appropriate error. | |
| 65 | + */ | |
| 66 | +static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain, | |
| 67 | + struct irq_fwspec *fwspec, | |
| 68 | + unsigned long *hwirq, | |
| 69 | + unsigned int *type) | |
| 70 | +{ | |
| 71 | + struct ti_sci_intr_irq_domain *intr = domain->host_data; | |
| 72 | + | |
| 73 | + if (fwspec->param_count != 2) | |
| 74 | + return -EINVAL; | |
| 75 | + | |
| 76 | + *hwirq = TO_HWIRQ(fwspec->param[0], fwspec->param[1]); | |
| 77 | + *type = intr->type; | |
| 78 | + | |
| 79 | + return 0; | |
| 80 | +} | |
| 81 | + | |
| 82 | +/** | |
| 83 | + * ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain. | |
| 84 | + * @domain: Domain to which the irqs belong | |
| 85 | + * @virq: Linux virtual IRQ to be freed. | |
| 86 | + * @nr_irqs: Number of continuous irqs to be freed | |
| 87 | + */ | |
| 88 | +static void ti_sci_intr_irq_domain_free(struct irq_domain *domain, | |
| 89 | + unsigned int virq, unsigned int nr_irqs) | |
| 90 | +{ | |
| 91 | + struct ti_sci_intr_irq_domain *intr = domain->host_data; | |
| 92 | + struct irq_data *data, *parent_data; | |
| 93 | + u16 dev_id, irq_index; | |
| 94 | + | |
| 95 | + parent_data = irq_domain_get_irq_data(domain->parent, virq); | |
| 96 | + data = irq_domain_get_irq_data(domain, virq); | |
| 97 | + irq_index = HWIRQ_TO_IRQID(data->hwirq); | |
| 98 | + dev_id = HWIRQ_TO_DEVID(data->hwirq); | |
| 99 | + | |
| 100 | + intr->sci->ops.rm_irq_ops.free_irq(intr->sci, dev_id, irq_index, | |
| 101 | + intr->dst_id, parent_data->hwirq); | |
| 102 | + ti_sci_release_resource(intr->dst_irq, parent_data->hwirq); | |
| 103 | + irq_domain_free_irqs_parent(domain, virq, 1); | |
| 104 | + irq_domain_reset_irq_data(data); | |
| 105 | +} | |
| 106 | + | |
| 107 | +/** | |
| 108 | + * ti_sci_intr_alloc_gic_irq() - Allocate GIC specific IRQ | |
| 109 | + * @domain: Pointer to the interrupt router IRQ domain | |
| 110 | + * @virq: Corresponding Linux virtual IRQ number | |
| 111 | + * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain | |
| 112 | + * | |
| 113 | + * Returns 0 if all went well else appropriate error pointer. | |
| 114 | + */ | |
| 115 | +static int ti_sci_intr_alloc_gic_irq(struct irq_domain *domain, | |
| 116 | + unsigned int virq, u32 hwirq) | |
| 117 | +{ | |
| 118 | + struct ti_sci_intr_irq_domain *intr = domain->host_data; | |
| 119 | + struct irq_fwspec fwspec; | |
| 120 | + u16 dev_id, irq_index; | |
| 121 | + u16 dst_irq; | |
| 122 | + int err; | |
| 123 | + | |
| 124 | + dev_id = HWIRQ_TO_DEVID(hwirq); | |
| 125 | + irq_index = HWIRQ_TO_IRQID(hwirq); | |
| 126 | + | |
| 127 | + dst_irq = ti_sci_get_free_resource(intr->dst_irq); | |
| 128 | + if (dst_irq == TI_SCI_RESOURCE_NULL) | |
| 129 | + return -EINVAL; | |
| 130 | + | |
| 131 | + fwspec.fwnode = domain->parent->fwnode; | |
| 132 | + fwspec.param_count = 3; | |
| 133 | + fwspec.param[0] = 0; /* SPI */ | |
| 134 | + fwspec.param[1] = dst_irq - 32; /* SPI offset */ | |
| 135 | + fwspec.param[2] = intr->type; | |
| 136 | + | |
| 137 | + err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); | |
| 138 | + if (err) | |
| 139 | + goto err_irqs; | |
| 140 | + | |
| 141 | + err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, dev_id, irq_index, | |
| 142 | + intr->dst_id, dst_irq); | |
| 143 | + if (err) | |
| 144 | + goto err_msg; | |
| 145 | + | |
| 146 | + return 0; | |
| 147 | + | |
| 148 | +err_msg: | |
| 149 | + irq_domain_free_irqs_parent(domain, virq, 1); | |
| 150 | +err_irqs: | |
| 151 | + ti_sci_release_resource(intr->dst_irq, dst_irq); | |
| 152 | + return err; | |
| 153 | +} | |
| 154 | + | |
| 155 | +/** | |
| 156 | + * ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs | |
| 157 | + * @domain: Point to the interrupt router IRQ domain | |
| 158 | + * @virq: Corresponding Linux virtual IRQ number | |
| 159 | + * @nr_irqs: Continuous irqs to be allocated | |
| 160 | + * @data: Pointer to firmware specifier | |
| 161 | + * | |
| 162 | + * Return 0 if all went well else appropriate error value. | |
| 163 | + */ | |
| 164 | +static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain, | |
| 165 | + unsigned int virq, unsigned int nr_irqs, | |
| 166 | + void *data) | |
| 167 | +{ | |
| 168 | + struct irq_fwspec *fwspec = data; | |
| 169 | + unsigned long hwirq; | |
| 170 | + unsigned int flags; | |
| 171 | + int err; | |
| 172 | + | |
| 173 | + err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags); | |
| 174 | + if (err) | |
| 175 | + return err; | |
| 176 | + | |
| 177 | + err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq); | |
| 178 | + if (err) | |
| 179 | + return err; | |
| 180 | + | |
| 181 | + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, | |
| 182 | + &ti_sci_intr_irq_chip, NULL); | |
| 183 | + | |
| 184 | + return 0; | |
| 185 | +} | |
| 186 | + | |
| 187 | +static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = { | |
| 188 | + .free = ti_sci_intr_irq_domain_free, | |
| 189 | + .alloc = ti_sci_intr_irq_domain_alloc, | |
| 190 | + .translate = ti_sci_intr_irq_domain_translate, | |
| 191 | +}; | |
| 192 | + | |
| 193 | +static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev) | |
| 194 | +{ | |
| 195 | + struct irq_domain *parent_domain, *domain; | |
| 196 | + struct ti_sci_intr_irq_domain *intr; | |
| 197 | + struct device_node *parent_node; | |
| 198 | + struct device *dev = &pdev->dev; | |
| 199 | + int ret; | |
| 200 | + | |
| 201 | + parent_node = of_irq_find_parent(dev_of_node(dev)); | |
| 202 | + if (!parent_node) { | |
| 203 | + dev_err(dev, "Failed to get IRQ parent node\n"); | |
| 204 | + return -ENODEV; | |
| 205 | + } | |
| 206 | + | |
| 207 | + parent_domain = irq_find_host(parent_node); | |
| 208 | + if (!parent_domain) { | |
| 209 | + dev_err(dev, "Failed to find IRQ parent domain\n"); | |
| 210 | + return -ENODEV; | |
| 211 | + } | |
| 212 | + | |
| 213 | + intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL); | |
| 214 | + if (!intr) | |
| 215 | + return -ENOMEM; | |
| 216 | + | |
| 217 | + ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", | |
| 218 | + &intr->type); | |
| 219 | + if (ret) { | |
| 220 | + dev_err(dev, "missing ti,intr-trigger-type property\n"); | |
| 221 | + return -EINVAL; | |
| 222 | + } | |
| 223 | + | |
| 224 | + intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); | |
| 225 | + if (IS_ERR(intr->sci)) { | |
| 226 | + ret = PTR_ERR(intr->sci); | |
| 227 | + if (ret != -EPROBE_DEFER) | |
| 228 | + dev_err(dev, "ti,sci read fail %d\n", ret); | |
| 229 | + intr->sci = NULL; | |
| 230 | + return ret; | |
| 231 | + } | |
| 232 | + | |
| 233 | + ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id", | |
| 234 | + &intr->dst_id); | |
| 235 | + if (ret) { | |
| 236 | + dev_err(dev, "missing 'ti,sci-dst-id' property\n"); | |
| 237 | + return -EINVAL; | |
| 238 | + } | |
| 239 | + | |
| 240 | + intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev, | |
| 241 | + intr->dst_id, | |
| 242 | + "ti,sci-rm-range-girq"); | |
| 243 | + if (IS_ERR(intr->dst_irq)) { | |
| 244 | + dev_err(dev, "Destination irq resource allocation failed\n"); | |
| 245 | + return PTR_ERR(intr->dst_irq); | |
| 246 | + } | |
| 247 | + | |
| 248 | + domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev), | |
| 249 | + &ti_sci_intr_irq_domain_ops, intr); | |
| 250 | + if (!domain) { | |
| 251 | + dev_err(dev, "Failed to allocate IRQ domain\n"); | |
| 252 | + return -ENOMEM; | |
| 253 | + } | |
| 254 | + | |
| 255 | + return 0; | |
| 256 | +} | |
| 257 | + | |
| 258 | +static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = { | |
| 259 | + { .compatible = "ti,sci-intr", }, | |
| 260 | + { /* sentinel */ }, | |
| 261 | +}; | |
| 262 | +MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match); | |
| 263 | + | |
| 264 | +static struct platform_driver ti_sci_intr_irq_domain_driver = { | |
| 265 | + .probe = ti_sci_intr_irq_domain_probe, | |
| 266 | + .driver = { | |
| 267 | + .name = "ti-sci-intr", | |
| 268 | + .of_match_table = ti_sci_intr_irq_domain_of_match, | |
| 269 | + }, | |
| 270 | +}; | |
| 271 | +module_platform_driver(ti_sci_intr_irq_domain_driver); | |
| 272 | + | |
| 273 | +MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); | |
| 274 | +MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol"); | |
| 275 | +MODULE_LICENSE("GPL v2"); | |
| @@ -74,4 +74,10 @@ config TI_SCI_PM_DOMAINS · 73 unmodified lines | ||
| 74 | 74 | called ti_sci_pm_domains. Note this is needed early in boot before |
| 75 | 75 | rootfs may be available. |
| 76 | 76 | |
| 77 | +config TI_SCI_INTA_MSI_DOMAIN | |
| 78 | + bool | |
| 79 | + select GENERIC_MSI_IRQ_DOMAIN | |
| 80 | + help | |
| 81 | + Driver to enable Interrupt Aggregator specific MSI Domain. | |
| 82 | + | |
| 77 | 83 | endif # SOC_TI |
| @@ -8,3 +8,4 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o · 7 unmodified lines | ||
| 8 | 8 | obj-$(CONFIG_AMX3_PM) += pm33xx.o |
| 9 | 9 | obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o |
| 10 | 10 | obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o |
| 11 | +obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o | |
| @@ -0,0 +1,146 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Texas Instruments' K3 Interrupt Aggregator MSI bus | |
| 4 | + * | |
| 5 | + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | |
| 6 | + * Lokesh Vutla <lokeshvutla@ti.com> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/irq.h> | |
| 10 | +#include <linux/irqdomain.h> | |
| 11 | +#include <linux/msi.h> | |
| 12 | +#include <linux/of_address.h> | |
| 13 | +#include <linux/of_device.h> | |
| 14 | +#include <linux/of_irq.h> | |
| 15 | +#include <linux/soc/ti/ti_sci_inta_msi.h> | |
| 16 | +#include <linux/soc/ti/ti_sci_protocol.h> | |
| 17 | + | |
| 18 | +static void ti_sci_inta_msi_write_msg(struct irq_data *data, | |
| 19 | + struct msi_msg *msg) | |
| 20 | +{ | |
| 21 | + /* Nothing to do */ | |
| 22 | +} | |
| 23 | + | |
| 24 | +static void ti_sci_inta_msi_compose_msi_msg(struct irq_data *data, | |
| 25 | + struct msi_msg *msg) | |
| 26 | +{ | |
| 27 | + /* Nothing to do */ | |
| 28 | +} | |
| 29 | + | |
| 30 | +static void ti_sci_inta_msi_update_chip_ops(struct msi_domain_info *info) | |
| 31 | +{ | |
| 32 | + struct irq_chip *chip = info->chip; | |
| 33 | + | |
| 34 | + if (WARN_ON(!chip)) | |
| 35 | + return; | |
| 36 | + | |
| 37 | + chip->irq_request_resources = irq_chip_request_resources_parent; | |
| 38 | + chip->irq_release_resources = irq_chip_release_resources_parent; | |
| 39 | + chip->irq_compose_msi_msg = ti_sci_inta_msi_compose_msi_msg; | |
| 40 | + chip->irq_write_msi_msg = ti_sci_inta_msi_write_msg; | |
| 41 | + chip->irq_set_type = irq_chip_set_type_parent; | |
| 42 | + chip->irq_unmask = irq_chip_unmask_parent; | |
| 43 | + chip->irq_mask = irq_chip_mask_parent; | |
| 44 | + chip->irq_ack = irq_chip_ack_parent; | |
| 45 | +} | |
| 46 | + | |
| 47 | +struct irq_domain *ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode, | |
| 48 | + struct msi_domain_info *info, | |
| 49 | + struct irq_domain *parent) | |
| 50 | +{ | |
| 51 | + struct irq_domain *domain; | |
| 52 | + | |
| 53 | + ti_sci_inta_msi_update_chip_ops(info); | |
| 54 | + | |
| 55 | + domain = msi_create_irq_domain(fwnode, info, parent); | |
| 56 | + if (domain) | |
| 57 | + irq_domain_update_bus_token(domain, DOMAIN_BUS_TI_SCI_INTA_MSI); | |
| 58 | + | |
| 59 | + return domain; | |
| 60 | +} | |
| 61 | +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_create_irq_domain); | |
| 62 | + | |
| 63 | +static void ti_sci_inta_msi_free_descs(struct device *dev) | |
| 64 | +{ | |
| 65 | + struct msi_desc *desc, *tmp; | |
| 66 | + | |
| 67 | + list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { | |
| 68 | + list_del(&desc->list); | |
| 69 | + free_msi_entry(desc); | |
| 70 | + } | |
| 71 | +} | |
| 72 | + | |
| 73 | +static int ti_sci_inta_msi_alloc_descs(struct device *dev, | |
| 74 | + struct ti_sci_resource *res) | |
| 75 | +{ | |
| 76 | + struct msi_desc *msi_desc; | |
| 77 | + int set, i, count = 0; | |
| 78 | + | |
| 79 | + for (set = 0; set < res->sets; set++) { | |
| 80 | + for (i = 0; i < res->desc[set].num; i++) { | |
| 81 | + msi_desc = alloc_msi_entry(dev, 1, NULL); | |
| 82 | + if (!msi_desc) { | |
| 83 | + ti_sci_inta_msi_free_descs(dev); | |
| 84 | + return -ENOMEM; | |
| 85 | + } | |
| 86 | + | |
| 87 | + msi_desc->inta.dev_index = res->desc[set].start + i; | |
| 88 | + INIT_LIST_HEAD(&msi_desc->list); | |
| 89 | + list_add_tail(&msi_desc->list, dev_to_msi_list(dev)); | |
| 90 | + count++; | |
| 91 | + } | |
| 92 | + } | |
| 93 | + | |
| 94 | + return count; | |
| 95 | +} | |
| 96 | + | |
| 97 | +int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev, | |
| 98 | + struct ti_sci_resource *res) | |
| 99 | +{ | |
| 100 | + struct platform_device *pdev = to_platform_device(dev); | |
| 101 | + struct irq_domain *msi_domain; | |
| 102 | + int ret, nvec; | |
| 103 | + | |
| 104 | + msi_domain = dev_get_msi_domain(dev); | |
| 105 | + if (!msi_domain) | |
| 106 | + return -EINVAL; | |
| 107 | + | |
| 108 | + if (pdev->id < 0) | |
| 109 | + return -ENODEV; | |
| 110 | + | |
| 111 | + nvec = ti_sci_inta_msi_alloc_descs(dev, res); | |
| 112 | + if (nvec <= 0) | |
| 113 | + return nvec; | |
| 114 | + | |
| 115 | + ret = msi_domain_alloc_irqs(msi_domain, dev, nvec); | |
| 116 | + if (ret) { | |
| 117 | + dev_err(dev, "Failed to allocate IRQs %d\n", ret); | |
| 118 | + goto cleanup; | |
| 119 | + } | |
| 120 | + | |
| 121 | + return 0; | |
| 122 | + | |
| 123 | +cleanup: | |
| 124 | + ti_sci_inta_msi_free_descs(&pdev->dev); | |
| 125 | + return ret; | |
| 126 | +} | |
| 127 | +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_alloc_irqs); | |
| 128 | + | |
| 129 | +void ti_sci_inta_msi_domain_free_irqs(struct device *dev) | |
| 130 | +{ | |
| 131 | + msi_domain_free_irqs(dev->msi_domain, dev); | |
| 132 | + ti_sci_inta_msi_free_descs(dev); | |
| 133 | +} | |
| 134 | +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_free_irqs); | |
| 135 | + | |
| 136 | +unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 dev_index) | |
| 137 | +{ | |
| 138 | + struct msi_desc *desc; | |
| 139 | + | |
| 140 | + for_each_msi_entry(desc, dev) | |
| 141 | + if (desc->inta.dev_index == dev_index) | |
| 142 | + return desc->irq; | |
| 143 | + | |
| 144 | + return -ENODEV; | |
| 145 | +} | |
| 146 | +EXPORT_SYMBOL_GPL(ti_sci_inta_msi_get_virq); | |
| @@ -71,12 +71,25 @@ void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle, · 70 unmodified lines | ||
| 71 | 71 | size_t size, enum dma_data_direction dir, unsigned long attrs); |
| 72 | 72 | |
| 73 | 73 | /* The DMA API isn't _quite_ the whole story, though... */ |
| 74 | −void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg); | |
| 74 | +/* | |
| 75 | + * iommu_dma_prepare_msi() - Map the MSI page in the IOMMU device | |
| 76 | + * | |
| 77 | + * The MSI page will be stored in @desc. | |
| 78 | + * | |
| 79 | + * Return: 0 on success otherwise an error describing the failure. | |
| 80 | + */ | |
| 81 | +int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr); | |
| 82 | + | |
| 83 | +/* Update the MSI message if required. */ | |
| 84 | +void iommu_dma_compose_msi_msg(struct msi_desc *desc, | |
| 85 | + struct msi_msg *msg); | |
| 86 | + | |
| 75 | 87 | void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); |
| 76 | 88 | |
| 77 | 89 | #else |
| 78 | 90 | |
| 79 | 91 | struct iommu_domain; |
| 92 | +struct msi_desc; | |
| 80 | 93 | struct msi_msg; |
| 81 | 94 | struct device; |
| 82 | 95 | |
| @@ -99,7 +112,14 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain) · 16 unmodified lines | ||
| 99 | 112 | { |
| 100 | 113 | } |
| 101 | 114 | |
| 102 | −static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) | |
| 115 | +static inline int iommu_dma_prepare_msi(struct msi_desc *desc, | |
| 116 | + phys_addr_t msi_addr) | |
| 117 | +{ | |
| 118 | + return 0; | |
| 119 | +} | |
| 120 | + | |
| 121 | +static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc, | |
| 122 | + struct msi_msg *msg) | |
| 103 | 123 | { |
| 104 | 124 | } |
| 105 | 125 | |
| @@ -625,6 +625,8 @@ extern int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on); · 624 unmodified lines | ||
| 625 | 625 | extern int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, |
| 626 | 626 | void *vcpu_info); |
| 627 | 627 | extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type); |
| 628 | +extern int irq_chip_request_resources_parent(struct irq_data *data); | |
| 629 | +extern void irq_chip_release_resources_parent(struct irq_data *data); | |
| 628 | 630 | #endif |
| 629 | 631 | |
| 630 | 632 | /* Handling of unhandled and spurious interrupts: */ |
| @@ -165,7 +165,7 @@ · 164 unmodified lines | ||
| 165 | 165 | #define GICR_PROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nCnB) |
| 166 | 166 | #define GICR_PROPBASER_nC GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nC) |
| 167 | 167 | #define GICR_PROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt) |
| 168 | −#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt) | |
| 168 | +#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb) | |
| 169 | 169 | #define GICR_PROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWt) |
| 170 | 170 | #define GICR_PROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWb) |
| 171 | 171 | #define GICR_PROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWt) |
| @@ -192,7 +192,7 @@ · 20 unmodified lines | ||
| 192 | 192 | #define GICR_PENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nCnB) |
| 193 | 193 | #define GICR_PENDBASER_nC GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nC) |
| 194 | 194 | #define GICR_PENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt) |
| 195 | −#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt) | |
| 195 | +#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb) | |
| 196 | 196 | #define GICR_PENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWt) |
| 197 | 197 | #define GICR_PENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWb) |
| 198 | 198 | #define GICR_PENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWt) |
| @@ -251,7 +251,7 @@ · 52 unmodified lines | ||
| 251 | 251 | #define GICR_VPROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nCnB) |
| 252 | 252 | #define GICR_VPROPBASER_nC GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nC) |
| 253 | 253 | #define GICR_VPROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt) |
| 254 | −#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt) | |
| 254 | +#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWb) | |
| 255 | 255 | #define GICR_VPROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWt) |
| 256 | 256 | #define GICR_VPROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWb) |
| 257 | 257 | #define GICR_VPROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWt) |
| @@ -277,7 +277,7 @@ · 19 unmodified lines | ||
| 277 | 277 | #define GICR_VPENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nCnB) |
| 278 | 278 | #define GICR_VPENDBASER_nC GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nC) |
| 279 | 279 | #define GICR_VPENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt) |
| 280 | −#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt) | |
| 280 | +#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWb) | |
| 281 | 281 | #define GICR_VPENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWt) |
| 282 | 282 | #define GICR_VPENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWb) |
| 283 | 283 | #define GICR_VPENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWt) |
| @@ -351,7 +351,7 @@ · 67 unmodified lines | ||
| 351 | 351 | #define GITS_CBASER_nCnB GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nCnB) |
| 352 | 352 | #define GITS_CBASER_nC GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nC) |
| 353 | 353 | #define GITS_CBASER_RaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt) |
| 354 | −#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt) | |
| 354 | +#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWb) | |
| 355 | 355 | #define GITS_CBASER_WaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWt) |
| 356 | 356 | #define GITS_CBASER_WaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWb) |
| 357 | 357 | #define GITS_CBASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWt) |
| @@ -377,7 +377,7 @@ · 19 unmodified lines | ||
| 377 | 377 | #define GITS_BASER_nCnB GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nCnB) |
| 378 | 378 | #define GITS_BASER_nC GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nC) |
| 379 | 379 | #define GITS_BASER_RaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt) |
| 380 | −#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt) | |
| 380 | +#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb) | |
| 381 | 381 | #define GITS_BASER_WaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWt) |
| 382 | 382 | #define GITS_BASER_WaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWb) |
| 383 | 383 | #define GITS_BASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWt) |
| @@ -82,6 +82,7 @@ enum irq_domain_bus_token { · 81 unmodified lines | ||
| 82 | 82 | DOMAIN_BUS_NEXUS, |
| 83 | 83 | DOMAIN_BUS_IPI, |
| 84 | 84 | DOMAIN_BUS_FSL_MC_MSI, |
| 85 | + DOMAIN_BUS_TI_SCI_INTA_MSI, | |
| 85 | 86 | }; |
| 86 | 87 | |
| 87 | 88 | /** |
| @@ -47,6 +47,14 @@ struct fsl_mc_msi_desc { · 46 unmodified lines | ||
| 47 | 47 | u16 msi_index; |
| 48 | 48 | }; |
| 49 | 49 | |
| 50 | +/** | |
| 51 | + * ti_sci_inta_msi_desc - TISCI based INTA specific msi descriptor data | |
| 52 | + * @dev_index: TISCI device index | |
| 53 | + */ | |
| 54 | +struct ti_sci_inta_msi_desc { | |
| 55 | + u16 dev_index; | |
| 56 | +}; | |
| 57 | + | |
| 50 | 58 | /** |
| 51 | 59 | * struct msi_desc - Descriptor structure for MSI based interrupts |
| 52 | 60 | * @list: List head for management |
| @@ -68,6 +76,7 @@ struct fsl_mc_msi_desc { · 15 unmodified lines | ||
| 68 | 76 | * @mask_base: [PCI MSI-X] Mask register base address |
| 69 | 77 | * @platform: [platform] Platform device specific msi descriptor data |
| 70 | 78 | * @fsl_mc: [fsl-mc] FSL MC device specific msi descriptor data |
| 79 | + * @inta: [INTA] TISCI based INTA specific msi descriptor data | |
| 71 | 80 | */ |
| 72 | 81 | struct msi_desc { |
| 73 | 82 | /* Shared device/bus type independent data */ |
| @@ -77,6 +86,9 @@ struct msi_desc { · 3 unmodified lines | ||
| 77 | 86 | struct device *dev; |
| 78 | 87 | struct msi_msg msg; |
| 79 | 88 | struct irq_affinity_desc *affinity; |
| 89 | +#ifdef CONFIG_IRQ_MSI_IOMMU | |
| 90 | + const void *iommu_cookie; | |
| 91 | +#endif | |
| 80 | 92 | |
| 81 | 93 | union { |
| 82 | 94 | /* PCI MSI/X specific data */ |
| @@ -106,6 +118,7 @@ struct msi_desc { · 23 unmodified lines | ||
| 106 | 118 | */ |
| 107 | 119 | struct platform_msi_desc platform; |
| 108 | 120 | struct fsl_mc_msi_desc fsl_mc; |
| 121 | + struct ti_sci_inta_msi_desc inta; | |
| 109 | 122 | }; |
| 110 | 123 | }; |
| 111 | 124 | |
| @@ -119,6 +132,29 @@ struct msi_desc { · 7 unmodified lines | ||
| 119 | 132 | #define for_each_msi_entry_safe(desc, tmp, dev) \ |
| 120 | 133 | list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list) |
| 121 | 134 | |
| 135 | +#ifdef CONFIG_IRQ_MSI_IOMMU | |
| 136 | +static inline const void *msi_desc_get_iommu_cookie(struct msi_desc *desc) | |
| 137 | +{ | |
| 138 | + return desc->iommu_cookie; | |
| 139 | +} | |
| 140 | + | |
| 141 | +static inline void msi_desc_set_iommu_cookie(struct msi_desc *desc, | |
| 142 | + const void *iommu_cookie) | |
| 143 | +{ | |
| 144 | + desc->iommu_cookie = iommu_cookie; | |
| 145 | +} | |
| 146 | +#else | |
| 147 | +static inline const void *msi_desc_get_iommu_cookie(struct msi_desc *desc) | |
| 148 | +{ | |
| 149 | + return NULL; | |
| 150 | +} | |
| 151 | + | |
| 152 | +static inline void msi_desc_set_iommu_cookie(struct msi_desc *desc, | |
| 153 | + const void *iommu_cookie) | |
| 154 | +{ | |
| 155 | +} | |
| 156 | +#endif | |
| 157 | + | |
| 122 | 158 | #ifdef CONFIG_PCI_MSI |
| 123 | 159 | #define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) |
| 124 | 160 | #define for_each_pci_msi_entry(desc, pdev) \ |
| @@ -0,0 +1,23 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* | |
| 3 | + * Texas Instruments' K3 TI SCI INTA MSI helper | |
| 4 | + * | |
| 5 | + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | |
| 6 | + * Lokesh Vutla <lokeshvutla@ti.com> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#ifndef __INCLUDE_LINUX_TI_SCI_INTA_MSI_H | |
| 10 | +#define __INCLUDE_LINUX_TI_SCI_INTA_MSI_H | |
| 11 | + | |
| 12 | +#include <linux/msi.h> | |
| 13 | +#include <linux/soc/ti/ti_sci_protocol.h> | |
| 14 | + | |
| 15 | +struct irq_domain | |
| 16 | +*ti_sci_inta_msi_create_irq_domain(struct fwnode_handle *fwnode, | |
| 17 | + struct msi_domain_info *info, | |
| 18 | + struct irq_domain *parent); | |
| 19 | +int ti_sci_inta_msi_domain_alloc_irqs(struct device *dev, | |
| 20 | + struct ti_sci_resource *res); | |
| 21 | +unsigned int ti_sci_inta_msi_get_virq(struct device *dev, u32 index); | |
| 22 | +void ti_sci_inta_msi_domain_free_irqs(struct device *dev); | |
| 23 | +#endif /* __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H */ | |
| @@ -192,15 +192,68 @@ struct ti_sci_clk_ops { · 191 unmodified lines | ||
| 192 | 192 | u64 *current_freq); |
| 193 | 193 | }; |
| 194 | 194 | |
| 195 | +/** | |
| 196 | + * struct ti_sci_rm_core_ops - Resource management core operations | |
| 197 | + * @get_range: Get a range of resources belonging to ti sci host. | |
| 198 | + * @get_rage_from_shost: Get a range of resources belonging to | |
| 199 | + * specified host id. | |
| 200 | + * - s_host: Host processing entity to which the | |
| 201 | + * resources are allocated | |
| 202 | + * | |
| 203 | + * NOTE: for these functions, all the parameters are consolidated and defined | |
| 204 | + * as below: | |
| 205 | + * - handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle | |
| 206 | + * - dev_id: TISCI device ID. | |
| 207 | + * - subtype: Resource assignment subtype that is being requested | |
| 208 | + * from the given device. | |
| 209 | + * - range_start: Start index of the resource range | |
| 210 | + * - range_end: Number of resources in the range | |
| 211 | + */ | |
| 212 | +struct ti_sci_rm_core_ops { | |
| 213 | + int (*get_range)(const struct ti_sci_handle *handle, u32 dev_id, | |
| 214 | + u8 subtype, u16 *range_start, u16 *range_num); | |
| 215 | + int (*get_range_from_shost)(const struct ti_sci_handle *handle, | |
| 216 | + u32 dev_id, u8 subtype, u8 s_host, | |
| 217 | + u16 *range_start, u16 *range_num); | |
| 218 | +}; | |
| 219 | + | |
| 220 | +/** | |
| 221 | + * struct ti_sci_rm_irq_ops: IRQ management operations | |
| 222 | + * @set_irq: Set an IRQ route between the requested source | |
| 223 | + * and destination | |
| 224 | + * @set_event_map: Set an Event based peripheral irq to Interrupt | |
| 225 | + * Aggregator. | |
| 226 | + * @free_irq: Free an an IRQ route between the requested source | |
| 227 | + * destination. | |
| 228 | + * @free_event_map: Free an event based peripheral irq to Interrupt | |
| 229 | + * Aggregator. | |
| 230 | + */ | |
| 231 | +struct ti_sci_rm_irq_ops { | |
| 232 | + int (*set_irq)(const struct ti_sci_handle *handle, u16 src_id, | |
| 233 | + u16 src_index, u16 dst_id, u16 dst_host_irq); | |
| 234 | + int (*set_event_map)(const struct ti_sci_handle *handle, u16 src_id, | |
| 235 | + u16 src_index, u16 ia_id, u16 vint, | |
| 236 | + u16 global_event, u8 vint_status_bit); | |
| 237 | + int (*free_irq)(const struct ti_sci_handle *handle, u16 src_id, | |
| 238 | + u16 src_index, u16 dst_id, u16 dst_host_irq); | |
| 239 | + int (*free_event_map)(const struct ti_sci_handle *handle, u16 src_id, | |
| 240 | + u16 src_index, u16 ia_id, u16 vint, | |
| 241 | + u16 global_event, u8 vint_status_bit); | |
| 242 | +}; | |
| 243 | + | |
| 195 | 244 | /** |
| 196 | 245 | * struct ti_sci_ops - Function support for TI SCI |
| 197 | 246 | * @dev_ops: Device specific operations |
| 198 | 247 | * @clk_ops: Clock specific operations |
| 248 | + * @rm_core_ops: Resource management core operations. | |
| 249 | + * @rm_irq_ops: IRQ management specific operations | |
| 199 | 250 | */ |
| 200 | 251 | struct ti_sci_ops { |
| 201 | 252 | struct ti_sci_core_ops core_ops; |
| 202 | 253 | struct ti_sci_dev_ops dev_ops; |
| 203 | 254 | struct ti_sci_clk_ops clk_ops; |
| 255 | + struct ti_sci_rm_core_ops rm_core_ops; | |
| 256 | + struct ti_sci_rm_irq_ops rm_irq_ops; | |
| 204 | 257 | }; |
| 205 | 258 | |
| 206 | 259 | /** |
| @@ -213,10 +266,47 @@ struct ti_sci_handle { · 6 unmodified lines | ||
| 213 | 266 | struct ti_sci_ops ops; |
| 214 | 267 | }; |
| 215 | 268 | |
| 269 | +#define TI_SCI_RESOURCE_NULL 0xffff | |
| 270 | + | |
| 271 | +/** | |
| 272 | + * struct ti_sci_resource_desc - Description of TI SCI resource instance range. | |
| 273 | + * @start: Start index of the resource. | |
| 274 | + * @num: Number of resources. | |
| 275 | + * @res_map: Bitmap to manage the allocation of these resources. | |
| 276 | + */ | |
| 277 | +struct ti_sci_resource_desc { | |
| 278 | + u16 start; | |
| 279 | + u16 num; | |
| 280 | + unsigned long *res_map; | |
| 281 | +}; | |
| 282 | + | |
| 283 | +/** | |
| 284 | + * struct ti_sci_resource - Structure representing a resource assigned | |
| 285 | + * to a device. | |
| 286 | + * @sets: Number of sets available from this resource type | |
| 287 | + * @lock: Lock to guard the res map in each set. | |
| 288 | + * @desc: Array of resource descriptors. | |
| 289 | + */ | |
| 290 | +struct ti_sci_resource { | |
| 291 | + u16 sets; | |
| 292 | + raw_spinlock_t lock; | |
| 293 | + struct ti_sci_resource_desc *desc; | |
| 294 | +}; | |
| 295 | + | |
| 216 | 296 | #if IS_ENABLED(CONFIG_TI_SCI_PROTOCOL) |
| 217 | 297 | const struct ti_sci_handle *ti_sci_get_handle(struct device *dev); |
| 218 | 298 | int ti_sci_put_handle(const struct ti_sci_handle *handle); |
| 219 | 299 | const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev); |
| 300 | +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, | |
| 301 | + const char *property); | |
| 302 | +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, | |
| 303 | + const char *property); | |
| 304 | +u16 ti_sci_get_free_resource(struct ti_sci_resource *res); | |
| 305 | +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id); | |
| 306 | +u32 ti_sci_get_num_resources(struct ti_sci_resource *res); | |
| 307 | +struct ti_sci_resource * | |
| 308 | +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, | |
| 309 | + struct device *dev, u32 dev_id, char *of_prop); | |
| 220 | 310 | |
| 221 | 311 | #else /* CONFIG_TI_SCI_PROTOCOL */ |
| 222 | 312 | |
| @@ -236,6 +326,40 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev) · 13 unmodified lines | ||
| 236 | 326 | return ERR_PTR(-EINVAL); |
| 237 | 327 | } |
| 238 | 328 | |
| 329 | +static inline | |
| 330 | +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, | |
| 331 | + const char *property) | |
| 332 | +{ | |
| 333 | + return ERR_PTR(-EINVAL); | |
| 334 | +} | |
| 335 | + | |
| 336 | +static inline | |
| 337 | +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, | |
| 338 | + const char *property) | |
| 339 | +{ | |
| 340 | + return ERR_PTR(-EINVAL); | |
| 341 | +} | |
| 342 | + | |
| 343 | +static inline u16 ti_sci_get_free_resource(struct ti_sci_resource *res) | |
| 344 | +{ | |
| 345 | + return TI_SCI_RESOURCE_NULL; | |
| 346 | +} | |
| 347 | + | |
| 348 | +static inline void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) | |
| 349 | +{ | |
| 350 | +} | |
| 351 | + | |
| 352 | +static inline u32 ti_sci_get_num_resources(struct ti_sci_resource *res) | |
| 353 | +{ | |
| 354 | + return 0; | |
| 355 | +} | |
| 356 | + | |
| 357 | +static inline struct ti_sci_resource * | |
| 358 | +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, | |
| 359 | + struct device *dev, u32 dev_id, char *of_prop) | |
| 360 | +{ | |
| 361 | + return ERR_PTR(-EINVAL); | |
| 362 | +} | |
| 239 | 363 | #endif /* CONFIG_TI_SCI_PROTOCOL */ |
| 240 | 364 | |
| 241 | 365 | #endif /* __TISCI_PROTOCOL_H */ |
| @@ -91,6 +91,9 @@ config GENERIC_MSI_IRQ_DOMAIN · 90 unmodified lines | ||
| 91 | 91 | select IRQ_DOMAIN_HIERARCHY |
| 92 | 92 | select GENERIC_MSI_IRQ |
| 93 | 93 | |
| 94 | +config IRQ_MSI_IOMMU | |
| 95 | + bool | |
| 96 | + | |
| 94 | 97 | config HANDLE_DOMAIN_IRQ |
| 95 | 98 | bool |
| 96 | 99 | |
| @@ -1459,6 +1459,33 @@ int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) · 1458 unmodified lines | ||
| 1459 | 1459 | return -ENOSYS; |
| 1460 | 1460 | } |
| 1461 | 1461 | EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent); |
| 1462 | + | |
| 1463 | +/** | |
| 1464 | + * irq_chip_request_resources_parent - Request resources on the parent interrupt | |
| 1465 | + * @data: Pointer to interrupt specific data | |
| 1466 | + */ | |
| 1467 | +int irq_chip_request_resources_parent(struct irq_data *data) | |
| 1468 | +{ | |
| 1469 | + data = data->parent_data; | |
| 1470 | + | |
| 1471 | + if (data->chip->irq_request_resources) | |
| 1472 | + return data->chip->irq_request_resources(data); | |
| 1473 | + | |
| 1474 | + return -ENOSYS; | |
| 1475 | +} | |
| 1476 | +EXPORT_SYMBOL_GPL(irq_chip_request_resources_parent); | |
| 1477 | + | |
| 1478 | +/** | |
| 1479 | + * irq_chip_release_resources_parent - Release resources on the parent interrupt | |
| 1480 | + * @data: Pointer to interrupt specific data | |
| 1481 | + */ | |
| 1482 | +void irq_chip_release_resources_parent(struct irq_data *data) | |
| 1483 | +{ | |
| 1484 | + data = data->parent_data; | |
| 1485 | + if (data->chip->irq_release_resources) | |
| 1486 | + data->chip->irq_release_resources(data); | |
| 1487 | +} | |
| 1488 | +EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent); | |
| 1462 | 1489 | #endif |
| 1463 | 1490 | |
| 1464 | 1491 | /** |
| @@ -1297,7 +1297,7 @@ int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain, · 1296 unmodified lines | ||
| 1297 | 1297 | /** |
| 1298 | 1298 | * __irq_domain_alloc_irqs - Allocate IRQs from domain |
| 1299 | 1299 | * @domain: domain to allocate from |
| 1300 | − * @irq_base: allocate specified IRQ nubmer if irq_base >= 0 | |
| 1300 | + * @irq_base: allocate specified IRQ number if irq_base >= 0 | |
| 1301 | 1301 | * @nr_irqs: number of IRQs to allocate |
| 1302 | 1302 | * @node: NUMA node id for memory allocation |
| 1303 | 1303 | * @arg: domain specific argument |