spi: atmel-quadspi: Convert to platform remove
[linux-block.git] / drivers / spi / spi-dw-mmio.c
CommitLineData
75a6faf6 1// SPDX-License-Identifier: GPL-2.0-only
f7b6fd6d 2/*
ca632f55 3 * Memory-mapped interface driver for DW SPI Core
f7b6fd6d
JHD
4 *
5 * Copyright (c) 2010, Octasic semiconductor.
f7b6fd6d
JHD
6 */
7
8#include <linux/clk.h>
50c01fc3 9#include <linux/err.h>
f7b6fd6d 10#include <linux/platform_device.h>
b9fc2d20 11#include <linux/pm_runtime.h>
5a0e3ad6 12#include <linux/slab.h>
f7b6fd6d 13#include <linux/spi/spi.h>
568a60ed 14#include <linux/scatterlist.h>
c2c25cc3 15#include <linux/mfd/syscon.h>
d7614de4 16#include <linux/module.h>
22dae17e 17#include <linux/of.h>
22dae17e 18#include <linux/of_platform.h>
32215a6c 19#include <linux/acpi.h>
9899995e 20#include <linux/property.h>
c2c25cc3 21#include <linux/regmap.h>
7830c0ef 22#include <linux/reset.h>
568a60ed 23
ca632f55 24#include "spi-dw.h"
f7b6fd6d
JHD
25
26#define DRIVER_NAME "dw_spi_mmio"
27
28struct dw_spi_mmio {
0a4c1d7d
JHD
29 struct dw_spi dws;
30 struct clk *clk;
560ee7e9 31 struct clk *pclk;
c2c25cc3 32 void *priv;
7830c0ef 33 struct reset_control *rstc;
f7b6fd6d
JHD
34};
35
c2c25cc3 36#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24
c2c25cc3 37#define OCELOT_IF_SI_OWNER_OFFSET 4
be17ee0d 38#define JAGUAR2_IF_SI_OWNER_OFFSET 6
c1d8b082 39#define MSCC_IF_SI_OWNER_MASK GENMASK(1, 0)
c2c25cc3
AB
40#define MSCC_IF_SI_OWNER_SISL 0
41#define MSCC_IF_SI_OWNER_SIBM 1
42#define MSCC_IF_SI_OWNER_SIMC 2
43
44#define MSCC_SPI_MST_SW_MODE 0x14
45#define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13)
46#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5)
47
53a09635
LP
48#define SPARX5_FORCE_ENA 0xa4
49#define SPARX5_FORCE_VAL 0xa8
50
c2c25cc3
AB
51struct dw_spi_mscc {
52 struct regmap *syscon;
53a09635 53 void __iomem *spi_mst; /* Not sparx5 */
c2c25cc3
AB
54};
55
56/*
57 * The Designware SPI controller (referred to as master in the documentation)
58 * automatically deasserts chip select when the tx fifo is empty. The chip
db56d030 59 * selects then needs to be either driven as GPIOs or, for the first 4 using
c2c25cc3
AB
60 * the SPI boot controller registers. the final chip select is an OR gate
61 * between the Designware SPI controller and the SPI boot controller.
62 */
63static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable)
64{
65 struct dw_spi *dws = spi_master_get_devdata(spi->master);
66 struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
67 struct dw_spi_mscc *dwsmscc = dwsmmio->priv;
9e264f3f 68 u32 cs = spi_get_chipselect(spi, 0);
c2c25cc3
AB
69
70 if (cs < 4) {
71 u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE;
72
73 if (!enable)
74 sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs));
75
76 writel(sw_mode, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE);
77 }
78
79 dw_spi_set_cs(spi, enable);
80}
81
82static int dw_spi_mscc_init(struct platform_device *pdev,
be17ee0d
AB
83 struct dw_spi_mmio *dwsmmio,
84 const char *cpu_syscon, u32 if_si_owner_offset)
c2c25cc3
AB
85{
86 struct dw_spi_mscc *dwsmscc;
c2c25cc3
AB
87
88 dwsmscc = devm_kzalloc(&pdev->dev, sizeof(*dwsmscc), GFP_KERNEL);
89 if (!dwsmscc)
90 return -ENOMEM;
91
5cc6fdcc 92 dwsmscc->spi_mst = devm_platform_ioremap_resource(pdev, 1);
c2c25cc3
AB
93 if (IS_ERR(dwsmscc->spi_mst)) {
94 dev_err(&pdev->dev, "SPI_MST region map failed\n");
95 return PTR_ERR(dwsmscc->spi_mst);
96 }
97
be17ee0d 98 dwsmscc->syscon = syscon_regmap_lookup_by_compatible(cpu_syscon);
c2c25cc3
AB
99 if (IS_ERR(dwsmscc->syscon))
100 return PTR_ERR(dwsmscc->syscon);
101
102 /* Deassert all CS */
103 writel(0, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE);
104
105 /* Select the owner of the SI interface */
106 regmap_update_bits(dwsmscc->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL,
c1d8b082 107 MSCC_IF_SI_OWNER_MASK << if_si_owner_offset,
be17ee0d 108 MSCC_IF_SI_OWNER_SIMC << if_si_owner_offset);
c2c25cc3
AB
109
110 dwsmmio->dws.set_cs = dw_spi_mscc_set_cs;
111 dwsmmio->priv = dwsmscc;
112
113 return 0;
114}
115
be17ee0d
AB
116static int dw_spi_mscc_ocelot_init(struct platform_device *pdev,
117 struct dw_spi_mmio *dwsmmio)
118{
119 return dw_spi_mscc_init(pdev, dwsmmio, "mscc,ocelot-cpu-syscon",
120 OCELOT_IF_SI_OWNER_OFFSET);
121}
122
123static int dw_spi_mscc_jaguar2_init(struct platform_device *pdev,
124 struct dw_spi_mmio *dwsmmio)
125{
126 return dw_spi_mscc_init(pdev, dwsmmio, "mscc,jaguar2-cpu-syscon",
127 JAGUAR2_IF_SI_OWNER_OFFSET);
128}
129
53a09635
LP
130/*
131 * The Designware SPI controller (referred to as master in the
132 * documentation) automatically deasserts chip select when the tx fifo
133 * is empty. The chip selects then needs to be driven by a CS override
134 * register. enable is an active low signal.
135 */
136static void dw_spi_sparx5_set_cs(struct spi_device *spi, bool enable)
137{
138 struct dw_spi *dws = spi_master_get_devdata(spi->master);
139 struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
140 struct dw_spi_mscc *dwsmscc = dwsmmio->priv;
9e264f3f 141 u8 cs = spi_get_chipselect(spi, 0);
53a09635
LP
142
143 if (!enable) {
144 /* CS override drive enable */
145 regmap_write(dwsmscc->syscon, SPARX5_FORCE_ENA, 1);
146 /* Now set CSx enabled */
147 regmap_write(dwsmscc->syscon, SPARX5_FORCE_VAL, ~BIT(cs));
148 /* Allow settle */
149 usleep_range(1, 5);
150 } else {
151 /* CS value */
152 regmap_write(dwsmscc->syscon, SPARX5_FORCE_VAL, ~0);
153 /* Allow settle */
154 usleep_range(1, 5);
155 /* CS override drive disable */
156 regmap_write(dwsmscc->syscon, SPARX5_FORCE_ENA, 0);
157 }
158
159 dw_spi_set_cs(spi, enable);
160}
161
162static int dw_spi_mscc_sparx5_init(struct platform_device *pdev,
163 struct dw_spi_mmio *dwsmmio)
164{
165 const char *syscon_name = "microchip,sparx5-cpu-syscon";
166 struct device *dev = &pdev->dev;
167 struct dw_spi_mscc *dwsmscc;
168
169 if (!IS_ENABLED(CONFIG_SPI_MUX)) {
170 dev_err(dev, "This driver needs CONFIG_SPI_MUX\n");
171 return -EOPNOTSUPP;
172 }
173
174 dwsmscc = devm_kzalloc(dev, sizeof(*dwsmscc), GFP_KERNEL);
175 if (!dwsmscc)
176 return -ENOMEM;
177
178 dwsmscc->syscon =
179 syscon_regmap_lookup_by_compatible(syscon_name);
180 if (IS_ERR(dwsmscc->syscon)) {
181 dev_err(dev, "No syscon map %s\n", syscon_name);
182 return PTR_ERR(dwsmscc->syscon);
183 }
184
185 dwsmmio->dws.set_cs = dw_spi_sparx5_set_cs;
186 dwsmmio->priv = dwsmscc;
187
53a09635
LP
188 return 0;
189}
190
f2d70479
TS
191static int dw_spi_alpine_init(struct platform_device *pdev,
192 struct dw_spi_mmio *dwsmmio)
193{
cc760f31 194 dwsmmio->dws.caps = DW_SPI_CAP_CS_OVERRIDE;
f2d70479 195
c4eadee2
WAZ
196 return 0;
197}
198
725b0e3e
SS
199static int dw_spi_pssi_init(struct platform_device *pdev,
200 struct dw_spi_mmio *dwsmmio)
c4eadee2 201{
0fdad596
SS
202 dw_spi_dma_setup_generic(&dwsmmio->dws);
203
f2d70479
TS
204 return 0;
205}
206
725b0e3e
SS
207static int dw_spi_hssi_init(struct platform_device *pdev,
208 struct dw_spi_mmio *dwsmmio)
e539f435 209{
2b8a47e0 210 dwsmmio->dws.ip = DW_HSSI_ID;
e539f435 211
0fdad596
SS
212 dw_spi_dma_setup_generic(&dwsmmio->dws);
213
e539f435
WAZ
214 return 0;
215}
216
dc4e6d9f
NS
217static int dw_spi_intel_init(struct platform_device *pdev,
218 struct dw_spi_mmio *dwsmmio)
f4237791 219{
2b8a47e0 220 dwsmmio->dws.ip = DW_HSSI_ID;
f4237791
WAZ
221
222 return 0;
223}
224
b0dfd948
DLM
225static int dw_spi_canaan_k210_init(struct platform_device *pdev,
226 struct dw_spi_mmio *dwsmmio)
227{
228 /*
229 * The Canaan Kendryte K210 SoC DW apb_ssi v4 spi controller is
230 * documented to have a 32 word deep TX and RX FIFO, which
231 * spi_hw_init() detects. However, when the RX FIFO is filled up to
232 * 32 entries (RXFLR = 32), an RX FIFO overrun error occurs. Avoid this
233 * problem by force setting fifo_len to 31.
234 */
235 dwsmmio->dws.fifo_len = 31;
236
237 return 0;
238}
239
fd4a319b 240static int dw_spi_mmio_probe(struct platform_device *pdev)
f7b6fd6d 241{
c2c25cc3
AB
242 int (*init_func)(struct platform_device *pdev,
243 struct dw_spi_mmio *dwsmmio);
f7b6fd6d 244 struct dw_spi_mmio *dwsmmio;
77810d48 245 struct resource *mem;
f7b6fd6d 246 struct dw_spi *dws;
f7b6fd6d 247 int ret;
22dae17e 248 int num_cs;
f7b6fd6d 249
04f421e7
BS
250 dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
251 GFP_KERNEL);
252 if (!dwsmmio)
253 return -ENOMEM;
f7b6fd6d
JHD
254
255 dws = &dwsmmio->dws;
256
257 /* Get basic io resource and map it */
77810d48 258 dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
afb7f565 259 if (IS_ERR(dws->regs))
04f421e7 260 return PTR_ERR(dws->regs);
f7b6fd6d 261
77810d48
SS
262 dws->paddr = mem->start;
263
f7b6fd6d 264 dws->irq = platform_get_irq(pdev, 0);
6b8ac10e 265 if (dws->irq < 0)
04f421e7 266 return dws->irq; /* -ENXIO */
f7b6fd6d 267
04f421e7
BS
268 dwsmmio->clk = devm_clk_get(&pdev->dev, NULL);
269 if (IS_ERR(dwsmmio->clk))
270 return PTR_ERR(dwsmmio->clk);
020fe3fe 271 ret = clk_prepare_enable(dwsmmio->clk);
04f421e7
BS
272 if (ret)
273 return ret;
f7b6fd6d 274
560ee7e9
PE
275 /* Optional clock needed to access the registers */
276 dwsmmio->pclk = devm_clk_get_optional(&pdev->dev, "pclk");
3da9834d
AS
277 if (IS_ERR(dwsmmio->pclk)) {
278 ret = PTR_ERR(dwsmmio->pclk);
279 goto out_clk;
280 }
560ee7e9
PE
281 ret = clk_prepare_enable(dwsmmio->pclk);
282 if (ret)
283 goto out_clk;
284
7830c0ef
DN
285 /* find an optional reset controller */
286 dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
287 if (IS_ERR(dwsmmio->rstc)) {
288 ret = PTR_ERR(dwsmmio->rstc);
289 goto out_clk;
290 }
291 reset_control_deassert(dwsmmio->rstc);
292
2418991e 293 dws->bus_num = pdev->id;
22dae17e 294
f7b6fd6d
JHD
295 dws->max_freq = clk_get_rate(dwsmmio->clk);
296
9899995e 297 device_property_read_u32(&pdev->dev, "reg-io-width", &dws->reg_io_width);
c4fe57f7 298
22dae17e
ST
299 num_cs = 4;
300
9899995e 301 device_property_read_u32(&pdev->dev, "num-cs", &num_cs);
22dae17e
ST
302
303 dws->num_cs = num_cs;
304
c2c25cc3
AB
305 init_func = device_get_match_data(&pdev->dev);
306 if (init_func) {
307 ret = init_func(pdev, dwsmmio);
308 if (ret)
309 goto out;
310 }
311
b9fc2d20
JN
312 pm_runtime_enable(&pdev->dev);
313
04f421e7 314 ret = dw_spi_add_host(&pdev->dev, dws);
f7b6fd6d 315 if (ret)
04f421e7 316 goto out;
f7b6fd6d
JHD
317
318 platform_set_drvdata(pdev, dwsmmio);
319 return 0;
320
04f421e7 321out:
b9fc2d20 322 pm_runtime_disable(&pdev->dev);
560ee7e9
PE
323 clk_disable_unprepare(dwsmmio->pclk);
324out_clk:
020fe3fe 325 clk_disable_unprepare(dwsmmio->clk);
7830c0ef
DN
326 reset_control_assert(dwsmmio->rstc);
327
f7b6fd6d
JHD
328 return ret;
329}
330
f74abea2 331static void dw_spi_mmio_remove(struct platform_device *pdev)
f7b6fd6d
JHD
332{
333 struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
f7b6fd6d 334
f7b6fd6d 335 dw_spi_remove_host(&dwsmmio->dws);
b9fc2d20 336 pm_runtime_disable(&pdev->dev);
560ee7e9 337 clk_disable_unprepare(dwsmmio->pclk);
400c18e3 338 clk_disable_unprepare(dwsmmio->clk);
7830c0ef 339 reset_control_assert(dwsmmio->rstc);
f7b6fd6d
JHD
340}
341
22dae17e 342static const struct of_device_id dw_spi_mmio_of_match[] = {
725b0e3e 343 { .compatible = "snps,dw-apb-ssi", .data = dw_spi_pssi_init},
be17ee0d
AB
344 { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_ocelot_init},
345 { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init},
f2d70479 346 { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init},
725b0e3e
SS
347 { .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init},
348 { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init},
dc4e6d9f
NS
349 { .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init},
350 { .compatible = "intel,thunderbay-ssi", .data = dw_spi_intel_init},
53a09635 351 { .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
b0dfd948 352 { .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
22dae17e
ST
353 { /* end of table */}
354};
355MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
356
4dd227a5 357#ifdef CONFIG_ACPI
32215a6c 358static const struct acpi_device_id dw_spi_mmio_acpi_match[] = {
725b0e3e 359 {"HISI0173", (kernel_ulong_t)dw_spi_pssi_init},
32215a6c
JF
360 {},
361};
362MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
4dd227a5 363#endif
32215a6c 364
f7b6fd6d 365static struct platform_driver dw_spi_mmio_driver = {
940ab889 366 .probe = dw_spi_mmio_probe,
f74abea2 367 .remove_new = dw_spi_mmio_remove,
f7b6fd6d
JHD
368 .driver = {
369 .name = DRIVER_NAME,
22dae17e 370 .of_match_table = dw_spi_mmio_of_match,
32215a6c 371 .acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match),
f7b6fd6d
JHD
372 },
373};
940ab889 374module_platform_driver(dw_spi_mmio_driver);
f7b6fd6d
JHD
375
376MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>");
377MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core");
378MODULE_LICENSE("GPL v2");
a62bacba 379MODULE_IMPORT_NS(SPI_DW_CORE);