Blame view
drivers/spi/spi-altera.c
8.51 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
0b782531c spi: New driver f... |
2 3 4 5 6 7 8 9 10 |
/* * Altera SPI driver * * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> * * Based on spi_s3c24xx.c, which is: * Copyright (c) 2006 Ben Dooks * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> |
0b782531c spi: New driver f... |
11 |
*/ |
0b782531c spi: New driver f... |
12 13 |
#include <linux/interrupt.h> #include <linux/errno.h> |
d7614de42 spi: Add module.h... |
14 |
#include <linux/module.h> |
0b782531c spi: New driver f... |
15 |
#include <linux/platform_device.h> |
8e04187c1 spi: altera: add ... |
16 |
#include <linux/spi/altera.h> |
0b782531c spi: New driver f... |
17 |
#include <linux/spi/spi.h> |
0b782531c spi: New driver f... |
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <linux/io.h> #include <linux/of.h> #define DRV_NAME "spi_altera" #define ALTERA_SPI_RXDATA 0 #define ALTERA_SPI_TXDATA 4 #define ALTERA_SPI_STATUS 8 #define ALTERA_SPI_CONTROL 12 #define ALTERA_SPI_SLAVE_SEL 20 #define ALTERA_SPI_STATUS_ROE_MSK 0x8 #define ALTERA_SPI_STATUS_TOE_MSK 0x10 #define ALTERA_SPI_STATUS_TMT_MSK 0x20 #define ALTERA_SPI_STATUS_TRDY_MSK 0x40 #define ALTERA_SPI_STATUS_RRDY_MSK 0x80 #define ALTERA_SPI_STATUS_E_MSK 0x100 #define ALTERA_SPI_CONTROL_IROE_MSK 0x8 #define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 #define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 #define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 #define ALTERA_SPI_CONTROL_IE_MSK 0x100 #define ALTERA_SPI_CONTROL_SSO_MSK 0x400 |
8e04187c1 spi: altera: add ... |
42 |
#define ALTERA_SPI_MAX_CS 32 |
3820061d3 spi: altera: supp... |
43 44 45 46 |
enum altera_spi_type { ALTERA_SPI_TYPE_UNKNOWN, ALTERA_SPI_TYPE_SUBDEV, }; |
0b782531c spi: New driver f... |
47 |
struct altera_spi { |
0b782531c spi: New driver f... |
48 49 50 51 |
int irq; int len; int count; int bytes_per_word; |
d9dd0fb0e spi: altera: fix ... |
52 |
u32 imr; |
0b782531c spi: New driver f... |
53 54 55 56 |
/* data buffers */ const unsigned char *tx; unsigned char *rx; |
3c6519736 spi: altera: use ... |
57 58 |
struct regmap *regmap; |
3820061d3 spi: altera: supp... |
59 |
u32 regoff; |
3c6519736 spi: altera: use ... |
60 61 62 63 64 65 66 67 |
struct device *dev; }; static const struct regmap_config spi_altera_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .fast_io = true, |
0b782531c spi: New driver f... |
68 |
}; |
3c6519736 spi: altera: use ... |
69 70 71 72 |
static int altr_spi_writel(struct altera_spi *hw, unsigned int reg, unsigned int val) { int ret; |
3820061d3 spi: altera: supp... |
73 |
ret = regmap_write(hw->regmap, hw->regoff + reg, val); |
3c6519736 spi: altera: use ... |
74 75 76 77 78 79 80 81 82 83 84 85 |
if (ret) dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d ", reg, val, ret); return ret; } static int altr_spi_readl(struct altera_spi *hw, unsigned int reg, unsigned int *val) { int ret; |
3820061d3 spi: altera: supp... |
86 |
ret = regmap_read(hw->regmap, hw->regoff + reg, val); |
3c6519736 spi: altera: use ... |
87 88 89 90 91 92 |
if (ret) dev_err(hw->dev, "fail to read reg 0x%x: %d ", reg, ret); return ret; } |
0b782531c spi: New driver f... |
93 94 95 96 |
static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) { return spi_master_get_devdata(sdev->master); } |
e19b63cd3 spi: altera: Swit... |
97 |
static void altera_spi_set_cs(struct spi_device *spi, bool is_high) |
0b782531c spi: New driver f... |
98 99 |
{ struct altera_spi *hw = altera_spi_to_hw(spi); |
e19b63cd3 spi: altera: Swit... |
100 101 |
if (is_high) { hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; |
3c6519736 spi: altera: use ... |
102 103 |
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0); |
0b782531c spi: New driver f... |
104 |
} else { |
3c6519736 spi: altera: use ... |
105 106 |
altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, BIT(spi->chip_select)); |
e19b63cd3 spi: altera: Swit... |
107 |
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; |
3c6519736 spi: altera: use ... |
108 |
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
0b782531c spi: New driver f... |
109 110 |
} } |
b64836a57 spi: altera: Cons... |
111 |
static void altera_spi_tx_word(struct altera_spi *hw) |
0b782531c spi: New driver f... |
112 |
{ |
b64836a57 spi: altera: Cons... |
113 |
unsigned int txd = 0; |
0b782531c spi: New driver f... |
114 115 116 |
if (hw->tx) { switch (hw->bytes_per_word) { case 1: |
b64836a57 spi: altera: Cons... |
117 118 |
txd = hw->tx[hw->count]; break; |
0b782531c spi: New driver f... |
119 |
case 2: |
b64836a57 spi: altera: Cons... |
120 121 122 |
txd = (hw->tx[hw->count * 2] | (hw->tx[hw->count * 2 + 1] << 8)); break; |
3011d3147 spi: altera: add ... |
123 124 125 126 127 128 |
case 4: txd = (hw->tx[hw->count * 4] | (hw->tx[hw->count * 4 + 1] << 8) | (hw->tx[hw->count * 4 + 2] << 16) | (hw->tx[hw->count * 4 + 3] << 24)); break; |
0b782531c spi: New driver f... |
129 130 |
} } |
b64836a57 spi: altera: Cons... |
131 |
|
3c6519736 spi: altera: use ... |
132 |
altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd); |
b64836a57 spi: altera: Cons... |
133 134 135 136 137 |
} static void altera_spi_rx_word(struct altera_spi *hw) { unsigned int rxd; |
3c6519736 spi: altera: use ... |
138 |
altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd); |
b64836a57 spi: altera: Cons... |
139 140 141 142 143 144 145 146 147 |
if (hw->rx) { switch (hw->bytes_per_word) { case 1: hw->rx[hw->count] = rxd; break; case 2: hw->rx[hw->count * 2] = rxd; hw->rx[hw->count * 2 + 1] = rxd >> 8; break; |
3011d3147 spi: altera: add ... |
148 149 150 151 152 153 |
case 4: hw->rx[hw->count * 4] = rxd; hw->rx[hw->count * 4 + 1] = rxd >> 8; hw->rx[hw->count * 4 + 2] = rxd >> 16; hw->rx[hw->count * 4 + 3] = rxd >> 24; break; |
b64836a57 spi: altera: Cons... |
154 155 156 157 |
} } hw->count++; |
0b782531c spi: New driver f... |
158 |
} |
e19b63cd3 spi: altera: Swit... |
159 160 |
static int altera_spi_txrx(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) |
0b782531c spi: New driver f... |
161 |
{ |
e19b63cd3 spi: altera: Swit... |
162 |
struct altera_spi *hw = spi_master_get_devdata(master); |
3c6519736 spi: altera: use ... |
163 |
u32 val; |
0b782531c spi: New driver f... |
164 165 166 167 |
hw->tx = t->tx_buf; hw->rx = t->rx_buf; hw->count = 0; |
f073d37de spi: altera: Use ... |
168 |
hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); |
0b782531c spi: New driver f... |
169 170 171 172 173 |
hw->len = t->len / hw->bytes_per_word; if (hw->irq >= 0) { /* enable receive interrupt */ hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; |
3c6519736 spi: altera: use ... |
174 |
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
0b782531c spi: New driver f... |
175 176 |
/* send the first byte */ |
b64836a57 spi: altera: Cons... |
177 |
altera_spi_tx_word(hw); |
72be0ee42 spi: altera: Simp... |
178 |
|
78755373a spi: altera: fix ... |
179 180 |
return 1; } |
3c6519736 spi: altera: use ... |
181 |
|
78755373a spi: altera: fix ... |
182 183 |
while (hw->count < hw->len) { altera_spi_tx_word(hw); |
0b782531c spi: New driver f... |
184 |
|
78755373a spi: altera: fix ... |
185 186 187 188 189 190 |
for (;;) { altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); if (val & ALTERA_SPI_STATUS_RRDY_MSK) break; cpu_relax(); |
0b782531c spi: New driver f... |
191 |
} |
78755373a spi: altera: fix ... |
192 193 |
altera_spi_rx_word(hw); |
0b782531c spi: New driver f... |
194 |
} |
78755373a spi: altera: fix ... |
195 |
spi_finalize_current_transfer(master); |
0b782531c spi: New driver f... |
196 |
|
78755373a spi: altera: fix ... |
197 |
return 0; |
0b782531c spi: New driver f... |
198 199 200 201 |
} static irqreturn_t altera_spi_irq(int irq, void *dev) { |
e19b63cd3 spi: altera: Swit... |
202 203 |
struct spi_master *master = dev; struct altera_spi *hw = spi_master_get_devdata(master); |
0b782531c spi: New driver f... |
204 |
|
b64836a57 spi: altera: Cons... |
205 |
altera_spi_rx_word(hw); |
0b782531c spi: New driver f... |
206 |
|
e19b63cd3 spi: altera: Swit... |
207 |
if (hw->count < hw->len) { |
b64836a57 spi: altera: Cons... |
208 |
altera_spi_tx_word(hw); |
e19b63cd3 spi: altera: Swit... |
209 210 211 |
} else { /* disable receive interrupt */ hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; |
3c6519736 spi: altera: use ... |
212 |
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
e19b63cd3 spi: altera: Swit... |
213 214 215 |
spi_finalize_current_transfer(master); } |
0b782531c spi: New driver f... |
216 217 218 |
return IRQ_HANDLED; } |
fd4a319bc spi: Remove HOTPL... |
219 |
static int altera_spi_probe(struct platform_device *pdev) |
0b782531c spi: New driver f... |
220 |
{ |
3820061d3 spi: altera: supp... |
221 |
const struct platform_device_id *platid = platform_get_device_id(pdev); |
8e04187c1 spi: altera: add ... |
222 |
struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev); |
3820061d3 spi: altera: supp... |
223 |
enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; |
0b782531c spi: New driver f... |
224 225 |
struct altera_spi *hw; struct spi_master *master; |
0b782531c spi: New driver f... |
226 |
int err = -ENODEV; |
3c6519736 spi: altera: use ... |
227 |
u32 val; |
1fccd182a spi: altera: add ... |
228 |
u16 i; |
0b782531c spi: New driver f... |
229 230 231 232 233 234 235 |
master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); if (!master) return err; /* setup the master state. */ master->bus_num = pdev->id; |
8e04187c1 spi: altera: add ... |
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
if (pdata) { if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) { dev_err(&pdev->dev, "Invalid number of chipselect: %hu ", pdata->num_chipselect); return -EINVAL; } master->num_chipselect = pdata->num_chipselect; master->mode_bits = pdata->mode_bits; master->bits_per_word_mask = pdata->bits_per_word_mask; } else { master->num_chipselect = 16; master->mode_bits = SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); } |
bf2f2f795 spi: altera: Remo... |
254 |
master->dev.of_node = pdev->dev.of_node; |
e19b63cd3 spi: altera: Swit... |
255 256 |
master->transfer_one = altera_spi_txrx; master->set_cs = altera_spi_set_cs; |
0b782531c spi: New driver f... |
257 258 |
hw = spi_master_get_devdata(master); |
3c6519736 spi: altera: use ... |
259 |
hw->dev = &pdev->dev; |
0b782531c spi: New driver f... |
260 |
|
3820061d3 spi: altera: supp... |
261 262 |
if (platid) type = platid->driver_data; |
0b782531c spi: New driver f... |
263 |
/* find and map our resources */ |
3820061d3 spi: altera: supp... |
264 265 |
if (type == ALTERA_SPI_TYPE_SUBDEV) { struct resource *regoff; |
3c6519736 spi: altera: use ... |
266 |
|
3820061d3 spi: altera: supp... |
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hw->regmap) { dev_err(&pdev->dev, "get regmap failed "); goto exit; } regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); if (regoff) hw->regoff = regoff->start; } else { void __iomem *res; res = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(res)) { err = PTR_ERR(res); goto exit; } hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, &spi_altera_config); if (IS_ERR(hw->regmap)) { dev_err(&pdev->dev, "regmap mmio init failed "); err = PTR_ERR(hw->regmap); goto exit; } |
3c6519736 spi: altera: use ... |
294 |
} |
0b782531c spi: New driver f... |
295 296 |
/* program defaults into the registers */ hw->imr = 0; /* disable spi interrupts */ |
3c6519736 spi: altera: use ... |
297 298 299 300 301 |
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */ altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); if (val & ALTERA_SPI_STATUS_RRDY_MSK) altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */ |
0b782531c spi: New driver f... |
302 303 304 |
/* irq is optional */ hw->irq = platform_get_irq(pdev, 0); if (hw->irq >= 0) { |
0b782531c spi: New driver f... |
305 |
err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, |
e19b63cd3 spi: altera: Swit... |
306 |
pdev->name, master); |
0b782531c spi: New driver f... |
307 308 309 |
if (err) goto exit; } |
0b782531c spi: New driver f... |
310 |
|
e19b63cd3 spi: altera: Swit... |
311 |
err = devm_spi_register_master(&pdev->dev, master); |
0b782531c spi: New driver f... |
312 313 |
if (err) goto exit; |
1fccd182a spi: altera: add ... |
314 315 316 317 318 319 320 321 322 323 |
if (pdata) { for (i = 0; i < pdata->num_devices; i++) { if (!spi_new_device(master, pdata->devices + i)) dev_warn(&pdev->dev, "unable to create SPI device: %s ", pdata->devices[i].modalias); } } |
3820061d3 spi: altera: supp... |
324 325 |
dev_info(&pdev->dev, "regoff %u, irq %d ", hw->regoff, hw->irq); |
0b782531c spi: New driver f... |
326 327 |
return 0; |
0b782531c spi: New driver f... |
328 |
exit: |
0b782531c spi: New driver f... |
329 330 331 |
spi_master_put(master); return err; } |
0b782531c spi: New driver f... |
332 333 334 |
#ifdef CONFIG_OF static const struct of_device_id altera_spi_match[] = { { .compatible = "ALTR,spi-1.0", }, |
13960b47d dts: Deprecate AL... |
335 |
{ .compatible = "altr,spi-1.0", }, |
0b782531c spi: New driver f... |
336 337 338 |
{}, }; MODULE_DEVICE_TABLE(of, altera_spi_match); |
0b782531c spi: New driver f... |
339 |
#endif /* CONFIG_OF */ |
3820061d3 spi: altera: supp... |
340 |
static const struct platform_device_id altera_spi_ids[] = { |
de5fd9cb6 spi: altera: fix ... |
341 342 |
{ DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, |
3820061d3 spi: altera: supp... |
343 344 |
{ } }; |
1ac6f21a9 spi: altera: fix ... |
345 |
MODULE_DEVICE_TABLE(platform, altera_spi_ids); |
3820061d3 spi: altera: supp... |
346 |
|
0b782531c spi: New driver f... |
347 348 |
static struct platform_driver altera_spi_driver = { .probe = altera_spi_probe, |
0b782531c spi: New driver f... |
349 350 |
.driver = { .name = DRV_NAME, |
0b782531c spi: New driver f... |
351 |
.pm = NULL, |
89f98dc5e spi: spi-altera: ... |
352 |
.of_match_table = of_match_ptr(altera_spi_match), |
0b782531c spi: New driver f... |
353 |
}, |
3820061d3 spi: altera: supp... |
354 |
.id_table = altera_spi_ids, |
0b782531c spi: New driver f... |
355 |
}; |
940ab8896 drivercore: Add h... |
356 |
module_platform_driver(altera_spi_driver); |
0b782531c spi: New driver f... |
357 358 359 360 361 |
MODULE_DESCRIPTION("Altera SPI driver"); MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); |