1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Synopsys G210 Test Chip driver |
4 | * |
5 | * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com) |
6 | * |
7 | * Authors: Joao Pinto <jpinto@synopsys.com> |
8 | */ |
9 | |
10 | #include <ufs/ufshcd.h> |
11 | #include "ufshcd-dwc.h" |
12 | #include "tc-dwc-g210.h" |
13 | |
14 | #include <linux/module.h> |
15 | #include <linux/pci.h> |
16 | #include <linux/pm_runtime.h> |
17 | |
18 | /* Test Chip type expected values */ |
19 | #define TC_G210_20BIT 20 |
20 | #define TC_G210_40BIT 40 |
21 | #define TC_G210_INV 0 |
22 | |
23 | static int tc_type = TC_G210_INV; |
24 | module_param(tc_type, int, 0); |
25 | MODULE_PARM_DESC(tc_type, "Test Chip Type (20 = 20-bit, 40 = 40-bit)" ); |
26 | |
27 | /* |
28 | * struct ufs_hba_dwc_vops - UFS DWC specific variant operations |
29 | */ |
30 | static struct ufs_hba_variant_ops tc_dwc_g210_pci_hba_vops = { |
31 | .name = "tc-dwc-g210-pci" , |
32 | .link_startup_notify = ufshcd_dwc_link_startup_notify, |
33 | }; |
34 | |
35 | /** |
36 | * tc_dwc_g210_pci_remove - de-allocate PCI/SCSI host and host memory space |
37 | * data structure memory |
38 | * @pdev: pointer to PCI handle |
39 | */ |
40 | static void tc_dwc_g210_pci_remove(struct pci_dev *pdev) |
41 | { |
42 | struct ufs_hba *hba = pci_get_drvdata(pdev); |
43 | |
44 | pm_runtime_forbid(dev: &pdev->dev); |
45 | pm_runtime_get_noresume(dev: &pdev->dev); |
46 | ufshcd_remove(hba); |
47 | } |
48 | |
49 | /** |
50 | * tc_dwc_g210_pci_probe - probe routine of the driver |
51 | * @pdev: pointer to PCI device handle |
52 | * @id: PCI device id |
53 | * |
54 | * Return: 0 on success, non-zero value on failure. |
55 | */ |
56 | static int |
57 | tc_dwc_g210_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
58 | { |
59 | struct ufs_hba *hba; |
60 | void __iomem *mmio_base; |
61 | int err; |
62 | |
63 | /* Check Test Chip type and set the specific setup routine */ |
64 | if (tc_type == TC_G210_20BIT) { |
65 | tc_dwc_g210_pci_hba_vops.phy_initialization = |
66 | tc_dwc_g210_config_20_bit; |
67 | } else if (tc_type == TC_G210_40BIT) { |
68 | tc_dwc_g210_pci_hba_vops.phy_initialization = |
69 | tc_dwc_g210_config_40_bit; |
70 | } else { |
71 | dev_err(&pdev->dev, "test chip version not specified\n" ); |
72 | return -EPERM; |
73 | } |
74 | |
75 | err = pcim_enable_device(pdev); |
76 | if (err) { |
77 | dev_err(&pdev->dev, "pcim_enable_device failed\n" ); |
78 | return err; |
79 | } |
80 | |
81 | pci_set_master(dev: pdev); |
82 | |
83 | err = pcim_iomap_regions(pdev, mask: 1 << 0, UFSHCD); |
84 | if (err < 0) { |
85 | dev_err(&pdev->dev, "request and iomap failed\n" ); |
86 | return err; |
87 | } |
88 | |
89 | mmio_base = pcim_iomap_table(pdev)[0]; |
90 | |
91 | err = ufshcd_alloc_host(&pdev->dev, &hba); |
92 | if (err) { |
93 | dev_err(&pdev->dev, "Allocation failed\n" ); |
94 | return err; |
95 | } |
96 | |
97 | hba->vops = &tc_dwc_g210_pci_hba_vops; |
98 | |
99 | err = ufshcd_init(hba, mmio_base, pdev->irq); |
100 | if (err) { |
101 | dev_err(&pdev->dev, "Initialization failed\n" ); |
102 | return err; |
103 | } |
104 | |
105 | pm_runtime_put_noidle(dev: &pdev->dev); |
106 | pm_runtime_allow(dev: &pdev->dev); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { |
112 | SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume) |
113 | SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL) |
114 | .prepare = ufshcd_suspend_prepare, |
115 | .complete = ufshcd_resume_complete, |
116 | }; |
117 | |
118 | static const struct pci_device_id tc_dwc_g210_pci_tbl[] = { |
119 | { PCI_VENDOR_ID_SYNOPSYS, 0xB101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, |
120 | { PCI_VENDOR_ID_SYNOPSYS, 0xB102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, |
121 | { } /* terminate list */ |
122 | }; |
123 | |
124 | MODULE_DEVICE_TABLE(pci, tc_dwc_g210_pci_tbl); |
125 | |
126 | static struct pci_driver tc_dwc_g210_pci_driver = { |
127 | .name = "tc-dwc-g210-pci" , |
128 | .id_table = tc_dwc_g210_pci_tbl, |
129 | .probe = tc_dwc_g210_pci_probe, |
130 | .remove = tc_dwc_g210_pci_remove, |
131 | .driver = { |
132 | .pm = &tc_dwc_g210_pci_pm_ops |
133 | }, |
134 | }; |
135 | |
136 | module_pci_driver(tc_dwc_g210_pci_driver); |
137 | |
138 | MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>" ); |
139 | MODULE_DESCRIPTION("Synopsys Test Chip G210 PCI glue driver" ); |
140 | MODULE_LICENSE("Dual BSD/GPL" ); |
141 | |