1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PCI interface driver for DW SPI Core |
4 | * |
5 | * Copyright (c) 2009, 2014 Intel Corporation. |
6 | */ |
7 | |
8 | #include <linux/pci.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/spi/spi.h> |
12 | #include <linux/module.h> |
13 | |
14 | #include "spi-dw.h" |
15 | |
16 | #define DRIVER_NAME "dw_spi_pci" |
17 | |
18 | /* HW info for MRST Clk Control Unit, 32b reg per controller */ |
19 | #define MRST_SPI_CLK_BASE 100000000 /* 100m */ |
20 | #define MRST_CLK_SPI_REG 0xff11d86c |
21 | #define CLK_SPI_BDIV_OFFSET 0 |
22 | #define CLK_SPI_BDIV_MASK 0x00000007 |
23 | #define CLK_SPI_CDIV_OFFSET 9 |
24 | #define CLK_SPI_CDIV_MASK 0x00000e00 |
25 | #define CLK_SPI_DISABLE_OFFSET 8 |
26 | |
27 | struct dw_spi_pci_desc { |
28 | int (*setup)(struct dw_spi *); |
29 | u16 num_cs; |
30 | u16 bus_num; |
31 | u32 max_freq; |
32 | }; |
33 | |
34 | static int dw_spi_pci_mid_init(struct dw_spi *dws) |
35 | { |
36 | void __iomem *clk_reg; |
37 | u32 clk_cdiv; |
38 | |
39 | clk_reg = ioremap(MRST_CLK_SPI_REG, size: 16); |
40 | if (!clk_reg) |
41 | return -ENOMEM; |
42 | |
43 | /* Get SPI controller operating freq info */ |
44 | clk_cdiv = readl(addr: clk_reg + dws->bus_num * sizeof(u32)); |
45 | clk_cdiv &= CLK_SPI_CDIV_MASK; |
46 | clk_cdiv >>= CLK_SPI_CDIV_OFFSET; |
47 | dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); |
48 | |
49 | iounmap(addr: clk_reg); |
50 | |
51 | dw_spi_dma_setup_mfld(dws); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static int dw_spi_pci_generic_init(struct dw_spi *dws) |
57 | { |
58 | dw_spi_dma_setup_generic(dws); |
59 | |
60 | return 0; |
61 | } |
62 | |
63 | static struct dw_spi_pci_desc dw_spi_pci_mid_desc_1 = { |
64 | .setup = dw_spi_pci_mid_init, |
65 | .num_cs = 5, |
66 | .bus_num = 0, |
67 | }; |
68 | |
69 | static struct dw_spi_pci_desc dw_spi_pci_mid_desc_2 = { |
70 | .setup = dw_spi_pci_mid_init, |
71 | .num_cs = 2, |
72 | .bus_num = 1, |
73 | }; |
74 | |
75 | static struct dw_spi_pci_desc dw_spi_pci_ehl_desc = { |
76 | .setup = dw_spi_pci_generic_init, |
77 | .num_cs = 2, |
78 | .bus_num = -1, |
79 | .max_freq = 100000000, |
80 | }; |
81 | |
82 | static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
83 | { |
84 | struct dw_spi_pci_desc *desc = (struct dw_spi_pci_desc *)ent->driver_data; |
85 | struct dw_spi *dws; |
86 | int pci_bar = 0; |
87 | int ret; |
88 | |
89 | ret = pcim_enable_device(pdev); |
90 | if (ret) |
91 | return ret; |
92 | |
93 | dws = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dws), GFP_KERNEL); |
94 | if (!dws) |
95 | return -ENOMEM; |
96 | |
97 | /* Get basic io resource and map it */ |
98 | dws->paddr = pci_resource_start(pdev, pci_bar); |
99 | pci_set_master(dev: pdev); |
100 | |
101 | ret = pcim_iomap_regions(pdev, mask: 1 << pci_bar, name: pci_name(pdev)); |
102 | if (ret) |
103 | return ret; |
104 | |
105 | ret = pci_alloc_irq_vectors(dev: pdev, min_vecs: 1, max_vecs: 1, PCI_IRQ_ALL_TYPES); |
106 | if (ret < 0) |
107 | return ret; |
108 | |
109 | dws->regs = pcim_iomap_table(pdev)[pci_bar]; |
110 | dws->irq = pci_irq_vector(dev: pdev, nr: 0); |
111 | |
112 | /* |
113 | * Specific handling for platforms, like dma setup, |
114 | * clock rate, FIFO depth. |
115 | */ |
116 | if (desc) { |
117 | dws->num_cs = desc->num_cs; |
118 | dws->bus_num = desc->bus_num; |
119 | dws->max_freq = desc->max_freq; |
120 | |
121 | if (desc->setup) { |
122 | ret = desc->setup(dws); |
123 | if (ret) |
124 | goto err_free_irq_vectors; |
125 | } |
126 | } else { |
127 | ret = -ENODEV; |
128 | goto err_free_irq_vectors; |
129 | } |
130 | |
131 | ret = dw_spi_add_host(dev: &pdev->dev, dws); |
132 | if (ret) |
133 | goto err_free_irq_vectors; |
134 | |
135 | /* PCI hook and SPI hook use the same drv data */ |
136 | pci_set_drvdata(pdev, data: dws); |
137 | |
138 | dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n" , |
139 | pdev->vendor, pdev->device); |
140 | |
141 | pm_runtime_set_autosuspend_delay(dev: &pdev->dev, delay: 1000); |
142 | pm_runtime_use_autosuspend(dev: &pdev->dev); |
143 | pm_runtime_put_autosuspend(dev: &pdev->dev); |
144 | pm_runtime_allow(dev: &pdev->dev); |
145 | |
146 | return 0; |
147 | |
148 | err_free_irq_vectors: |
149 | pci_free_irq_vectors(dev: pdev); |
150 | return ret; |
151 | } |
152 | |
153 | static void dw_spi_pci_remove(struct pci_dev *pdev) |
154 | { |
155 | struct dw_spi *dws = pci_get_drvdata(pdev); |
156 | |
157 | pm_runtime_forbid(dev: &pdev->dev); |
158 | pm_runtime_get_noresume(dev: &pdev->dev); |
159 | |
160 | dw_spi_remove_host(dws); |
161 | pci_free_irq_vectors(dev: pdev); |
162 | } |
163 | |
164 | #ifdef CONFIG_PM_SLEEP |
165 | static int dw_spi_pci_suspend(struct device *dev) |
166 | { |
167 | struct dw_spi *dws = dev_get_drvdata(dev); |
168 | |
169 | return dw_spi_suspend_host(dws); |
170 | } |
171 | |
172 | static int dw_spi_pci_resume(struct device *dev) |
173 | { |
174 | struct dw_spi *dws = dev_get_drvdata(dev); |
175 | |
176 | return dw_spi_resume_host(dws); |
177 | } |
178 | #endif |
179 | |
180 | static SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume); |
181 | |
182 | static const struct pci_device_id dw_spi_pci_ids[] = { |
183 | /* Intel MID platform SPI controller 0 */ |
184 | /* |
185 | * The access to the device 8086:0801 is disabled by HW, since it's |
186 | * exclusively used by SCU to communicate with MSIC. |
187 | */ |
188 | /* Intel MID platform SPI controller 1 */ |
189 | { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&dw_spi_pci_mid_desc_1}, |
190 | /* Intel MID platform SPI controller 2 */ |
191 | { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&dw_spi_pci_mid_desc_2}, |
192 | /* Intel Elkhart Lake PSE SPI controllers */ |
193 | { PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, |
194 | { PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, |
195 | { PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, |
196 | { PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, |
197 | {}, |
198 | }; |
199 | MODULE_DEVICE_TABLE(pci, dw_spi_pci_ids); |
200 | |
201 | static struct pci_driver dw_spi_pci_driver = { |
202 | .name = DRIVER_NAME, |
203 | .id_table = dw_spi_pci_ids, |
204 | .probe = dw_spi_pci_probe, |
205 | .remove = dw_spi_pci_remove, |
206 | .driver = { |
207 | .pm = &dw_spi_pci_pm_ops, |
208 | }, |
209 | }; |
210 | module_pci_driver(dw_spi_pci_driver); |
211 | |
212 | MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>" ); |
213 | MODULE_DESCRIPTION("PCI interface driver for DW SPI Core" ); |
214 | MODULE_LICENSE("GPL v2" ); |
215 | MODULE_IMPORT_NS(SPI_DW_CORE); |
216 | |