Blame view
drivers/spi/spi-dw-mmio.c
9.56 KB
75a6faf61 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
f7b6fd6d1 Memory-mapped dw_... |
2 |
/* |
ca632f556 spi: reorganize d... |
3 |
* Memory-mapped interface driver for DW SPI Core |
f7b6fd6d1 Memory-mapped dw_... |
4 5 |
* * Copyright (c) 2010, Octasic semiconductor. |
f7b6fd6d1 Memory-mapped dw_... |
6 7 8 |
*/ #include <linux/clk.h> |
50c01fc35 spi/dw_spi: don't... |
9 |
#include <linux/err.h> |
f7b6fd6d1 Memory-mapped dw_... |
10 |
#include <linux/platform_device.h> |
b9fc2d207 spi: dw: Move run... |
11 |
#include <linux/pm_runtime.h> |
5a0e3ad6a include cleanup: ... |
12 |
#include <linux/slab.h> |
f7b6fd6d1 Memory-mapped dw_... |
13 |
#include <linux/spi/spi.h> |
568a60eda spi/dw_spi: move ... |
14 |
#include <linux/scatterlist.h> |
c2c25cc39 spi: dw-mmio: add... |
15 |
#include <linux/mfd/syscon.h> |
d7614de42 spi: Add module.h... |
16 |
#include <linux/module.h> |
22dae17e7 spi: dw-mmio: add... |
17 |
#include <linux/of.h> |
22dae17e7 spi: dw-mmio: add... |
18 |
#include <linux/of_platform.h> |
32215a6c6 spi: dw-mmio: add... |
19 |
#include <linux/acpi.h> |
9899995e9 spi: dw-mmio: con... |
20 |
#include <linux/property.h> |
c2c25cc39 spi: dw-mmio: add... |
21 |
#include <linux/regmap.h> |
7830c0ef2 spi: dw: add rese... |
22 |
#include <linux/reset.h> |
568a60eda spi/dw_spi: move ... |
23 |
|
ca632f556 spi: reorganize d... |
24 |
#include "spi-dw.h" |
f7b6fd6d1 Memory-mapped dw_... |
25 26 27 28 |
#define DRIVER_NAME "dw_spi_mmio" struct dw_spi_mmio { |
0a4c1d7d4 spi/dw_spi: mmio ... |
29 30 |
struct dw_spi dws; struct clk *clk; |
560ee7e91 spi: dw: Add supp... |
31 |
struct clk *pclk; |
c2c25cc39 spi: dw-mmio: add... |
32 |
void *priv; |
7830c0ef2 spi: dw: add rese... |
33 |
struct reset_control *rstc; |
f7b6fd6d1 Memory-mapped dw_... |
34 |
}; |
c2c25cc39 spi: dw-mmio: add... |
35 |
#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 |
c2c25cc39 spi: dw-mmio: add... |
36 |
#define OCELOT_IF_SI_OWNER_OFFSET 4 |
be17ee0da spi: dw-mmio: add... |
37 |
#define JAGUAR2_IF_SI_OWNER_OFFSET 6 |
c1d8b0825 spi: dw-mmio: avo... |
38 |
#define MSCC_IF_SI_OWNER_MASK GENMASK(1, 0) |
c2c25cc39 spi: dw-mmio: add... |
39 40 41 42 43 44 45 |
#define MSCC_IF_SI_OWNER_SISL 0 #define MSCC_IF_SI_OWNER_SIBM 1 #define MSCC_IF_SI_OWNER_SIMC 2 #define MSCC_SPI_MST_SW_MODE 0x14 #define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) #define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) |
53a09635c spi: dw: Add Micr... |
46 47 |
#define SPARX5_FORCE_ENA 0xa4 #define SPARX5_FORCE_VAL 0xa8 |
c2c25cc39 spi: dw-mmio: add... |
48 49 |
struct dw_spi_mscc { struct regmap *syscon; |
53a09635c spi: dw: Add Micr... |
50 |
void __iomem *spi_mst; /* Not sparx5 */ |
c2c25cc39 spi: dw-mmio: add... |
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
}; /* * The Designware SPI controller (referred to as master in the documentation) * automatically deasserts chip select when the tx fifo is empty. The chip * selects then needs to be either driven as GPIOs or, for the first 4 using the * the SPI boot controller registers. the final chip select is an OR gate * between the Designware SPI controller and the SPI boot controller. */ static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_master_get_devdata(spi->master); struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws); struct dw_spi_mscc *dwsmscc = dwsmmio->priv; u32 cs = spi->chip_select; if (cs < 4) { u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE; if (!enable) sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs)); writel(sw_mode, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); } dw_spi_set_cs(spi, enable); } static int dw_spi_mscc_init(struct platform_device *pdev, |
be17ee0da spi: dw-mmio: add... |
80 81 |
struct dw_spi_mmio *dwsmmio, const char *cpu_syscon, u32 if_si_owner_offset) |
c2c25cc39 spi: dw-mmio: add... |
82 83 |
{ struct dw_spi_mscc *dwsmscc; |
c2c25cc39 spi: dw-mmio: add... |
84 85 86 87 |
dwsmscc = devm_kzalloc(&pdev->dev, sizeof(*dwsmscc), GFP_KERNEL); if (!dwsmscc) return -ENOMEM; |
5cc6fdccb spi: dw-mmio: use... |
88 |
dwsmscc->spi_mst = devm_platform_ioremap_resource(pdev, 1); |
c2c25cc39 spi: dw-mmio: add... |
89 90 91 92 93 |
if (IS_ERR(dwsmscc->spi_mst)) { dev_err(&pdev->dev, "SPI_MST region map failed "); return PTR_ERR(dwsmscc->spi_mst); } |
be17ee0da spi: dw-mmio: add... |
94 |
dwsmscc->syscon = syscon_regmap_lookup_by_compatible(cpu_syscon); |
c2c25cc39 spi: dw-mmio: add... |
95 96 97 98 99 100 101 102 |
if (IS_ERR(dwsmscc->syscon)) return PTR_ERR(dwsmscc->syscon); /* Deassert all CS */ writel(0, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); /* Select the owner of the SI interface */ regmap_update_bits(dwsmscc->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL, |
c1d8b0825 spi: dw-mmio: avo... |
103 |
MSCC_IF_SI_OWNER_MASK << if_si_owner_offset, |
be17ee0da spi: dw-mmio: add... |
104 |
MSCC_IF_SI_OWNER_SIMC << if_si_owner_offset); |
c2c25cc39 spi: dw-mmio: add... |
105 106 107 108 109 110 |
dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; dwsmmio->priv = dwsmscc; return 0; } |
be17ee0da spi: dw-mmio: add... |
111 112 113 114 115 116 117 118 119 120 121 122 123 |
static int dw_spi_mscc_ocelot_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { return dw_spi_mscc_init(pdev, dwsmmio, "mscc,ocelot-cpu-syscon", OCELOT_IF_SI_OWNER_OFFSET); } static int dw_spi_mscc_jaguar2_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { return dw_spi_mscc_init(pdev, dwsmmio, "mscc,jaguar2-cpu-syscon", JAGUAR2_IF_SI_OWNER_OFFSET); } |
53a09635c spi: dw: Add Micr... |
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
/* * The Designware SPI controller (referred to as master in the * documentation) automatically deasserts chip select when the tx fifo * is empty. The chip selects then needs to be driven by a CS override * register. enable is an active low signal. */ static void dw_spi_sparx5_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_master_get_devdata(spi->master); struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws); struct dw_spi_mscc *dwsmscc = dwsmmio->priv; u8 cs = spi->chip_select; if (!enable) { /* CS override drive enable */ regmap_write(dwsmscc->syscon, SPARX5_FORCE_ENA, 1); /* Now set CSx enabled */ regmap_write(dwsmscc->syscon, SPARX5_FORCE_VAL, ~BIT(cs)); /* Allow settle */ usleep_range(1, 5); } else { /* CS value */ regmap_write(dwsmscc->syscon, SPARX5_FORCE_VAL, ~0); /* Allow settle */ usleep_range(1, 5); /* CS override drive disable */ regmap_write(dwsmscc->syscon, SPARX5_FORCE_ENA, 0); } dw_spi_set_cs(spi, enable); } static int dw_spi_mscc_sparx5_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { const char *syscon_name = "microchip,sparx5-cpu-syscon"; struct device *dev = &pdev->dev; struct dw_spi_mscc *dwsmscc; if (!IS_ENABLED(CONFIG_SPI_MUX)) { dev_err(dev, "This driver needs CONFIG_SPI_MUX "); return -EOPNOTSUPP; } dwsmscc = devm_kzalloc(dev, sizeof(*dwsmscc), GFP_KERNEL); if (!dwsmscc) return -ENOMEM; dwsmscc->syscon = syscon_regmap_lookup_by_compatible(syscon_name); if (IS_ERR(dwsmscc->syscon)) { dev_err(dev, "No syscon map %s ", syscon_name); return PTR_ERR(dwsmscc->syscon); } dwsmmio->dws.set_cs = dw_spi_sparx5_set_cs; dwsmmio->priv = dwsmscc; |
53a09635c spi: dw: Add Micr... |
183 184 |
return 0; } |
f2d704794 dw: spi: add supp... |
185 186 187 |
static int dw_spi_alpine_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { |
cc760f314 spi: dw: Convert ... |
188 |
dwsmmio->dws.caps = DW_SPI_CAP_CS_OVERRIDE; |
f2d704794 dw: spi: add supp... |
189 |
|
c4eadee21 spi: dw: Add upda... |
190 191 192 193 194 195 |
return 0; } static int dw_spi_dw_apb_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { |
0fdad596d spi: dw: Add DMA ... |
196 |
dw_spi_dma_setup_generic(&dwsmmio->dws); |
f2d704794 dw: spi: add supp... |
197 198 |
return 0; } |
e539f435c spi: dw: Add supp... |
199 200 201 |
static int dw_spi_dwc_ssi_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { |
d6bbd1193 spi: dw: Add DWC ... |
202 |
dwsmmio->dws.caps = DW_SPI_CAP_DWC_SSI; |
e539f435c spi: dw: Add supp... |
203 |
|
0fdad596d spi: dw: Add DMA ... |
204 |
dw_spi_dma_setup_generic(&dwsmmio->dws); |
e539f435c spi: dw: Add supp... |
205 206 |
return 0; } |
f42377916 spi: dw: Add supp... |
207 208 209 |
static int dw_spi_keembay_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { |
d6bbd1193 spi: dw: Add DWC ... |
210 |
dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST | DW_SPI_CAP_DWC_SSI; |
f42377916 spi: dw: Add supp... |
211 212 213 |
return 0; } |
fd4a319bc spi: Remove HOTPL... |
214 |
static int dw_spi_mmio_probe(struct platform_device *pdev) |
f7b6fd6d1 Memory-mapped dw_... |
215 |
{ |
c2c25cc39 spi: dw-mmio: add... |
216 217 |
int (*init_func)(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio); |
f7b6fd6d1 Memory-mapped dw_... |
218 |
struct dw_spi_mmio *dwsmmio; |
77810d484 spi: dw: Initiali... |
219 |
struct resource *mem; |
f7b6fd6d1 Memory-mapped dw_... |
220 |
struct dw_spi *dws; |
f7b6fd6d1 Memory-mapped dw_... |
221 |
int ret; |
22dae17e7 spi: dw-mmio: add... |
222 |
int num_cs; |
f7b6fd6d1 Memory-mapped dw_... |
223 |
|
04f421e7b spi: dw: use mana... |
224 225 226 227 |
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio), GFP_KERNEL); if (!dwsmmio) return -ENOMEM; |
f7b6fd6d1 Memory-mapped dw_... |
228 229 230 231 |
dws = &dwsmmio->dws; /* Get basic io resource and map it */ |
77810d484 spi: dw: Initiali... |
232 |
dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); |
afb7f5652 spi: dw: Drop dup... |
233 |
if (IS_ERR(dws->regs)) |
04f421e7b spi: dw: use mana... |
234 |
return PTR_ERR(dws->regs); |
f7b6fd6d1 Memory-mapped dw_... |
235 |
|
77810d484 spi: dw: Initiali... |
236 |
dws->paddr = mem->start; |
f7b6fd6d1 Memory-mapped dw_... |
237 |
dws->irq = platform_get_irq(pdev, 0); |
6b8ac10e0 spi: Remove dev_e... |
238 |
if (dws->irq < 0) |
04f421e7b spi: dw: use mana... |
239 |
return dws->irq; /* -ENXIO */ |
f7b6fd6d1 Memory-mapped dw_... |
240 |
|
04f421e7b spi: dw: use mana... |
241 242 243 |
dwsmmio->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dwsmmio->clk)) return PTR_ERR(dwsmmio->clk); |
020fe3fe1 spi: dw-mmio: pre... |
244 |
ret = clk_prepare_enable(dwsmmio->clk); |
04f421e7b spi: dw: use mana... |
245 246 |
if (ret) return ret; |
f7b6fd6d1 Memory-mapped dw_... |
247 |
|
560ee7e91 spi: dw: Add supp... |
248 249 |
/* Optional clock needed to access the registers */ dwsmmio->pclk = devm_clk_get_optional(&pdev->dev, "pclk"); |
3da9834d9 spi: dw-mmio: Clo... |
250 251 252 253 |
if (IS_ERR(dwsmmio->pclk)) { ret = PTR_ERR(dwsmmio->pclk); goto out_clk; } |
560ee7e91 spi: dw: Add supp... |
254 255 256 |
ret = clk_prepare_enable(dwsmmio->pclk); if (ret) goto out_clk; |
7830c0ef2 spi: dw: add rese... |
257 258 259 260 261 262 263 |
/* find an optional reset controller */ dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); if (IS_ERR(dwsmmio->rstc)) { ret = PTR_ERR(dwsmmio->rstc); goto out_clk; } reset_control_deassert(dwsmmio->rstc); |
2418991ef spi: dw-mmio: all... |
264 |
dws->bus_num = pdev->id; |
22dae17e7 spi: dw-mmio: add... |
265 |
|
f7b6fd6d1 Memory-mapped dw_... |
266 |
dws->max_freq = clk_get_rate(dwsmmio->clk); |
9899995e9 spi: dw-mmio: con... |
267 |
device_property_read_u32(&pdev->dev, "reg-io-width", &dws->reg_io_width); |
c4fe57f76 spi: dw: Allow in... |
268 |
|
22dae17e7 spi: dw-mmio: add... |
269 |
num_cs = 4; |
9899995e9 spi: dw-mmio: con... |
270 |
device_property_read_u32(&pdev->dev, "num-cs", &num_cs); |
22dae17e7 spi: dw-mmio: add... |
271 272 |
dws->num_cs = num_cs; |
c2c25cc39 spi: dw-mmio: add... |
273 274 275 276 277 278 |
init_func = device_get_match_data(&pdev->dev); if (init_func) { ret = init_func(pdev, dwsmmio); if (ret) goto out; } |
b9fc2d207 spi: dw: Move run... |
279 |
pm_runtime_enable(&pdev->dev); |
04f421e7b spi: dw: use mana... |
280 |
ret = dw_spi_add_host(&pdev->dev, dws); |
f7b6fd6d1 Memory-mapped dw_... |
281 |
if (ret) |
04f421e7b spi: dw: use mana... |
282 |
goto out; |
f7b6fd6d1 Memory-mapped dw_... |
283 284 285 |
platform_set_drvdata(pdev, dwsmmio); return 0; |
04f421e7b spi: dw: use mana... |
286 |
out: |
b9fc2d207 spi: dw: Move run... |
287 |
pm_runtime_disable(&pdev->dev); |
560ee7e91 spi: dw: Add supp... |
288 289 |
clk_disable_unprepare(dwsmmio->pclk); out_clk: |
020fe3fe1 spi: dw-mmio: pre... |
290 |
clk_disable_unprepare(dwsmmio->clk); |
7830c0ef2 spi: dw: add rese... |
291 |
reset_control_assert(dwsmmio->rstc); |
f7b6fd6d1 Memory-mapped dw_... |
292 293 |
return ret; } |
fd4a319bc spi: Remove HOTPL... |
294 |
static int dw_spi_mmio_remove(struct platform_device *pdev) |
f7b6fd6d1 Memory-mapped dw_... |
295 296 |
{ struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); |
f7b6fd6d1 Memory-mapped dw_... |
297 |
|
f7b6fd6d1 Memory-mapped dw_... |
298 |
dw_spi_remove_host(&dwsmmio->dws); |
b9fc2d207 spi: dw: Move run... |
299 |
pm_runtime_disable(&pdev->dev); |
560ee7e91 spi: dw: Add supp... |
300 |
clk_disable_unprepare(dwsmmio->pclk); |
400c18e3d spi: dw: Disable ... |
301 |
clk_disable_unprepare(dwsmmio->clk); |
7830c0ef2 spi: dw: add rese... |
302 |
reset_control_assert(dwsmmio->rstc); |
f7b6fd6d1 Memory-mapped dw_... |
303 |
|
f7b6fd6d1 Memory-mapped dw_... |
304 305 |
return 0; } |
22dae17e7 spi: dw-mmio: add... |
306 |
static const struct of_device_id dw_spi_mmio_of_match[] = { |
c4eadee21 spi: dw: Add upda... |
307 |
{ .compatible = "snps,dw-apb-ssi", .data = dw_spi_dw_apb_init}, |
be17ee0da spi: dw-mmio: add... |
308 309 |
{ .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_ocelot_init}, { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init}, |
f2d704794 dw: spi: add supp... |
310 |
{ .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, |
c4eadee21 spi: dw: Add upda... |
311 |
{ .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init}, |
e539f435c spi: dw: Add supp... |
312 |
{ .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init}, |
f42377916 spi: dw: Add supp... |
313 |
{ .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init}, |
53a09635c spi: dw: Add Micr... |
314 |
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init}, |
22dae17e7 spi: dw-mmio: add... |
315 316 317 |
{ /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); |
4dd227a55 spi: dw-mmio: Do ... |
318 |
#ifdef CONFIG_ACPI |
32215a6c6 spi: dw-mmio: add... |
319 |
static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { |
c4eadee21 spi: dw: Add upda... |
320 |
{"HISI0173", (kernel_ulong_t)dw_spi_dw_apb_init}, |
32215a6c6 spi: dw-mmio: add... |
321 322 323 |
{}, }; MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); |
4dd227a55 spi: dw-mmio: Do ... |
324 |
#endif |
32215a6c6 spi: dw-mmio: add... |
325 |
|
f7b6fd6d1 Memory-mapped dw_... |
326 |
static struct platform_driver dw_spi_mmio_driver = { |
940ab8896 drivercore: Add h... |
327 |
.probe = dw_spi_mmio_probe, |
fd4a319bc spi: Remove HOTPL... |
328 |
.remove = dw_spi_mmio_remove, |
f7b6fd6d1 Memory-mapped dw_... |
329 330 |
.driver = { .name = DRIVER_NAME, |
22dae17e7 spi: dw-mmio: add... |
331 |
.of_match_table = dw_spi_mmio_of_match, |
32215a6c6 spi: dw-mmio: add... |
332 |
.acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match), |
f7b6fd6d1 Memory-mapped dw_... |
333 334 |
}, }; |
940ab8896 drivercore: Add h... |
335 |
module_platform_driver(dw_spi_mmio_driver); |
f7b6fd6d1 Memory-mapped dw_... |
336 337 338 339 |
MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>"); MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core"); MODULE_LICENSE("GPL v2"); |