blob: 3eac41072ad2410c7cdfbbe9fc5b1f745e88afa4 [file] [log] [blame]
Loic Poulain855a70c2020-10-21 19:18:19 +02001// 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
Loic Poulainb012ee62021-01-04 17:14:55 +010011#include <linux/aer.h>
Loic Poulain8ccc3272021-01-04 17:14:53 +010012#include <linux/delay.h>
Loic Poulain855a70c2020-10-21 19:18:19 +020013#include <linux/device.h>
14#include <linux/mhi.h>
15#include <linux/module.h>
16#include <linux/pci.h>
Loic Poulain8562d4f2021-01-04 17:14:56 +010017#include <linux/timer.h>
Loic Poulain73893372021-01-04 17:14:54 +010018#include <linux/workqueue.h>
Loic Poulain855a70c2020-10-21 19:18:19 +020019
20#define MHI_PCI_DEFAULT_BAR_NUM 0
21
Loic Poulain8ccc3272021-01-04 17:14:53 +010022#define MHI_POST_RESET_DELAY_MS 500
Loic Poulain8562d4f2021-01-04 17:14:56 +010023
24#define HEALTH_CHECK_PERIOD (HZ * 2)
25
Loic Poulain855a70c2020-10-21 19:18:19 +020026/**
27 * struct mhi_pci_dev_info - MHI PCI device specific information
28 * @config: MHI controller configuration
29 * @name: name of the PCI module
30 * @fw: firmware path (if any)
31 * @edl: emergency download mode firmware path (if any)
32 * @bar_num: PCI base address register to use for MHI MMIO register space
33 * @dma_data_width: DMA transfer word size (32 or 64 bits)
34 */
35struct mhi_pci_dev_info {
36 const struct mhi_controller_config *config;
37 const char *name;
38 const char *fw;
39 const char *edl;
40 unsigned int bar_num;
41 unsigned int dma_data_width;
42};
43
44#define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \
45 { \
46 .num = ch_num, \
47 .name = ch_name, \
48 .num_elements = el_count, \
49 .event_ring = ev_ring, \
50 .dir = DMA_TO_DEVICE, \
51 .ee_mask = BIT(MHI_EE_AMSS), \
52 .pollcfg = 0, \
53 .doorbell = MHI_DB_BRST_DISABLE, \
54 .lpm_notify = false, \
55 .offload_channel = false, \
56 .doorbell_mode_switch = false, \
57 } \
58
59#define MHI_CHANNEL_CONFIG_DL(ch_num, ch_name, el_count, ev_ring) \
60 { \
61 .num = ch_num, \
62 .name = ch_name, \
63 .num_elements = el_count, \
64 .event_ring = ev_ring, \
65 .dir = DMA_FROM_DEVICE, \
66 .ee_mask = BIT(MHI_EE_AMSS), \
67 .pollcfg = 0, \
68 .doorbell = MHI_DB_BRST_DISABLE, \
69 .lpm_notify = false, \
70 .offload_channel = false, \
71 .doorbell_mode_switch = false, \
72 }
73
74#define MHI_EVENT_CONFIG_CTRL(ev_ring) \
75 { \
76 .num_elements = 64, \
77 .irq_moderation_ms = 0, \
78 .irq = (ev_ring) + 1, \
79 .priority = 1, \
80 .mode = MHI_DB_BRST_DISABLE, \
81 .data_type = MHI_ER_CTRL, \
82 .hardware_event = false, \
83 .client_managed = false, \
84 .offload_channel = false, \
85 }
86
Loic Poulaineb967872021-01-04 17:14:52 +010087#define MHI_CHANNEL_CONFIG_HW_UL(ch_num, ch_name, el_count, ev_ring) \
88 { \
89 .num = ch_num, \
90 .name = ch_name, \
91 .num_elements = el_count, \
92 .event_ring = ev_ring, \
93 .dir = DMA_TO_DEVICE, \
94 .ee_mask = BIT(MHI_EE_AMSS), \
95 .pollcfg = 0, \
96 .doorbell = MHI_DB_BRST_ENABLE, \
97 .lpm_notify = false, \
98 .offload_channel = false, \
99 .doorbell_mode_switch = true, \
100 } \
101
102#define MHI_CHANNEL_CONFIG_HW_DL(ch_num, ch_name, el_count, ev_ring) \
103 { \
104 .num = ch_num, \
105 .name = ch_name, \
106 .num_elements = el_count, \
107 .event_ring = ev_ring, \
108 .dir = DMA_FROM_DEVICE, \
109 .ee_mask = BIT(MHI_EE_AMSS), \
110 .pollcfg = 0, \
111 .doorbell = MHI_DB_BRST_ENABLE, \
112 .lpm_notify = false, \
113 .offload_channel = false, \
114 .doorbell_mode_switch = true, \
115 }
116
Loic Poulain855a70c2020-10-21 19:18:19 +0200117#define MHI_EVENT_CONFIG_DATA(ev_ring) \
118 { \
119 .num_elements = 128, \
120 .irq_moderation_ms = 5, \
121 .irq = (ev_ring) + 1, \
122 .priority = 1, \
123 .mode = MHI_DB_BRST_DISABLE, \
124 .data_type = MHI_ER_DATA, \
125 .hardware_event = false, \
126 .client_managed = false, \
127 .offload_channel = false, \
128 }
129
130#define MHI_EVENT_CONFIG_HW_DATA(ev_ring, ch_num) \
131 { \
Loic Poulaind9f23ea2021-01-04 17:14:51 +0100132 .num_elements = 256, \
Loic Poulain855a70c2020-10-21 19:18:19 +0200133 .irq_moderation_ms = 5, \
134 .irq = (ev_ring) + 1, \
135 .priority = 1, \
136 .mode = MHI_DB_BRST_DISABLE, \
137 .data_type = MHI_ER_DATA, \
138 .hardware_event = true, \
139 .client_managed = false, \
140 .offload_channel = false, \
141 .channel = ch_num, \
142 }
143
144static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
145 MHI_CHANNEL_CONFIG_UL(12, "MBIM", 4, 0),
146 MHI_CHANNEL_CONFIG_DL(13, "MBIM", 4, 0),
147 MHI_CHANNEL_CONFIG_UL(14, "QMI", 4, 0),
148 MHI_CHANNEL_CONFIG_DL(15, "QMI", 4, 0),
149 MHI_CHANNEL_CONFIG_UL(20, "IPCR", 8, 0),
150 MHI_CHANNEL_CONFIG_DL(21, "IPCR", 8, 0),
Loic Poulaineb967872021-01-04 17:14:52 +0100151 MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 1),
152 MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 2),
Loic Poulain855a70c2020-10-21 19:18:19 +0200153};
154
155static const struct mhi_event_config modem_qcom_v1_mhi_events[] = {
156 /* first ring is control+data ring */
157 MHI_EVENT_CONFIG_CTRL(0),
158 /* Hardware channels request dedicated hardware event rings */
159 MHI_EVENT_CONFIG_HW_DATA(1, 100),
160 MHI_EVENT_CONFIG_HW_DATA(2, 101)
161};
162
163static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
164 .max_channels = 128,
165 .timeout_ms = 5000,
166 .num_channels = ARRAY_SIZE(modem_qcom_v1_mhi_channels),
167 .ch_cfg = modem_qcom_v1_mhi_channels,
168 .num_events = ARRAY_SIZE(modem_qcom_v1_mhi_events),
169 .event_cfg = modem_qcom_v1_mhi_events,
170};
171
172static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = {
173 .name = "qcom-sdx55m",
174 .fw = "qcom/sdx55m/sbl1.mbn",
175 .edl = "qcom/sdx55m/edl.mbn",
176 .config = &modem_qcom_v1_mhiv_config,
177 .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
178 .dma_data_width = 32
179};
180
181static const struct pci_device_id mhi_pci_id_table[] = {
182 { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0306),
183 .driver_data = (kernel_ulong_t) &mhi_qcom_sdx55_info },
184 { }
185};
186MODULE_DEVICE_TABLE(pci, mhi_pci_id_table);
187
Loic Poulain8ccc3272021-01-04 17:14:53 +0100188enum mhi_pci_device_status {
189 MHI_PCI_DEV_STARTED,
190};
191
192struct mhi_pci_device {
193 struct mhi_controller mhi_cntrl;
194 struct pci_saved_state *pci_state;
Loic Poulain73893372021-01-04 17:14:54 +0100195 struct work_struct recovery_work;
Loic Poulain8562d4f2021-01-04 17:14:56 +0100196 struct timer_list health_check_timer;
Loic Poulain8ccc3272021-01-04 17:14:53 +0100197 unsigned long status;
198};
199
Loic Poulain855a70c2020-10-21 19:18:19 +0200200static int mhi_pci_read_reg(struct mhi_controller *mhi_cntrl,
201 void __iomem *addr, u32 *out)
202{
203 *out = readl(addr);
204 return 0;
205}
206
207static void mhi_pci_write_reg(struct mhi_controller *mhi_cntrl,
208 void __iomem *addr, u32 val)
209{
210 writel(val, addr);
211}
212
213static void mhi_pci_status_cb(struct mhi_controller *mhi_cntrl,
214 enum mhi_callback cb)
215{
216 /* Nothing to do for now */
217}
218
Loic Poulain8ccc3272021-01-04 17:14:53 +0100219static bool mhi_pci_is_alive(struct mhi_controller *mhi_cntrl)
220{
221 struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
222 u16 vendor = 0;
223
224 if (pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor))
225 return false;
226
227 if (vendor == (u16) ~0 || vendor == 0)
228 return false;
229
230 return true;
231}
232
Loic Poulain855a70c2020-10-21 19:18:19 +0200233static int mhi_pci_claim(struct mhi_controller *mhi_cntrl,
234 unsigned int bar_num, u64 dma_mask)
235{
236 struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
237 int err;
238
239 err = pci_assign_resource(pdev, bar_num);
240 if (err)
241 return err;
242
243 err = pcim_enable_device(pdev);
244 if (err) {
245 dev_err(&pdev->dev, "failed to enable pci device: %d\n", err);
246 return err;
247 }
248
249 err = pcim_iomap_regions(pdev, 1 << bar_num, pci_name(pdev));
250 if (err) {
251 dev_err(&pdev->dev, "failed to map pci region: %d\n", err);
252 return err;
253 }
254 mhi_cntrl->regs = pcim_iomap_table(pdev)[bar_num];
255
256 err = pci_set_dma_mask(pdev, dma_mask);
257 if (err) {
258 dev_err(&pdev->dev, "Cannot set proper DMA mask\n");
259 return err;
260 }
261
262 err = pci_set_consistent_dma_mask(pdev, dma_mask);
263 if (err) {
264 dev_err(&pdev->dev, "set consistent dma mask failed\n");
265 return err;
266 }
267
268 pci_set_master(pdev);
269
270 return 0;
271}
272
273static int mhi_pci_get_irqs(struct mhi_controller *mhi_cntrl,
274 const struct mhi_controller_config *mhi_cntrl_config)
275{
276 struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
277 int nr_vectors, i;
278 int *irq;
279
280 /*
281 * Alloc one MSI vector for BHI + one vector per event ring, ideally...
282 * No explicit pci_free_irq_vectors required, done by pcim_release.
283 */
284 mhi_cntrl->nr_irqs = 1 + mhi_cntrl_config->num_events;
285
286 nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSI);
287 if (nr_vectors < 0) {
288 dev_err(&pdev->dev, "Error allocating MSI vectors %d\n",
289 nr_vectors);
290 return nr_vectors;
291 }
292
293 if (nr_vectors < mhi_cntrl->nr_irqs) {
294 dev_warn(&pdev->dev, "Not enough MSI vectors (%d/%d), use shared MSI\n",
295 nr_vectors, mhi_cntrl_config->num_events);
296 }
297
298 irq = devm_kcalloc(&pdev->dev, mhi_cntrl->nr_irqs, sizeof(int), GFP_KERNEL);
299 if (!irq)
300 return -ENOMEM;
301
302 for (i = 0; i < mhi_cntrl->nr_irqs; i++) {
303 int vector = i >= nr_vectors ? (nr_vectors - 1) : i;
304
305 irq[i] = pci_irq_vector(pdev, vector);
306 }
307
308 mhi_cntrl->irq = irq;
309
310 return 0;
311}
312
313static int mhi_pci_runtime_get(struct mhi_controller *mhi_cntrl)
314{
315 /* no PM for now */
316 return 0;
317}
318
319static void mhi_pci_runtime_put(struct mhi_controller *mhi_cntrl)
320{
321 /* no PM for now */
322}
323
Loic Poulain73893372021-01-04 17:14:54 +0100324static void mhi_pci_recovery_work(struct work_struct *work)
325{
326 struct mhi_pci_device *mhi_pdev = container_of(work, struct mhi_pci_device,
327 recovery_work);
328 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
329 struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
330 int err;
331
332 dev_warn(&pdev->dev, "device recovery started\n");
333
Loic Poulain8562d4f2021-01-04 17:14:56 +0100334 del_timer(&mhi_pdev->health_check_timer);
335
Loic Poulain73893372021-01-04 17:14:54 +0100336 /* Clean up MHI state */
337 if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
338 mhi_power_down(mhi_cntrl, false);
339 mhi_unprepare_after_power_down(mhi_cntrl);
340 }
341
342 /* Check if we can recover without full reset */
343 pci_set_power_state(pdev, PCI_D0);
344 pci_load_saved_state(pdev, mhi_pdev->pci_state);
345 pci_restore_state(pdev);
346
347 if (!mhi_pci_is_alive(mhi_cntrl))
348 goto err_try_reset;
349
350 err = mhi_prepare_for_power_up(mhi_cntrl);
351 if (err)
352 goto err_try_reset;
353
354 err = mhi_sync_power_up(mhi_cntrl);
355 if (err)
356 goto err_unprepare;
357
358 dev_dbg(&pdev->dev, "Recovery completed\n");
359
360 set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
Loic Poulain8562d4f2021-01-04 17:14:56 +0100361 mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
Loic Poulain73893372021-01-04 17:14:54 +0100362 return;
363
364err_unprepare:
365 mhi_unprepare_after_power_down(mhi_cntrl);
366err_try_reset:
367 if (pci_reset_function(pdev))
368 dev_err(&pdev->dev, "Recovery failed\n");
369}
370
Loic Poulain8562d4f2021-01-04 17:14:56 +0100371static void health_check(struct timer_list *t)
372{
373 struct mhi_pci_device *mhi_pdev = from_timer(mhi_pdev, t, health_check_timer);
374 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
375
376 if (!mhi_pci_is_alive(mhi_cntrl)) {
377 dev_err(mhi_cntrl->cntrl_dev, "Device died\n");
378 queue_work(system_long_wq, &mhi_pdev->recovery_work);
379 return;
380 }
381
382 /* reschedule in two seconds */
383 mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
384}
385
Loic Poulain855a70c2020-10-21 19:18:19 +0200386static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
387{
388 const struct mhi_pci_dev_info *info = (struct mhi_pci_dev_info *) id->driver_data;
389 const struct mhi_controller_config *mhi_cntrl_config;
Loic Poulain8ccc3272021-01-04 17:14:53 +0100390 struct mhi_pci_device *mhi_pdev;
Loic Poulain855a70c2020-10-21 19:18:19 +0200391 struct mhi_controller *mhi_cntrl;
392 int err;
393
394 dev_dbg(&pdev->dev, "MHI PCI device found: %s\n", info->name);
395
Loic Poulain8ccc3272021-01-04 17:14:53 +0100396 /* mhi_pdev.mhi_cntrl must be zero-initialized */
397 mhi_pdev = devm_kzalloc(&pdev->dev, sizeof(*mhi_pdev), GFP_KERNEL);
398 if (!mhi_pdev)
Loic Poulain855a70c2020-10-21 19:18:19 +0200399 return -ENOMEM;
400
Loic Poulain73893372021-01-04 17:14:54 +0100401 INIT_WORK(&mhi_pdev->recovery_work, mhi_pci_recovery_work);
Loic Poulain8562d4f2021-01-04 17:14:56 +0100402 timer_setup(&mhi_pdev->health_check_timer, health_check, 0);
Loic Poulain73893372021-01-04 17:14:54 +0100403
Loic Poulain855a70c2020-10-21 19:18:19 +0200404 mhi_cntrl_config = info->config;
Loic Poulain8ccc3272021-01-04 17:14:53 +0100405 mhi_cntrl = &mhi_pdev->mhi_cntrl;
406
Loic Poulain855a70c2020-10-21 19:18:19 +0200407 mhi_cntrl->cntrl_dev = &pdev->dev;
408 mhi_cntrl->iova_start = 0;
Loic Poulain4ea6fa22020-12-02 09:12:30 +0100409 mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(info->dma_data_width);
Loic Poulain855a70c2020-10-21 19:18:19 +0200410 mhi_cntrl->fw_image = info->fw;
411 mhi_cntrl->edl_image = info->edl;
412
413 mhi_cntrl->read_reg = mhi_pci_read_reg;
414 mhi_cntrl->write_reg = mhi_pci_write_reg;
415 mhi_cntrl->status_cb = mhi_pci_status_cb;
416 mhi_cntrl->runtime_get = mhi_pci_runtime_get;
417 mhi_cntrl->runtime_put = mhi_pci_runtime_put;
418
419 err = mhi_pci_claim(mhi_cntrl, info->bar_num, DMA_BIT_MASK(info->dma_data_width));
420 if (err)
Loic Poulain8ccc3272021-01-04 17:14:53 +0100421 return err;
Loic Poulain855a70c2020-10-21 19:18:19 +0200422
423 err = mhi_pci_get_irqs(mhi_cntrl, mhi_cntrl_config);
424 if (err)
Loic Poulain8ccc3272021-01-04 17:14:53 +0100425 return err;
Loic Poulain855a70c2020-10-21 19:18:19 +0200426
Loic Poulain8ccc3272021-01-04 17:14:53 +0100427 pci_set_drvdata(pdev, mhi_pdev);
428
429 /* Have stored pci confspace at hand for restore in sudden PCI error */
430 pci_save_state(pdev);
431 mhi_pdev->pci_state = pci_store_saved_state(pdev);
Loic Poulain855a70c2020-10-21 19:18:19 +0200432
Loic Poulainb012ee62021-01-04 17:14:55 +0100433 pci_enable_pcie_error_reporting(pdev);
434
Loic Poulain855a70c2020-10-21 19:18:19 +0200435 err = mhi_register_controller(mhi_cntrl, mhi_cntrl_config);
436 if (err)
Loic Poulain8ccc3272021-01-04 17:14:53 +0100437 return err;
Loic Poulain855a70c2020-10-21 19:18:19 +0200438
439 /* MHI bus does not power up the controller by default */
440 err = mhi_prepare_for_power_up(mhi_cntrl);
441 if (err) {
442 dev_err(&pdev->dev, "failed to prepare MHI controller\n");
443 goto err_unregister;
444 }
445
446 err = mhi_sync_power_up(mhi_cntrl);
447 if (err) {
448 dev_err(&pdev->dev, "failed to power up MHI controller\n");
449 goto err_unprepare;
450 }
451
Loic Poulain8ccc3272021-01-04 17:14:53 +0100452 set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
453
Loic Poulain8562d4f2021-01-04 17:14:56 +0100454 /* start health check */
455 mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
456
Loic Poulain855a70c2020-10-21 19:18:19 +0200457 return 0;
458
459err_unprepare:
460 mhi_unprepare_after_power_down(mhi_cntrl);
461err_unregister:
462 mhi_unregister_controller(mhi_cntrl);
Loic Poulain855a70c2020-10-21 19:18:19 +0200463
464 return err;
465}
466
467static void mhi_pci_remove(struct pci_dev *pdev)
468{
Loic Poulain8ccc3272021-01-04 17:14:53 +0100469 struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
470 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
Loic Poulain855a70c2020-10-21 19:18:19 +0200471
Loic Poulain8562d4f2021-01-04 17:14:56 +0100472 del_timer(&mhi_pdev->health_check_timer);
Loic Poulain73893372021-01-04 17:14:54 +0100473 cancel_work_sync(&mhi_pdev->recovery_work);
474
Loic Poulain8ccc3272021-01-04 17:14:53 +0100475 if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
476 mhi_power_down(mhi_cntrl, true);
477 mhi_unprepare_after_power_down(mhi_cntrl);
478 }
479
Loic Poulain855a70c2020-10-21 19:18:19 +0200480 mhi_unregister_controller(mhi_cntrl);
Loic Poulain855a70c2020-10-21 19:18:19 +0200481}
482
Loic Poulain8ccc3272021-01-04 17:14:53 +0100483static void mhi_pci_reset_prepare(struct pci_dev *pdev)
484{
485 struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
486 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
487
488 dev_info(&pdev->dev, "reset\n");
489
Loic Poulain8562d4f2021-01-04 17:14:56 +0100490 del_timer(&mhi_pdev->health_check_timer);
491
Loic Poulain8ccc3272021-01-04 17:14:53 +0100492 /* Clean up MHI state */
493 if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
494 mhi_power_down(mhi_cntrl, false);
495 mhi_unprepare_after_power_down(mhi_cntrl);
496 }
497
498 /* cause internal device reset */
499 mhi_soc_reset(mhi_cntrl);
500
501 /* Be sure device reset has been executed */
502 msleep(MHI_POST_RESET_DELAY_MS);
503}
504
505static void mhi_pci_reset_done(struct pci_dev *pdev)
506{
507 struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
508 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
509 int err;
510
511 /* Restore initial known working PCI state */
512 pci_load_saved_state(pdev, mhi_pdev->pci_state);
513 pci_restore_state(pdev);
514
515 /* Is device status available ? */
516 if (!mhi_pci_is_alive(mhi_cntrl)) {
517 dev_err(&pdev->dev, "reset failed\n");
518 return;
519 }
520
521 err = mhi_prepare_for_power_up(mhi_cntrl);
522 if (err) {
523 dev_err(&pdev->dev, "failed to prepare MHI controller\n");
524 return;
525 }
526
527 err = mhi_sync_power_up(mhi_cntrl);
528 if (err) {
529 dev_err(&pdev->dev, "failed to power up MHI controller\n");
530 mhi_unprepare_after_power_down(mhi_cntrl);
531 return;
532 }
533
534 set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
Loic Poulain8562d4f2021-01-04 17:14:56 +0100535 mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
Loic Poulain8ccc3272021-01-04 17:14:53 +0100536}
537
Loic Poulainb012ee62021-01-04 17:14:55 +0100538static pci_ers_result_t mhi_pci_error_detected(struct pci_dev *pdev,
539 pci_channel_state_t state)
540{
541 struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
542 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
543
544 dev_err(&pdev->dev, "PCI error detected, state = %u\n", state);
545
546 if (state == pci_channel_io_perm_failure)
547 return PCI_ERS_RESULT_DISCONNECT;
548
549 /* Clean up MHI state */
550 if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
551 mhi_power_down(mhi_cntrl, false);
552 mhi_unprepare_after_power_down(mhi_cntrl);
553 } else {
554 /* Nothing to do */
555 return PCI_ERS_RESULT_RECOVERED;
556 }
557
558 pci_disable_device(pdev);
559
560 return PCI_ERS_RESULT_NEED_RESET;
561}
562
563static pci_ers_result_t mhi_pci_slot_reset(struct pci_dev *pdev)
564{
565 if (pci_enable_device(pdev)) {
566 dev_err(&pdev->dev, "Cannot re-enable PCI device after reset.\n");
567 return PCI_ERS_RESULT_DISCONNECT;
568 }
569
570 return PCI_ERS_RESULT_RECOVERED;
571}
572
573static void mhi_pci_io_resume(struct pci_dev *pdev)
574{
575 struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
576
577 dev_err(&pdev->dev, "PCI slot reset done\n");
578
579 queue_work(system_long_wq, &mhi_pdev->recovery_work);
580}
581
Loic Poulain8ccc3272021-01-04 17:14:53 +0100582static const struct pci_error_handlers mhi_pci_err_handler = {
Loic Poulainb012ee62021-01-04 17:14:55 +0100583 .error_detected = mhi_pci_error_detected,
584 .slot_reset = mhi_pci_slot_reset,
585 .resume = mhi_pci_io_resume,
Loic Poulain8ccc3272021-01-04 17:14:53 +0100586 .reset_prepare = mhi_pci_reset_prepare,
587 .reset_done = mhi_pci_reset_done,
588};
589
Loic Poulain73893372021-01-04 17:14:54 +0100590static int __maybe_unused mhi_pci_suspend(struct device *dev)
591{
592 struct pci_dev *pdev = to_pci_dev(dev);
593 struct mhi_pci_device *mhi_pdev = dev_get_drvdata(dev);
594 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
595
Loic Poulain8562d4f2021-01-04 17:14:56 +0100596 del_timer(&mhi_pdev->health_check_timer);
Loic Poulain73893372021-01-04 17:14:54 +0100597 cancel_work_sync(&mhi_pdev->recovery_work);
598
599 /* Transition to M3 state */
600 mhi_pm_suspend(mhi_cntrl);
601
602 pci_save_state(pdev);
603 pci_disable_device(pdev);
604 pci_wake_from_d3(pdev, true);
605 pci_set_power_state(pdev, PCI_D3hot);
606
607 return 0;
608}
609
610static int __maybe_unused mhi_pci_resume(struct device *dev)
611{
612 struct pci_dev *pdev = to_pci_dev(dev);
613 struct mhi_pci_device *mhi_pdev = dev_get_drvdata(dev);
614 struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
615 int err;
616
617 pci_set_power_state(pdev, PCI_D0);
618 pci_restore_state(pdev);
619 pci_set_master(pdev);
620
621 err = pci_enable_device(pdev);
622 if (err)
623 goto err_recovery;
624
625 /* Exit M3, transition to M0 state */
626 err = mhi_pm_resume(mhi_cntrl);
627 if (err) {
628 dev_err(&pdev->dev, "failed to resume device: %d\n", err);
629 goto err_recovery;
630 }
631
Loic Poulain8562d4f2021-01-04 17:14:56 +0100632 /* Resume health check */
633 mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
634
Loic Poulain73893372021-01-04 17:14:54 +0100635 return 0;
636
637err_recovery:
638 /* The device may have loose power or crashed, try recovering it */
639 queue_work(system_long_wq, &mhi_pdev->recovery_work);
640
641 return err;
642}
643
644static const struct dev_pm_ops mhi_pci_pm_ops = {
645 SET_SYSTEM_SLEEP_PM_OPS(mhi_pci_suspend, mhi_pci_resume)
646};
647
Loic Poulain855a70c2020-10-21 19:18:19 +0200648static struct pci_driver mhi_pci_driver = {
649 .name = "mhi-pci-generic",
650 .id_table = mhi_pci_id_table,
651 .probe = mhi_pci_probe,
Loic Poulain8ccc3272021-01-04 17:14:53 +0100652 .remove = mhi_pci_remove,
653 .err_handler = &mhi_pci_err_handler,
Loic Poulain73893372021-01-04 17:14:54 +0100654 .driver.pm = &mhi_pci_pm_ops
Loic Poulain855a70c2020-10-21 19:18:19 +0200655};
656module_pci_driver(mhi_pci_driver);
657
658MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
659MODULE_DESCRIPTION("Modem Host Interface (MHI) PCI controller driver");
660MODULE_LICENSE("GPL");