.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Altera SPI driver |
---|
3 | 4 | * |
---|
.. | .. |
---|
7 | 8 | * Copyright (c) 2006 Ben Dooks |
---|
8 | 9 | * Copyright (c) 2006 Simtec Electronics |
---|
9 | 10 | * Ben Dooks <ben@simtec.co.uk> |
---|
10 | | - * |
---|
11 | | - * This program is free software; you can redistribute it and/or modify |
---|
12 | | - * it under the terms of the GNU General Public License version 2 as |
---|
13 | | - * published by the Free Software Foundation. |
---|
14 | 11 | */ |
---|
15 | 12 | |
---|
16 | 13 | #include <linux/interrupt.h> |
---|
17 | 14 | #include <linux/errno.h> |
---|
18 | 15 | #include <linux/module.h> |
---|
19 | 16 | #include <linux/platform_device.h> |
---|
| 17 | +#include <linux/spi/altera.h> |
---|
20 | 18 | #include <linux/spi/spi.h> |
---|
21 | 19 | #include <linux/io.h> |
---|
22 | 20 | #include <linux/of.h> |
---|
.. | .. |
---|
43 | 41 | #define ALTERA_SPI_CONTROL_IE_MSK 0x100 |
---|
44 | 42 | #define ALTERA_SPI_CONTROL_SSO_MSK 0x400 |
---|
45 | 43 | |
---|
| 44 | +#define ALTERA_SPI_MAX_CS 32 |
---|
| 45 | + |
---|
| 46 | +enum altera_spi_type { |
---|
| 47 | + ALTERA_SPI_TYPE_UNKNOWN, |
---|
| 48 | + ALTERA_SPI_TYPE_SUBDEV, |
---|
| 49 | +}; |
---|
| 50 | + |
---|
46 | 51 | struct altera_spi { |
---|
47 | | - void __iomem *base; |
---|
48 | 52 | int irq; |
---|
49 | 53 | int len; |
---|
50 | 54 | int count; |
---|
51 | 55 | int bytes_per_word; |
---|
52 | | - unsigned long imr; |
---|
| 56 | + u32 imr; |
---|
53 | 57 | |
---|
54 | 58 | /* data buffers */ |
---|
55 | 59 | const unsigned char *tx; |
---|
56 | 60 | unsigned char *rx; |
---|
| 61 | + |
---|
| 62 | + struct regmap *regmap; |
---|
| 63 | + u32 regoff; |
---|
| 64 | + struct device *dev; |
---|
57 | 65 | }; |
---|
| 66 | + |
---|
| 67 | +static const struct regmap_config spi_altera_config = { |
---|
| 68 | + .reg_bits = 32, |
---|
| 69 | + .reg_stride = 4, |
---|
| 70 | + .val_bits = 32, |
---|
| 71 | + .fast_io = true, |
---|
| 72 | +}; |
---|
| 73 | + |
---|
| 74 | +static int altr_spi_writel(struct altera_spi *hw, unsigned int reg, |
---|
| 75 | + unsigned int val) |
---|
| 76 | +{ |
---|
| 77 | + int ret; |
---|
| 78 | + |
---|
| 79 | + ret = regmap_write(hw->regmap, hw->regoff + reg, val); |
---|
| 80 | + if (ret) |
---|
| 81 | + dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n", |
---|
| 82 | + reg, val, ret); |
---|
| 83 | + |
---|
| 84 | + return ret; |
---|
| 85 | +} |
---|
| 86 | + |
---|
| 87 | +static int altr_spi_readl(struct altera_spi *hw, unsigned int reg, |
---|
| 88 | + unsigned int *val) |
---|
| 89 | +{ |
---|
| 90 | + int ret; |
---|
| 91 | + |
---|
| 92 | + ret = regmap_read(hw->regmap, hw->regoff + reg, val); |
---|
| 93 | + if (ret) |
---|
| 94 | + dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret); |
---|
| 95 | + |
---|
| 96 | + return ret; |
---|
| 97 | +} |
---|
58 | 98 | |
---|
59 | 99 | static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) |
---|
60 | 100 | { |
---|
.. | .. |
---|
67 | 107 | |
---|
68 | 108 | if (is_high) { |
---|
69 | 109 | hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; |
---|
70 | | - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); |
---|
71 | | - writel(0, hw->base + ALTERA_SPI_SLAVE_SEL); |
---|
| 110 | + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
---|
| 111 | + altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0); |
---|
72 | 112 | } else { |
---|
73 | | - writel(BIT(spi->chip_select), hw->base + ALTERA_SPI_SLAVE_SEL); |
---|
| 113 | + altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, |
---|
| 114 | + BIT(spi->chip_select)); |
---|
74 | 115 | hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; |
---|
75 | | - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); |
---|
| 116 | + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
---|
76 | 117 | } |
---|
77 | 118 | } |
---|
78 | 119 | |
---|
.. | .. |
---|
89 | 130 | txd = (hw->tx[hw->count * 2] |
---|
90 | 131 | | (hw->tx[hw->count * 2 + 1] << 8)); |
---|
91 | 132 | break; |
---|
| 133 | + case 4: |
---|
| 134 | + txd = (hw->tx[hw->count * 4] |
---|
| 135 | + | (hw->tx[hw->count * 4 + 1] << 8) |
---|
| 136 | + | (hw->tx[hw->count * 4 + 2] << 16) |
---|
| 137 | + | (hw->tx[hw->count * 4 + 3] << 24)); |
---|
| 138 | + break; |
---|
| 139 | + |
---|
92 | 140 | } |
---|
93 | 141 | } |
---|
94 | 142 | |
---|
95 | | - writel(txd, hw->base + ALTERA_SPI_TXDATA); |
---|
| 143 | + altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd); |
---|
96 | 144 | } |
---|
97 | 145 | |
---|
98 | 146 | static void altera_spi_rx_word(struct altera_spi *hw) |
---|
99 | 147 | { |
---|
100 | 148 | unsigned int rxd; |
---|
101 | 149 | |
---|
102 | | - rxd = readl(hw->base + ALTERA_SPI_RXDATA); |
---|
| 150 | + altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd); |
---|
103 | 151 | if (hw->rx) { |
---|
104 | 152 | switch (hw->bytes_per_word) { |
---|
105 | 153 | case 1: |
---|
.. | .. |
---|
109 | 157 | hw->rx[hw->count * 2] = rxd; |
---|
110 | 158 | hw->rx[hw->count * 2 + 1] = rxd >> 8; |
---|
111 | 159 | break; |
---|
| 160 | + case 4: |
---|
| 161 | + hw->rx[hw->count * 4] = rxd; |
---|
| 162 | + hw->rx[hw->count * 4 + 1] = rxd >> 8; |
---|
| 163 | + hw->rx[hw->count * 4 + 2] = rxd >> 16; |
---|
| 164 | + hw->rx[hw->count * 4 + 3] = rxd >> 24; |
---|
| 165 | + break; |
---|
| 166 | + |
---|
112 | 167 | } |
---|
113 | 168 | } |
---|
114 | 169 | |
---|
.. | .. |
---|
119 | 174 | struct spi_device *spi, struct spi_transfer *t) |
---|
120 | 175 | { |
---|
121 | 176 | struct altera_spi *hw = spi_master_get_devdata(master); |
---|
| 177 | + u32 val; |
---|
122 | 178 | |
---|
123 | 179 | hw->tx = t->tx_buf; |
---|
124 | 180 | hw->rx = t->rx_buf; |
---|
.. | .. |
---|
129 | 185 | if (hw->irq >= 0) { |
---|
130 | 186 | /* enable receive interrupt */ |
---|
131 | 187 | hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; |
---|
132 | | - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); |
---|
| 188 | + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
---|
133 | 189 | |
---|
134 | 190 | /* send the first byte */ |
---|
135 | 191 | altera_spi_tx_word(hw); |
---|
136 | | - } else { |
---|
137 | | - while (hw->count < hw->len) { |
---|
138 | | - altera_spi_tx_word(hw); |
---|
139 | 192 | |
---|
140 | | - while (!(readl(hw->base + ALTERA_SPI_STATUS) & |
---|
141 | | - ALTERA_SPI_STATUS_RRDY_MSK)) |
---|
142 | | - cpu_relax(); |
---|
143 | | - |
---|
144 | | - altera_spi_rx_word(hw); |
---|
145 | | - } |
---|
146 | | - spi_finalize_current_transfer(master); |
---|
| 193 | + return 1; |
---|
147 | 194 | } |
---|
148 | 195 | |
---|
149 | | - return t->len; |
---|
| 196 | + while (hw->count < hw->len) { |
---|
| 197 | + altera_spi_tx_word(hw); |
---|
| 198 | + |
---|
| 199 | + for (;;) { |
---|
| 200 | + altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); |
---|
| 201 | + if (val & ALTERA_SPI_STATUS_RRDY_MSK) |
---|
| 202 | + break; |
---|
| 203 | + |
---|
| 204 | + cpu_relax(); |
---|
| 205 | + } |
---|
| 206 | + |
---|
| 207 | + altera_spi_rx_word(hw); |
---|
| 208 | + } |
---|
| 209 | + spi_finalize_current_transfer(master); |
---|
| 210 | + |
---|
| 211 | + return 0; |
---|
150 | 212 | } |
---|
151 | 213 | |
---|
152 | 214 | static irqreturn_t altera_spi_irq(int irq, void *dev) |
---|
.. | .. |
---|
161 | 223 | } else { |
---|
162 | 224 | /* disable receive interrupt */ |
---|
163 | 225 | hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; |
---|
164 | | - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); |
---|
| 226 | + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
---|
165 | 227 | |
---|
166 | 228 | spi_finalize_current_transfer(master); |
---|
167 | 229 | } |
---|
.. | .. |
---|
171 | 233 | |
---|
172 | 234 | static int altera_spi_probe(struct platform_device *pdev) |
---|
173 | 235 | { |
---|
| 236 | + const struct platform_device_id *platid = platform_get_device_id(pdev); |
---|
| 237 | + struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev); |
---|
| 238 | + enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; |
---|
174 | 239 | struct altera_spi *hw; |
---|
175 | 240 | struct spi_master *master; |
---|
176 | | - struct resource *res; |
---|
177 | 241 | int err = -ENODEV; |
---|
| 242 | + u32 val; |
---|
| 243 | + u16 i; |
---|
178 | 244 | |
---|
179 | 245 | master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); |
---|
180 | 246 | if (!master) |
---|
.. | .. |
---|
182 | 248 | |
---|
183 | 249 | /* setup the master state. */ |
---|
184 | 250 | master->bus_num = pdev->id; |
---|
185 | | - master->num_chipselect = 16; |
---|
186 | | - master->mode_bits = SPI_CS_HIGH; |
---|
187 | | - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); |
---|
| 251 | + |
---|
| 252 | + if (pdata) { |
---|
| 253 | + if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) { |
---|
| 254 | + dev_err(&pdev->dev, |
---|
| 255 | + "Invalid number of chipselect: %hu\n", |
---|
| 256 | + pdata->num_chipselect); |
---|
| 257 | + err = -EINVAL; |
---|
| 258 | + goto exit; |
---|
| 259 | + } |
---|
| 260 | + |
---|
| 261 | + master->num_chipselect = pdata->num_chipselect; |
---|
| 262 | + master->mode_bits = pdata->mode_bits; |
---|
| 263 | + master->bits_per_word_mask = pdata->bits_per_word_mask; |
---|
| 264 | + } else { |
---|
| 265 | + master->num_chipselect = 16; |
---|
| 266 | + master->mode_bits = SPI_CS_HIGH; |
---|
| 267 | + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); |
---|
| 268 | + } |
---|
| 269 | + |
---|
188 | 270 | master->dev.of_node = pdev->dev.of_node; |
---|
189 | 271 | master->transfer_one = altera_spi_txrx; |
---|
190 | 272 | master->set_cs = altera_spi_set_cs; |
---|
191 | 273 | |
---|
192 | 274 | hw = spi_master_get_devdata(master); |
---|
| 275 | + hw->dev = &pdev->dev; |
---|
| 276 | + |
---|
| 277 | + if (platid) |
---|
| 278 | + type = platid->driver_data; |
---|
193 | 279 | |
---|
194 | 280 | /* find and map our resources */ |
---|
195 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
196 | | - hw->base = devm_ioremap_resource(&pdev->dev, res); |
---|
197 | | - if (IS_ERR(hw->base)) { |
---|
198 | | - err = PTR_ERR(hw->base); |
---|
199 | | - goto exit; |
---|
| 281 | + if (type == ALTERA_SPI_TYPE_SUBDEV) { |
---|
| 282 | + struct resource *regoff; |
---|
| 283 | + |
---|
| 284 | + hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); |
---|
| 285 | + if (!hw->regmap) { |
---|
| 286 | + dev_err(&pdev->dev, "get regmap failed\n"); |
---|
| 287 | + goto exit; |
---|
| 288 | + } |
---|
| 289 | + |
---|
| 290 | + regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); |
---|
| 291 | + if (regoff) |
---|
| 292 | + hw->regoff = regoff->start; |
---|
| 293 | + } else { |
---|
| 294 | + void __iomem *res; |
---|
| 295 | + |
---|
| 296 | + res = devm_platform_ioremap_resource(pdev, 0); |
---|
| 297 | + if (IS_ERR(res)) { |
---|
| 298 | + err = PTR_ERR(res); |
---|
| 299 | + goto exit; |
---|
| 300 | + } |
---|
| 301 | + |
---|
| 302 | + hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, |
---|
| 303 | + &spi_altera_config); |
---|
| 304 | + if (IS_ERR(hw->regmap)) { |
---|
| 305 | + dev_err(&pdev->dev, "regmap mmio init failed\n"); |
---|
| 306 | + err = PTR_ERR(hw->regmap); |
---|
| 307 | + goto exit; |
---|
| 308 | + } |
---|
200 | 309 | } |
---|
| 310 | + |
---|
201 | 311 | /* program defaults into the registers */ |
---|
202 | 312 | hw->imr = 0; /* disable spi interrupts */ |
---|
203 | | - writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); |
---|
204 | | - writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */ |
---|
205 | | - if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK) |
---|
206 | | - readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */ |
---|
| 313 | + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
---|
| 314 | + altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */ |
---|
| 315 | + altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); |
---|
| 316 | + if (val & ALTERA_SPI_STATUS_RRDY_MSK) |
---|
| 317 | + altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */ |
---|
207 | 318 | /* irq is optional */ |
---|
208 | 319 | hw->irq = platform_get_irq(pdev, 0); |
---|
209 | 320 | if (hw->irq >= 0) { |
---|
.. | .. |
---|
216 | 327 | err = devm_spi_register_master(&pdev->dev, master); |
---|
217 | 328 | if (err) |
---|
218 | 329 | goto exit; |
---|
219 | | - dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); |
---|
| 330 | + |
---|
| 331 | + if (pdata) { |
---|
| 332 | + for (i = 0; i < pdata->num_devices; i++) { |
---|
| 333 | + if (!spi_new_device(master, pdata->devices + i)) |
---|
| 334 | + dev_warn(&pdev->dev, |
---|
| 335 | + "unable to create SPI device: %s\n", |
---|
| 336 | + pdata->devices[i].modalias); |
---|
| 337 | + } |
---|
| 338 | + } |
---|
| 339 | + |
---|
| 340 | + dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq); |
---|
220 | 341 | |
---|
221 | 342 | return 0; |
---|
222 | 343 | exit: |
---|
.. | .. |
---|
233 | 354 | MODULE_DEVICE_TABLE(of, altera_spi_match); |
---|
234 | 355 | #endif /* CONFIG_OF */ |
---|
235 | 356 | |
---|
| 357 | +static const struct platform_device_id altera_spi_ids[] = { |
---|
| 358 | + { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, |
---|
| 359 | + { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, |
---|
| 360 | + { } |
---|
| 361 | +}; |
---|
| 362 | +MODULE_DEVICE_TABLE(platform, altera_spi_ids); |
---|
| 363 | + |
---|
236 | 364 | static struct platform_driver altera_spi_driver = { |
---|
237 | 365 | .probe = altera_spi_probe, |
---|
238 | 366 | .driver = { |
---|
.. | .. |
---|
240 | 368 | .pm = NULL, |
---|
241 | 369 | .of_match_table = of_match_ptr(altera_spi_match), |
---|
242 | 370 | }, |
---|
| 371 | + .id_table = altera_spi_ids, |
---|
243 | 372 | }; |
---|
244 | 373 | module_platform_driver(altera_spi_driver); |
---|
245 | 374 | |
---|