]>
Commit | Line | Data |
---|---|---|
d6ea3df0 SAS |
1 | /* |
2 | * CE4100's SPI device is more or less the same one as found on PXA | |
3 | * | |
e379d2cd | 4 | * Copyright (C) 2016, Intel Corporation |
d6ea3df0 | 5 | */ |
e379d2cd AS |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/module.h> | |
8 | #include <linux/of_device.h> | |
d6ea3df0 SAS |
9 | #include <linux/pci.h> |
10 | #include <linux/platform_device.h> | |
d6ea3df0 SAS |
11 | #include <linux/spi/pxa2xx_spi.h> |
12 | ||
b729bf34 MW |
13 | #include <linux/dmaengine.h> |
14 | #include <linux/platform_data/dma-dw.h> | |
15 | ||
d6ba32d5 | 16 | enum { |
e379d2cd | 17 | PORT_QUARK_X1000, |
d6ba32d5 | 18 | PORT_BYT, |
4f470910 | 19 | PORT_MRFLD, |
39d36536 MW |
20 | PORT_BSW0, |
21 | PORT_BSW1, | |
22 | PORT_BSW2, | |
e379d2cd | 23 | PORT_CE4100, |
caba248d | 24 | PORT_LPT, |
d6ba32d5 CCE |
25 | }; |
26 | ||
27 | struct pxa_spi_info { | |
28 | enum pxa_ssp_type type; | |
29 | int port_id; | |
30 | int num_chipselect; | |
afa93c90 | 31 | unsigned long max_clk_rate; |
b729bf34 MW |
32 | |
33 | /* DMA channel request parameters */ | |
743485ea | 34 | bool (*dma_filter)(struct dma_chan *chan, void *param); |
b729bf34 MW |
35 | void *tx_param; |
36 | void *rx_param; | |
743485ea AS |
37 | |
38 | int (*setup)(struct pci_dev *pdev, struct pxa_spi_info *c); | |
d6ba32d5 CCE |
39 | }; |
40 | ||
b729bf34 MW |
41 | static struct dw_dma_slave byt_tx_param = { .dst_id = 0 }; |
42 | static struct dw_dma_slave byt_rx_param = { .src_id = 1 }; | |
43 | ||
39d36536 MW |
44 | static struct dw_dma_slave bsw0_tx_param = { .dst_id = 0 }; |
45 | static struct dw_dma_slave bsw0_rx_param = { .src_id = 1 }; | |
46 | static struct dw_dma_slave bsw1_tx_param = { .dst_id = 6 }; | |
47 | static struct dw_dma_slave bsw1_rx_param = { .src_id = 7 }; | |
48 | static struct dw_dma_slave bsw2_tx_param = { .dst_id = 8 }; | |
49 | static struct dw_dma_slave bsw2_rx_param = { .src_id = 9 }; | |
50 | ||
caba248d LL |
51 | static struct dw_dma_slave lpt_tx_param = { .dst_id = 0 }; |
52 | static struct dw_dma_slave lpt_rx_param = { .src_id = 1 }; | |
53 | ||
b729bf34 MW |
54 | static bool lpss_dma_filter(struct dma_chan *chan, void *param) |
55 | { | |
56 | struct dw_dma_slave *dws = param; | |
57 | ||
58 | if (dws->dma_dev != chan->device->dev) | |
59 | return false; | |
60 | ||
61 | chan->private = dws; | |
62 | return true; | |
63 | } | |
64 | ||
743485ea AS |
65 | static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) |
66 | { | |
67 | struct pci_dev *dma_dev; | |
68 | ||
69 | c->num_chipselect = 1; | |
70 | c->max_clk_rate = 50000000; | |
71 | ||
72 | dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); | |
73 | ||
74 | if (c->tx_param) { | |
75 | struct dw_dma_slave *slave = c->tx_param; | |
76 | ||
77 | slave->dma_dev = &dma_dev->dev; | |
78 | slave->m_master = 0; | |
79 | slave->p_master = 1; | |
80 | } | |
81 | ||
82 | if (c->rx_param) { | |
83 | struct dw_dma_slave *slave = c->rx_param; | |
84 | ||
85 | slave->dma_dev = &dma_dev->dev; | |
86 | slave->m_master = 0; | |
87 | slave->p_master = 1; | |
88 | } | |
89 | ||
90 | c->dma_filter = lpss_dma_filter; | |
91 | return 0; | |
92 | } | |
93 | ||
4f470910 AS |
94 | static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) |
95 | { | |
96 | switch (PCI_FUNC(dev->devfn)) { | |
97 | case 0: | |
98 | c->port_id = 3; | |
99 | c->num_chipselect = 1; | |
100 | break; | |
101 | case 1: | |
102 | c->port_id = 5; | |
103 | c->num_chipselect = 4; | |
104 | break; | |
105 | case 2: | |
106 | c->port_id = 6; | |
107 | c->num_chipselect = 1; | |
108 | break; | |
109 | default: | |
110 | return -ENODEV; | |
111 | } | |
112 | return 0; | |
113 | } | |
114 | ||
d6ba32d5 CCE |
115 | static struct pxa_spi_info spi_info_configs[] = { |
116 | [PORT_CE4100] = { | |
117 | .type = PXA25x_SSP, | |
118 | .port_id = -1, | |
119 | .num_chipselect = -1, | |
afa93c90 | 120 | .max_clk_rate = 3686400, |
d6ba32d5 CCE |
121 | }, |
122 | [PORT_BYT] = { | |
03fbf488 | 123 | .type = LPSS_BYT_SSP, |
d6ba32d5 | 124 | .port_id = 0, |
743485ea | 125 | .setup = lpss_spi_setup, |
b729bf34 MW |
126 | .tx_param = &byt_tx_param, |
127 | .rx_param = &byt_rx_param, | |
d6ba32d5 | 128 | }, |
39d36536 | 129 | [PORT_BSW0] = { |
ca80ef71 | 130 | .type = LPSS_BSW_SSP, |
39d36536 | 131 | .port_id = 0, |
743485ea | 132 | .setup = lpss_spi_setup, |
39d36536 MW |
133 | .tx_param = &bsw0_tx_param, |
134 | .rx_param = &bsw0_rx_param, | |
135 | }, | |
136 | [PORT_BSW1] = { | |
ca80ef71 | 137 | .type = LPSS_BSW_SSP, |
39d36536 | 138 | .port_id = 1, |
743485ea | 139 | .setup = lpss_spi_setup, |
39d36536 MW |
140 | .tx_param = &bsw1_tx_param, |
141 | .rx_param = &bsw1_rx_param, | |
142 | }, | |
143 | [PORT_BSW2] = { | |
ca80ef71 | 144 | .type = LPSS_BSW_SSP, |
39d36536 | 145 | .port_id = 2, |
743485ea | 146 | .setup = lpss_spi_setup, |
39d36536 MW |
147 | .tx_param = &bsw2_tx_param, |
148 | .rx_param = &bsw2_rx_param, | |
d6ba32d5 | 149 | }, |
4f470910 AS |
150 | [PORT_MRFLD] = { |
151 | .type = PXA27x_SSP, | |
152 | .max_clk_rate = 25000000, | |
153 | .setup = mrfld_spi_setup, | |
154 | }, | |
e5262d05 WC |
155 | [PORT_QUARK_X1000] = { |
156 | .type = QUARK_X1000_SSP, | |
157 | .port_id = -1, | |
158 | .num_chipselect = 1, | |
159 | .max_clk_rate = 50000000, | |
160 | }, | |
caba248d LL |
161 | [PORT_LPT] = { |
162 | .type = LPSS_LPT_SSP, | |
163 | .port_id = 0, | |
743485ea | 164 | .setup = lpss_spi_setup, |
caba248d LL |
165 | .tx_param = &lpt_tx_param, |
166 | .rx_param = &lpt_rx_param, | |
167 | }, | |
d6ba32d5 CCE |
168 | }; |
169 | ||
170 | static int pxa2xx_spi_pci_probe(struct pci_dev *dev, | |
d6ea3df0 SAS |
171 | const struct pci_device_id *ent) |
172 | { | |
0202775b | 173 | struct platform_device_info pi; |
d6ea3df0 | 174 | int ret; |
d6ea3df0 | 175 | struct platform_device *pdev; |
0f3e1d27 | 176 | struct pxa2xx_spi_master spi_pdata; |
d6ea3df0 | 177 | struct ssp_device *ssp; |
d6ba32d5 | 178 | struct pxa_spi_info *c; |
afa93c90 | 179 | char buf[40]; |
d6ea3df0 | 180 | |
0202775b | 181 | ret = pcim_enable_device(dev); |
d6ea3df0 SAS |
182 | if (ret) |
183 | return ret; | |
184 | ||
0202775b | 185 | ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI"); |
c1346340 | 186 | if (ret) |
d6ea3df0 | 187 | return ret; |
d6ea3df0 | 188 | |
d6ba32d5 | 189 | c = &spi_info_configs[ent->driver_data]; |
743485ea AS |
190 | if (c->setup) { |
191 | ret = c->setup(dev, c); | |
192 | if (ret) | |
193 | return ret; | |
b729bf34 MW |
194 | } |
195 | ||
743485ea AS |
196 | memset(&spi_pdata, 0, sizeof(spi_pdata)); |
197 | spi_pdata.num_chipselect = (c->num_chipselect > 0) ? c->num_chipselect : dev->devfn; | |
198 | spi_pdata.dma_filter = c->dma_filter; | |
b729bf34 MW |
199 | spi_pdata.tx_param = c->tx_param; |
200 | spi_pdata.rx_param = c->rx_param; | |
201 | spi_pdata.enable_dma = c->rx_param && c->tx_param; | |
d6ea3df0 | 202 | |
851bacf5 | 203 | ssp = &spi_pdata.ssp; |
d6ea3df0 | 204 | ssp->phys_base = pci_resource_start(dev, 0); |
0202775b | 205 | ssp->mmio_base = pcim_iomap_table(dev)[0]; |
d6ea3df0 | 206 | ssp->irq = dev->irq; |
d6ba32d5 CCE |
207 | ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn; |
208 | ssp->type = c->type; | |
d6ea3df0 | 209 | |
afa93c90 | 210 | snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id); |
280af2b8 SB |
211 | ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL, 0, |
212 | c->max_clk_rate); | |
afa93c90 CCE |
213 | if (IS_ERR(ssp->clk)) |
214 | return PTR_ERR(ssp->clk); | |
215 | ||
0202775b MW |
216 | memset(&pi, 0, sizeof(pi)); |
217 | pi.parent = &dev->dev; | |
218 | pi.name = "pxa2xx-spi"; | |
219 | pi.id = ssp->port_id; | |
220 | pi.data = &spi_pdata; | |
221 | pi.size_data = sizeof(spi_pdata); | |
d6ea3df0 | 222 | |
0202775b | 223 | pdev = platform_device_register_full(&pi); |
afa93c90 CCE |
224 | if (IS_ERR(pdev)) { |
225 | clk_unregister(ssp->clk); | |
d77b5382 | 226 | return PTR_ERR(pdev); |
afa93c90 | 227 | } |
d6ea3df0 | 228 | |
851bacf5 | 229 | pci_set_drvdata(dev, pdev); |
d6ea3df0 | 230 | |
0202775b | 231 | return 0; |
d6ea3df0 SAS |
232 | } |
233 | ||
d6ba32d5 | 234 | static void pxa2xx_spi_pci_remove(struct pci_dev *dev) |
d6ea3df0 | 235 | { |
851bacf5 | 236 | struct platform_device *pdev = pci_get_drvdata(dev); |
afa93c90 CCE |
237 | struct pxa2xx_spi_master *spi_pdata; |
238 | ||
239 | spi_pdata = dev_get_platdata(&pdev->dev); | |
d6ea3df0 | 240 | |
851bacf5 | 241 | platform_device_unregister(pdev); |
afa93c90 | 242 | clk_unregister(spi_pdata->ssp.clk); |
d6ea3df0 SAS |
243 | } |
244 | ||
d6ba32d5 | 245 | static const struct pci_device_id pxa2xx_spi_pci_devices[] = { |
e5262d05 | 246 | { PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 }, |
d6ba32d5 | 247 | { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT }, |
4f470910 | 248 | { PCI_VDEVICE(INTEL, 0x1194), PORT_MRFLD }, |
39d36536 MW |
249 | { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 }, |
250 | { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 }, | |
251 | { PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 }, | |
e379d2cd | 252 | { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 }, |
caba248d | 253 | { PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT }, |
d6ea3df0 SAS |
254 | { }, |
255 | }; | |
d6ba32d5 | 256 | MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices); |
d6ea3df0 | 257 | |
d6ba32d5 CCE |
258 | static struct pci_driver pxa2xx_spi_pci_driver = { |
259 | .name = "pxa2xx_spi_pci", | |
260 | .id_table = pxa2xx_spi_pci_devices, | |
261 | .probe = pxa2xx_spi_pci_probe, | |
262 | .remove = pxa2xx_spi_pci_remove, | |
d6ea3df0 SAS |
263 | }; |
264 | ||
d6ba32d5 | 265 | module_pci_driver(pxa2xx_spi_pci_driver); |
d6ea3df0 | 266 | |
d6ba32d5 | 267 | MODULE_DESCRIPTION("CE4100/LPSS PCI-SPI glue code for PXA's driver"); |
d6ea3df0 SAS |
268 | MODULE_LICENSE("GPL v2"); |
269 | MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); |