1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * MHI PCI driver - MHI over PCI controller driver
5 * This module is a generic driver for registering MHI-over-PCI devices,
6 * such as PCIe QCOM modems.
8 * Copyright (C) 2020 Linaro Ltd <loic.poulain@linaro.org>
11 #include <linux/device.h>
12 #include <linux/mhi.h>
13 #include <linux/module.h>
14 #include <linux/pci.h>
16 #define MHI_PCI_DEFAULT_BAR_NUM 0
19 * struct mhi_pci_dev_info - MHI PCI device specific information
20 * @config: MHI controller configuration
21 * @name: name of the PCI module
22 * @fw: firmware path (if any)
23 * @edl: emergency download mode firmware path (if any)
24 * @bar_num: PCI base address register to use for MHI MMIO register space
25 * @dma_data_width: DMA transfer word size (32 or 64 bits)
27 struct mhi_pci_dev_info
{
28 const struct mhi_controller_config
*config
;
33 unsigned int dma_data_width
;
36 #define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \
40 .num_elements = el_count, \
41 .event_ring = ev_ring, \
42 .dir = DMA_TO_DEVICE, \
43 .ee_mask = BIT(MHI_EE_AMSS), \
45 .doorbell = MHI_DB_BRST_DISABLE, \
46 .lpm_notify = false, \
47 .offload_channel = false, \
48 .doorbell_mode_switch = false, \
51 #define MHI_CHANNEL_CONFIG_DL(ch_num, ch_name, el_count, ev_ring) \
55 .num_elements = el_count, \
56 .event_ring = ev_ring, \
57 .dir = DMA_FROM_DEVICE, \
58 .ee_mask = BIT(MHI_EE_AMSS), \
60 .doorbell = MHI_DB_BRST_DISABLE, \
61 .lpm_notify = false, \
62 .offload_channel = false, \
63 .doorbell_mode_switch = false, \
66 #define MHI_EVENT_CONFIG_CTRL(ev_ring) \
69 .irq_moderation_ms = 0, \
70 .irq = (ev_ring) + 1, \
72 .mode = MHI_DB_BRST_DISABLE, \
73 .data_type = MHI_ER_CTRL, \
74 .hardware_event = false, \
75 .client_managed = false, \
76 .offload_channel = false, \
79 #define MHI_EVENT_CONFIG_DATA(ev_ring) \
81 .num_elements = 128, \
82 .irq_moderation_ms = 5, \
83 .irq = (ev_ring) + 1, \
85 .mode = MHI_DB_BRST_DISABLE, \
86 .data_type = MHI_ER_DATA, \
87 .hardware_event = false, \
88 .client_managed = false, \
89 .offload_channel = false, \
92 #define MHI_EVENT_CONFIG_HW_DATA(ev_ring, ch_num) \
94 .num_elements = 128, \
95 .irq_moderation_ms = 5, \
96 .irq = (ev_ring) + 1, \
98 .mode = MHI_DB_BRST_DISABLE, \
99 .data_type = MHI_ER_DATA, \
100 .hardware_event = true, \
101 .client_managed = false, \
102 .offload_channel = false, \
106 static const struct mhi_channel_config modem_qcom_v1_mhi_channels
[] = {
107 MHI_CHANNEL_CONFIG_UL(12, "MBIM", 4, 0),
108 MHI_CHANNEL_CONFIG_DL(13, "MBIM", 4, 0),
109 MHI_CHANNEL_CONFIG_UL(14, "QMI", 4, 0),
110 MHI_CHANNEL_CONFIG_DL(15, "QMI", 4, 0),
111 MHI_CHANNEL_CONFIG_UL(20, "IPCR", 8, 0),
112 MHI_CHANNEL_CONFIG_DL(21, "IPCR", 8, 0),
113 MHI_CHANNEL_CONFIG_UL(100, "IP_HW0", 128, 1),
114 MHI_CHANNEL_CONFIG_DL(101, "IP_HW0", 128, 2),
117 static const struct mhi_event_config modem_qcom_v1_mhi_events
[] = {
118 /* first ring is control+data ring */
119 MHI_EVENT_CONFIG_CTRL(0),
120 /* Hardware channels request dedicated hardware event rings */
121 MHI_EVENT_CONFIG_HW_DATA(1, 100),
122 MHI_EVENT_CONFIG_HW_DATA(2, 101)
125 static const struct mhi_controller_config modem_qcom_v1_mhiv_config
= {
128 .num_channels
= ARRAY_SIZE(modem_qcom_v1_mhi_channels
),
129 .ch_cfg
= modem_qcom_v1_mhi_channels
,
130 .num_events
= ARRAY_SIZE(modem_qcom_v1_mhi_events
),
131 .event_cfg
= modem_qcom_v1_mhi_events
,
134 static const struct mhi_pci_dev_info mhi_qcom_sdx55_info
= {
135 .name
= "qcom-sdx55m",
136 .fw
= "qcom/sdx55m/sbl1.mbn",
137 .edl
= "qcom/sdx55m/edl.mbn",
138 .config
= &modem_qcom_v1_mhiv_config
,
139 .bar_num
= MHI_PCI_DEFAULT_BAR_NUM
,
143 static const struct pci_device_id mhi_pci_id_table
[] = {
144 { PCI_DEVICE(PCI_VENDOR_ID_QCOM
, 0x0306),
145 .driver_data
= (kernel_ulong_t
) &mhi_qcom_sdx55_info
},
148 MODULE_DEVICE_TABLE(pci
, mhi_pci_id_table
);
150 static int mhi_pci_read_reg(struct mhi_controller
*mhi_cntrl
,
151 void __iomem
*addr
, u32
*out
)
157 static void mhi_pci_write_reg(struct mhi_controller
*mhi_cntrl
,
158 void __iomem
*addr
, u32 val
)
163 static void mhi_pci_status_cb(struct mhi_controller
*mhi_cntrl
,
164 enum mhi_callback cb
)
166 /* Nothing to do for now */
169 static int mhi_pci_claim(struct mhi_controller
*mhi_cntrl
,
170 unsigned int bar_num
, u64 dma_mask
)
172 struct pci_dev
*pdev
= to_pci_dev(mhi_cntrl
->cntrl_dev
);
175 err
= pci_assign_resource(pdev
, bar_num
);
179 err
= pcim_enable_device(pdev
);
181 dev_err(&pdev
->dev
, "failed to enable pci device: %d\n", err
);
185 err
= pcim_iomap_regions(pdev
, 1 << bar_num
, pci_name(pdev
));
187 dev_err(&pdev
->dev
, "failed to map pci region: %d\n", err
);
190 mhi_cntrl
->regs
= pcim_iomap_table(pdev
)[bar_num
];
192 err
= pci_set_dma_mask(pdev
, dma_mask
);
194 dev_err(&pdev
->dev
, "Cannot set proper DMA mask\n");
198 err
= pci_set_consistent_dma_mask(pdev
, dma_mask
);
200 dev_err(&pdev
->dev
, "set consistent dma mask failed\n");
204 pci_set_master(pdev
);
209 static int mhi_pci_get_irqs(struct mhi_controller
*mhi_cntrl
,
210 const struct mhi_controller_config
*mhi_cntrl_config
)
212 struct pci_dev
*pdev
= to_pci_dev(mhi_cntrl
->cntrl_dev
);
217 * Alloc one MSI vector for BHI + one vector per event ring, ideally...
218 * No explicit pci_free_irq_vectors required, done by pcim_release.
220 mhi_cntrl
->nr_irqs
= 1 + mhi_cntrl_config
->num_events
;
222 nr_vectors
= pci_alloc_irq_vectors(pdev
, 1, mhi_cntrl
->nr_irqs
, PCI_IRQ_MSI
);
223 if (nr_vectors
< 0) {
224 dev_err(&pdev
->dev
, "Error allocating MSI vectors %d\n",
229 if (nr_vectors
< mhi_cntrl
->nr_irqs
) {
230 dev_warn(&pdev
->dev
, "Not enough MSI vectors (%d/%d), use shared MSI\n",
231 nr_vectors
, mhi_cntrl_config
->num_events
);
234 irq
= devm_kcalloc(&pdev
->dev
, mhi_cntrl
->nr_irqs
, sizeof(int), GFP_KERNEL
);
238 for (i
= 0; i
< mhi_cntrl
->nr_irqs
; i
++) {
239 int vector
= i
>= nr_vectors
? (nr_vectors
- 1) : i
;
241 irq
[i
] = pci_irq_vector(pdev
, vector
);
244 mhi_cntrl
->irq
= irq
;
249 static int mhi_pci_runtime_get(struct mhi_controller
*mhi_cntrl
)
255 static void mhi_pci_runtime_put(struct mhi_controller
*mhi_cntrl
)
260 static int mhi_pci_probe(struct pci_dev
*pdev
, const struct pci_device_id
*id
)
262 const struct mhi_pci_dev_info
*info
= (struct mhi_pci_dev_info
*) id
->driver_data
;
263 const struct mhi_controller_config
*mhi_cntrl_config
;
264 struct mhi_controller
*mhi_cntrl
;
267 dev_dbg(&pdev
->dev
, "MHI PCI device found: %s\n", info
->name
);
269 mhi_cntrl
= mhi_alloc_controller();
273 mhi_cntrl_config
= info
->config
;
274 mhi_cntrl
->cntrl_dev
= &pdev
->dev
;
275 mhi_cntrl
->iova_start
= 0;
276 mhi_cntrl
->iova_stop
= (dma_addr_t
)DMA_BIT_MASK(info
->dma_data_width
);
277 mhi_cntrl
->fw_image
= info
->fw
;
278 mhi_cntrl
->edl_image
= info
->edl
;
280 mhi_cntrl
->read_reg
= mhi_pci_read_reg
;
281 mhi_cntrl
->write_reg
= mhi_pci_write_reg
;
282 mhi_cntrl
->status_cb
= mhi_pci_status_cb
;
283 mhi_cntrl
->runtime_get
= mhi_pci_runtime_get
;
284 mhi_cntrl
->runtime_put
= mhi_pci_runtime_put
;
286 err
= mhi_pci_claim(mhi_cntrl
, info
->bar_num
, DMA_BIT_MASK(info
->dma_data_width
));
290 err
= mhi_pci_get_irqs(mhi_cntrl
, mhi_cntrl_config
);
294 pci_set_drvdata(pdev
, mhi_cntrl
);
296 err
= mhi_register_controller(mhi_cntrl
, mhi_cntrl_config
);
300 /* MHI bus does not power up the controller by default */
301 err
= mhi_prepare_for_power_up(mhi_cntrl
);
303 dev_err(&pdev
->dev
, "failed to prepare MHI controller\n");
307 err
= mhi_sync_power_up(mhi_cntrl
);
309 dev_err(&pdev
->dev
, "failed to power up MHI controller\n");
316 mhi_unprepare_after_power_down(mhi_cntrl
);
318 mhi_unregister_controller(mhi_cntrl
);
320 mhi_free_controller(mhi_cntrl
);
325 static void mhi_pci_remove(struct pci_dev
*pdev
)
327 struct mhi_controller
*mhi_cntrl
= pci_get_drvdata(pdev
);
329 mhi_power_down(mhi_cntrl
, true);
330 mhi_unprepare_after_power_down(mhi_cntrl
);
331 mhi_unregister_controller(mhi_cntrl
);
332 mhi_free_controller(mhi_cntrl
);
335 static struct pci_driver mhi_pci_driver
= {
336 .name
= "mhi-pci-generic",
337 .id_table
= mhi_pci_id_table
,
338 .probe
= mhi_pci_probe
,
339 .remove
= mhi_pci_remove
341 module_pci_driver(mhi_pci_driver
);
343 MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
344 MODULE_DESCRIPTION("Modem Host Interface (MHI) PCI controller driver");
345 MODULE_LICENSE("GPL");