1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * MHI PCI driver - MHI over PCI controller driver
4 *
5 * This module is a generic driver for registering MHI-over-PCI devices,
6 * such as PCIe QCOM modems.
7 *
8 * Copyright (C) 2020 Linaro Ltd <loic.poulain@linaro.org>
9 */
10
11 #include <linux/device.h>
12 #include <linux/mhi.h>
13 #include <linux/module.h>
14 #include <linux/pci.h>
15
16 #define MHI_PCI_DEFAULT_BAR_NUM 0
17
18 /**
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)
26 */
27 struct mhi_pci_dev_info {
28 const struct mhi_controller_config *config;
29 const char *name;
30 const char *fw;
31 const char *edl;
32 unsigned int bar_num;
33 unsigned int dma_data_width;
34 };
35
36 #define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \
37 { \
38 .num = ch_num, \
39 .name = ch_name, \
40 .num_elements = el_count, \
41 .event_ring = ev_ring, \
42 .dir = DMA_TO_DEVICE, \
43 .ee_mask = BIT(MHI_EE_AMSS), \
44 .pollcfg = 0, \
45 .doorbell = MHI_DB_BRST_DISABLE, \
46 .lpm_notify = false, \
47 .offload_channel = false, \
48 .doorbell_mode_switch = false, \
49 } \
50
51 #define MHI_CHANNEL_CONFIG_DL(ch_num, ch_name, el_count, ev_ring) \
52 { \
53 .num = ch_num, \
54 .name = ch_name, \
55 .num_elements = el_count, \
56 .event_ring = ev_ring, \
57 .dir = DMA_FROM_DEVICE, \
58 .ee_mask = BIT(MHI_EE_AMSS), \
59 .pollcfg = 0, \
60 .doorbell = MHI_DB_BRST_DISABLE, \
61 .lpm_notify = false, \
62 .offload_channel = false, \
63 .doorbell_mode_switch = false, \
64 }
65
66 #define MHI_EVENT_CONFIG_CTRL(ev_ring) \
67 { \
68 .num_elements = 64, \
69 .irq_moderation_ms = 0, \
70 .irq = (ev_ring) + 1, \
71 .priority = 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, \
77 }
78
79 #define MHI_EVENT_CONFIG_DATA(ev_ring) \
80 { \
81 .num_elements = 128, \
82 .irq_moderation_ms = 5, \
83 .irq = (ev_ring) + 1, \
84 .priority = 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, \
90 }
91
92 #define MHI_EVENT_CONFIG_HW_DATA(ev_ring, ch_num) \
93 { \
94 .num_elements = 128, \
95 .irq_moderation_ms = 5, \
96 .irq = (ev_ring) + 1, \
97 .priority = 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, \
103 .channel = ch_num, \
104 }
105
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),
115 };
116
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)
123 };
124
125 static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
126 .max_channels = 128,
127 .timeout_ms = 5000,
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,
132 };
133
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,
140 .dma_data_width = 32
141 };
142
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 },
146 { }
147 };
148 MODULE_DEVICE_TABLE(pci, mhi_pci_id_table);
149
mhi_pci_read_reg(struct mhi_controller * mhi_cntrl,void __iomem * addr,u32 * out)150 static int mhi_pci_read_reg(struct mhi_controller *mhi_cntrl,
151 void __iomem *addr, u32 *out)
152 {
153 *out = readl(addr);
154 return 0;
155 }
156
mhi_pci_write_reg(struct mhi_controller * mhi_cntrl,void __iomem * addr,u32 val)157 static void mhi_pci_write_reg(struct mhi_controller *mhi_cntrl,
158 void __iomem *addr, u32 val)
159 {
160 writel(val, addr);
161 }
162
mhi_pci_status_cb(struct mhi_controller * mhi_cntrl,enum mhi_callback cb)163 static void mhi_pci_status_cb(struct mhi_controller *mhi_cntrl,
164 enum mhi_callback cb)
165 {
166 /* Nothing to do for now */
167 }
168
mhi_pci_claim(struct mhi_controller * mhi_cntrl,unsigned int bar_num,u64 dma_mask)169 static int mhi_pci_claim(struct mhi_controller *mhi_cntrl,
170 unsigned int bar_num, u64 dma_mask)
171 {
172 struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
173 int err;
174
175 err = pci_assign_resource(pdev, bar_num);
176 if (err)
177 return err;
178
179 err = pcim_enable_device(pdev);
180 if (err) {
181 dev_err(&pdev->dev, "failed to enable pci device: %d\n", err);
182 return err;
183 }
184
185 err = pcim_iomap_regions(pdev, 1 << bar_num, pci_name(pdev));
186 if (err) {
187 dev_err(&pdev->dev, "failed to map pci region: %d\n", err);
188 return err;
189 }
190 mhi_cntrl->regs = pcim_iomap_table(pdev)[bar_num];
191
192 err = pci_set_dma_mask(pdev, dma_mask);
193 if (err) {
194 dev_err(&pdev->dev, "Cannot set proper DMA mask\n");
195 return err;
196 }
197
198 err = pci_set_consistent_dma_mask(pdev, dma_mask);
199 if (err) {
200 dev_err(&pdev->dev, "set consistent dma mask failed\n");
201 return err;
202 }
203
204 pci_set_master(pdev);
205
206 return 0;
207 }
208
mhi_pci_get_irqs(struct mhi_controller * mhi_cntrl,const struct mhi_controller_config * mhi_cntrl_config)209 static int mhi_pci_get_irqs(struct mhi_controller *mhi_cntrl,
210 const struct mhi_controller_config *mhi_cntrl_config)
211 {
212 struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
213 int nr_vectors, i;
214 int *irq;
215
216 /*
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.
219 */
220 mhi_cntrl->nr_irqs = 1 + mhi_cntrl_config->num_events;
221
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",
225 nr_vectors);
226 return nr_vectors;
227 }
228
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);
232 }
233
234 irq = devm_kcalloc(&pdev->dev, mhi_cntrl->nr_irqs, sizeof(int), GFP_KERNEL);
235 if (!irq)
236 return -ENOMEM;
237
238 for (i = 0; i < mhi_cntrl->nr_irqs; i++) {
239 int vector = i >= nr_vectors ? (nr_vectors - 1) : i;
240
241 irq[i] = pci_irq_vector(pdev, vector);
242 }
243
244 mhi_cntrl->irq = irq;
245
246 return 0;
247 }
248
mhi_pci_runtime_get(struct mhi_controller * mhi_cntrl)249 static int mhi_pci_runtime_get(struct mhi_controller *mhi_cntrl)
250 {
251 /* no PM for now */
252 return 0;
253 }
254
mhi_pci_runtime_put(struct mhi_controller * mhi_cntrl)255 static void mhi_pci_runtime_put(struct mhi_controller *mhi_cntrl)
256 {
257 /* no PM for now */
258 }
259
mhi_pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)260 static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
261 {
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;
265 int err;
266
267 dev_dbg(&pdev->dev, "MHI PCI device found: %s\n", info->name);
268
269 mhi_cntrl = mhi_alloc_controller();
270 if (!mhi_cntrl)
271 return -ENOMEM;
272
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;
279
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;
285
286 err = mhi_pci_claim(mhi_cntrl, info->bar_num, DMA_BIT_MASK(info->dma_data_width));
287 if (err)
288 goto err_release;
289
290 err = mhi_pci_get_irqs(mhi_cntrl, mhi_cntrl_config);
291 if (err)
292 goto err_release;
293
294 pci_set_drvdata(pdev, mhi_cntrl);
295
296 err = mhi_register_controller(mhi_cntrl, mhi_cntrl_config);
297 if (err)
298 goto err_release;
299
300 /* MHI bus does not power up the controller by default */
301 err = mhi_prepare_for_power_up(mhi_cntrl);
302 if (err) {
303 dev_err(&pdev->dev, "failed to prepare MHI controller\n");
304 goto err_unregister;
305 }
306
307 err = mhi_sync_power_up(mhi_cntrl);
308 if (err) {
309 dev_err(&pdev->dev, "failed to power up MHI controller\n");
310 goto err_unprepare;
311 }
312
313 return 0;
314
315 err_unprepare:
316 mhi_unprepare_after_power_down(mhi_cntrl);
317 err_unregister:
318 mhi_unregister_controller(mhi_cntrl);
319 err_release:
320 mhi_free_controller(mhi_cntrl);
321
322 return err;
323 }
324
mhi_pci_remove(struct pci_dev * pdev)325 static void mhi_pci_remove(struct pci_dev *pdev)
326 {
327 struct mhi_controller *mhi_cntrl = pci_get_drvdata(pdev);
328
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);
333 }
334
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
340 };
341 module_pci_driver(mhi_pci_driver);
342
343 MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
344 MODULE_DESCRIPTION("Modem Host Interface (MHI) PCI controller driver");
345 MODULE_LICENSE("GPL");
346