1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | #include <linux/acpi.h> |
4 | #include <linux/clk.h> |
5 | #include <linux/device.h> |
6 | #include <linux/err.h> |
7 | #include <linux/init.h> |
8 | #include <linux/mod_devicetable.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/pm_runtime.h> |
11 | #include <linux/property.h> |
12 | #include <linux/types.h> |
13 | |
14 | #include "spi-pxa2xx.h" |
15 | |
16 | static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) |
17 | { |
18 | return param == chan->device->dev; |
19 | } |
20 | |
21 | static int |
22 | pxa2xx_spi_init_ssp(struct platform_device *pdev, struct ssp_device *ssp, enum pxa_ssp_type type) |
23 | { |
24 | struct device *dev = &pdev->dev; |
25 | struct resource *res; |
26 | int status; |
27 | u64 uid; |
28 | |
29 | ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
30 | if (IS_ERR(ptr: ssp->mmio_base)) |
31 | return PTR_ERR(ptr: ssp->mmio_base); |
32 | |
33 | ssp->phys_base = res->start; |
34 | |
35 | ssp->clk = devm_clk_get(dev, NULL); |
36 | if (IS_ERR(ptr: ssp->clk)) |
37 | return PTR_ERR(ptr: ssp->clk); |
38 | |
39 | ssp->irq = platform_get_irq(pdev, 0); |
40 | if (ssp->irq < 0) |
41 | return ssp->irq; |
42 | |
43 | ssp->type = type; |
44 | ssp->dev = dev; |
45 | |
46 | status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), integer: &uid); |
47 | if (status) |
48 | ssp->port_id = -1; |
49 | else |
50 | ssp->port_id = uid; |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | static void pxa2xx_spi_ssp_release(void *ssp) |
56 | { |
57 | pxa_ssp_free(ssp); |
58 | } |
59 | |
60 | static struct ssp_device *pxa2xx_spi_ssp_request(struct platform_device *pdev) |
61 | { |
62 | struct ssp_device *ssp; |
63 | int status; |
64 | |
65 | ssp = pxa_ssp_request(port: pdev->id, label: pdev->name); |
66 | if (!ssp) |
67 | return NULL; |
68 | |
69 | status = devm_add_action_or_reset(&pdev->dev, pxa2xx_spi_ssp_release, ssp); |
70 | if (status) |
71 | return ERR_PTR(error: status); |
72 | |
73 | return ssp; |
74 | } |
75 | |
76 | static struct pxa2xx_spi_controller * |
77 | pxa2xx_spi_init_pdata(struct platform_device *pdev) |
78 | { |
79 | struct pxa2xx_spi_controller *pdata; |
80 | struct device *dev = &pdev->dev; |
81 | struct device *parent = dev->parent; |
82 | const void *match = device_get_match_data(dev); |
83 | enum pxa_ssp_type type = SSP_UNDEFINED; |
84 | struct ssp_device *ssp; |
85 | bool is_lpss_priv; |
86 | u32 num_cs = 1; |
87 | int status; |
88 | |
89 | ssp = pxa2xx_spi_ssp_request(pdev); |
90 | if (IS_ERR(ptr: ssp)) |
91 | return ERR_CAST(ptr: ssp); |
92 | if (ssp) { |
93 | type = ssp->type; |
94 | } else if (match) { |
95 | type = (enum pxa_ssp_type)(uintptr_t)match; |
96 | } else { |
97 | u32 value; |
98 | |
99 | status = device_property_read_u32(dev, propname: "intel,spi-pxa2xx-type" , val: &value); |
100 | if (status) |
101 | return ERR_PTR(error: status); |
102 | |
103 | type = (enum pxa_ssp_type)value; |
104 | } |
105 | |
106 | /* Validate the SSP type correctness */ |
107 | if (!(type > SSP_UNDEFINED && type < SSP_MAX)) |
108 | return ERR_PTR(error: -EINVAL); |
109 | |
110 | pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL); |
111 | if (!pdata) |
112 | return ERR_PTR(error: -ENOMEM); |
113 | |
114 | /* Platforms with iDMA 64-bit */ |
115 | is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv" ); |
116 | if (is_lpss_priv) { |
117 | pdata->tx_param = parent; |
118 | pdata->rx_param = parent; |
119 | pdata->dma_filter = pxa2xx_spi_idma_filter; |
120 | } |
121 | |
122 | /* Read number of chip select pins, if provided */ |
123 | device_property_read_u32(dev, propname: "num-cs" , val: &num_cs); |
124 | |
125 | pdata->num_chipselect = num_cs; |
126 | pdata->is_target = device_property_read_bool(dev, propname: "spi-slave" ); |
127 | pdata->enable_dma = true; |
128 | pdata->dma_burst_size = 1; |
129 | |
130 | /* If SSP has been already enumerated, use it */ |
131 | if (ssp) |
132 | return pdata; |
133 | |
134 | status = pxa2xx_spi_init_ssp(pdev, ssp: &pdata->ssp, type); |
135 | if (status) |
136 | return ERR_PTR(error: status); |
137 | |
138 | return pdata; |
139 | } |
140 | |
141 | static int pxa2xx_spi_platform_probe(struct platform_device *pdev) |
142 | { |
143 | struct pxa2xx_spi_controller *platform_info; |
144 | struct device *dev = &pdev->dev; |
145 | struct ssp_device *ssp; |
146 | int ret; |
147 | |
148 | platform_info = dev_get_platdata(dev); |
149 | if (!platform_info) { |
150 | platform_info = pxa2xx_spi_init_pdata(pdev); |
151 | if (IS_ERR(ptr: platform_info)) |
152 | return dev_err_probe(dev, err: PTR_ERR(ptr: platform_info), fmt: "missing platform data\n" ); |
153 | } |
154 | |
155 | ssp = pxa2xx_spi_ssp_request(pdev); |
156 | if (IS_ERR(ptr: ssp)) |
157 | return PTR_ERR(ptr: ssp); |
158 | if (!ssp) |
159 | ssp = &platform_info->ssp; |
160 | |
161 | pm_runtime_set_autosuspend_delay(dev, delay: 50); |
162 | pm_runtime_use_autosuspend(dev); |
163 | pm_runtime_set_active(dev); |
164 | pm_runtime_enable(dev); |
165 | |
166 | ret = pxa2xx_spi_probe(dev, ssp, platform_info); |
167 | if (ret) |
168 | pm_runtime_disable(dev); |
169 | |
170 | return ret; |
171 | } |
172 | |
173 | static void pxa2xx_spi_platform_remove(struct platform_device *pdev) |
174 | { |
175 | struct device *dev = &pdev->dev; |
176 | |
177 | pm_runtime_get_sync(dev); |
178 | |
179 | pxa2xx_spi_remove(dev); |
180 | |
181 | pm_runtime_put_noidle(dev); |
182 | pm_runtime_disable(dev); |
183 | } |
184 | |
185 | static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { |
186 | { "80860F0E" }, |
187 | { "8086228E" }, |
188 | { "INT33C0" }, |
189 | { "INT33C1" }, |
190 | { "INT3430" }, |
191 | { "INT3431" }, |
192 | {} |
193 | }; |
194 | MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); |
195 | |
196 | static const struct of_device_id pxa2xx_spi_of_match[] = { |
197 | { .compatible = "marvell,mmp2-ssp" , .data = (void *)MMP2_SSP }, |
198 | {} |
199 | }; |
200 | MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match); |
201 | |
202 | static struct platform_driver driver = { |
203 | .driver = { |
204 | .name = "pxa2xx-spi" , |
205 | .pm = pm_ptr(&pxa2xx_spi_pm_ops), |
206 | .acpi_match_table = pxa2xx_spi_acpi_match, |
207 | .of_match_table = pxa2xx_spi_of_match, |
208 | }, |
209 | .probe = pxa2xx_spi_platform_probe, |
210 | .remove = pxa2xx_spi_platform_remove, |
211 | }; |
212 | |
213 | static int __init pxa2xx_spi_init(void) |
214 | { |
215 | return platform_driver_register(&driver); |
216 | } |
217 | subsys_initcall(pxa2xx_spi_init); |
218 | |
219 | static void __exit pxa2xx_spi_exit(void) |
220 | { |
221 | platform_driver_unregister(&driver); |
222 | } |
223 | module_exit(pxa2xx_spi_exit); |
224 | |
225 | MODULE_AUTHOR("Stephen Street" ); |
226 | MODULE_DESCRIPTION("PXA2xx SSP SPI Controller platform driver" ); |
227 | MODULE_LICENSE("GPL" ); |
228 | MODULE_IMPORT_NS("SPI_PXA2xx" ); |
229 | MODULE_ALIAS("platform:pxa2xx-spi" ); |
230 | MODULE_SOFTDEP("pre: dw_dmac" ); |
231 | |