1 // SPDX-License-Identifier: GPL-2.0
3 * AMD CDX bus driver MSI support
5 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
9 #include <linux/of_device.h>
10 #include <linux/of_address.h>
11 #include <linux/of_irq.h>
12 #include <linux/irq.h>
13 #include <linux/irqdomain.h>
14 #include <linux/msi.h>
15 #include <linux/cdx/cdx_bus.h>
19 static void cdx_msi_write_msg(struct irq_data
*irq_data
, struct msi_msg
*msg
)
21 struct msi_desc
*msi_desc
= irq_data_get_msi_desc(irq_data
);
22 struct cdx_device
*cdx_dev
= to_cdx_device(msi_desc
->dev
);
24 /* We would not operate on msg here rather we wait for irq_bus_sync_unlock()
25 * to be called from preemptible task context.
28 cdx_dev
->msi_write_pending
= true;
31 static void cdx_msi_write_irq_lock(struct irq_data
*irq_data
)
33 struct msi_desc
*msi_desc
= irq_data_get_msi_desc(irq_data
);
34 struct cdx_device
*cdx_dev
= to_cdx_device(msi_desc
->dev
);
36 mutex_lock(&cdx_dev
->irqchip_lock
);
39 static void cdx_msi_write_irq_unlock(struct irq_data
*irq_data
)
41 struct msi_desc
*msi_desc
= irq_data_get_msi_desc(irq_data
);
42 struct cdx_device
*cdx_dev
= to_cdx_device(msi_desc
->dev
);
43 struct cdx_controller
*cdx
= cdx_dev
->cdx
;
44 struct cdx_device_config dev_config
;
46 if (!cdx_dev
->msi_write_pending
) {
47 mutex_unlock(&cdx_dev
->irqchip_lock
);
51 cdx_dev
->msi_write_pending
= false;
52 mutex_unlock(&cdx_dev
->irqchip_lock
);
54 dev_config
.msi
.msi_index
= msi_desc
->msi_index
;
55 dev_config
.msi
.data
= msi_desc
->msg
.data
;
56 dev_config
.msi
.addr
= ((u64
)(msi_desc
->msg
.address_hi
) << 32) | msi_desc
->msg
.address_lo
;
59 * dev_configure() is a controller callback which can interact with
60 * Firmware or other entities, and can sleep, so invoke this function
61 * outside of the mutex held region.
63 dev_config
.type
= CDX_DEV_MSI_CONF
;
64 if (cdx
->ops
->dev_configure
)
65 cdx
->ops
->dev_configure(cdx
, cdx_dev
->bus_num
, cdx_dev
->dev_num
, &dev_config
);
68 int cdx_enable_msi(struct cdx_device
*cdx_dev
)
70 struct cdx_controller
*cdx
= cdx_dev
->cdx
;
71 struct cdx_device_config dev_config
;
73 dev_config
.type
= CDX_DEV_MSI_ENABLE
;
74 dev_config
.msi_enable
= true;
75 if (cdx
->ops
->dev_configure
) {
76 return cdx
->ops
->dev_configure(cdx
, cdx_dev
->bus_num
, cdx_dev
->dev_num
,
82 EXPORT_SYMBOL_GPL(cdx_enable_msi
);
84 void cdx_disable_msi(struct cdx_device
*cdx_dev
)
86 struct cdx_controller
*cdx
= cdx_dev
->cdx
;
87 struct cdx_device_config dev_config
;
89 dev_config
.type
= CDX_DEV_MSI_ENABLE
;
90 dev_config
.msi_enable
= false;
91 if (cdx
->ops
->dev_configure
)
92 cdx
->ops
->dev_configure(cdx
, cdx_dev
->bus_num
, cdx_dev
->dev_num
, &dev_config
);
94 EXPORT_SYMBOL_GPL(cdx_disable_msi
);
96 static struct irq_chip cdx_msi_irq_chip
= {
98 .irq_mask
= irq_chip_mask_parent
,
99 .irq_unmask
= irq_chip_unmask_parent
,
100 .irq_eoi
= irq_chip_eoi_parent
,
101 .irq_set_affinity
= msi_domain_set_affinity
,
102 .irq_write_msi_msg
= cdx_msi_write_msg
,
103 .irq_bus_lock
= cdx_msi_write_irq_lock
,
104 .irq_bus_sync_unlock
= cdx_msi_write_irq_unlock
107 /* Convert an msi_desc to a unique identifier within the domain. */
108 static irq_hw_number_t
cdx_domain_calc_hwirq(struct cdx_device
*dev
,
109 struct msi_desc
*desc
)
111 return ((irq_hw_number_t
)dev
->msi_dev_id
<< 10) | desc
->msi_index
;
114 static void cdx_msi_set_desc(msi_alloc_info_t
*arg
, struct msi_desc
*desc
)
117 arg
->hwirq
= cdx_domain_calc_hwirq(to_cdx_device(desc
->dev
), desc
);
120 static int cdx_msi_prepare(struct irq_domain
*msi_domain
,
122 int nvec
, msi_alloc_info_t
*info
)
124 struct cdx_device
*cdx_dev
= to_cdx_device(dev
);
125 struct device
*parent
= cdx_dev
->cdx
->dev
;
126 struct msi_domain_info
*msi_info
;
130 /* Retrieve device ID from requestor ID using parent device */
131 ret
= of_map_id(parent
->of_node
, cdx_dev
->msi_dev_id
, "msi-map", "msi-map-mask",
134 dev_err(dev
, "of_map_id failed for MSI: %d\n", ret
);
138 #ifdef GENERIC_MSI_DOMAIN_OPS
139 /* Set the device Id to be passed to the GIC-ITS */
140 info
->scratchpad
[0].ul
= dev_id
;
143 msi_info
= msi_get_domain_info(msi_domain
->parent
);
145 return msi_info
->ops
->msi_prepare(msi_domain
->parent
, dev
, nvec
, info
);
148 static struct msi_domain_ops cdx_msi_ops
= {
149 .msi_prepare
= cdx_msi_prepare
,
150 .set_desc
= cdx_msi_set_desc
153 static struct msi_domain_info cdx_msi_domain_info
= {
155 .chip
= &cdx_msi_irq_chip
,
156 .flags
= MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS
|
157 MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS
| MSI_FLAG_FREE_MSI_DESCS
160 struct irq_domain
*cdx_msi_domain_init(struct device
*dev
)
162 struct device_node
*np
= dev
->of_node
;
163 struct fwnode_handle
*fwnode_handle
;
164 struct irq_domain
*cdx_msi_domain
;
165 struct device_node
*parent_node
;
166 struct irq_domain
*parent
;
168 fwnode_handle
= of_node_to_fwnode(np
);
170 parent_node
= of_parse_phandle(np
, "msi-map", 1);
172 dev_err(dev
, "msi-map not present on cdx controller\n");
176 parent
= irq_find_matching_fwnode(of_node_to_fwnode(parent_node
), DOMAIN_BUS_NEXUS
);
177 if (!parent
|| !msi_get_domain_info(parent
)) {
178 dev_err(dev
, "unable to locate ITS domain\n");
182 cdx_msi_domain
= msi_create_irq_domain(fwnode_handle
, &cdx_msi_domain_info
, parent
);
183 if (!cdx_msi_domain
) {
184 dev_err(dev
, "unable to create CDX-MSI domain\n");
188 dev_dbg(dev
, "CDX-MSI domain created\n");
190 return cdx_msi_domain
;
192 EXPORT_SYMBOL_NS_GPL(cdx_msi_domain_init
, "CDX_BUS_CONTROLLER");