]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0b782531 TC |
2 | /* |
3 | * Altera SPI driver | |
4 | * | |
5 | * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> | |
6 | * | |
7 | * Based on spi_s3c24xx.c, which is: | |
8 | * Copyright (c) 2006 Ben Dooks | |
9 | * Copyright (c) 2006 Simtec Electronics | |
10 | * Ben Dooks <ben@simtec.co.uk> | |
0b782531 TC |
11 | */ |
12 | ||
0b782531 | 13 | #include <linux/errno.h> |
d7614de4 | 14 | #include <linux/module.h> |
0b782531 | 15 | #include <linux/platform_device.h> |
8e04187c | 16 | #include <linux/spi/altera.h> |
0b782531 | 17 | #include <linux/spi/spi.h> |
0b782531 TC |
18 | #include <linux/io.h> |
19 | #include <linux/of.h> | |
20 | ||
21 | #define DRV_NAME "spi_altera" | |
22 | ||
23 | #define ALTERA_SPI_RXDATA 0 | |
24 | #define ALTERA_SPI_TXDATA 4 | |
25 | #define ALTERA_SPI_STATUS 8 | |
26 | #define ALTERA_SPI_CONTROL 12 | |
27 | #define ALTERA_SPI_SLAVE_SEL 20 | |
28 | ||
29 | #define ALTERA_SPI_STATUS_ROE_MSK 0x8 | |
30 | #define ALTERA_SPI_STATUS_TOE_MSK 0x10 | |
31 | #define ALTERA_SPI_STATUS_TMT_MSK 0x20 | |
32 | #define ALTERA_SPI_STATUS_TRDY_MSK 0x40 | |
33 | #define ALTERA_SPI_STATUS_RRDY_MSK 0x80 | |
34 | #define ALTERA_SPI_STATUS_E_MSK 0x100 | |
35 | ||
36 | #define ALTERA_SPI_CONTROL_IROE_MSK 0x8 | |
37 | #define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 | |
38 | #define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 | |
39 | #define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 | |
40 | #define ALTERA_SPI_CONTROL_IE_MSK 0x100 | |
41 | #define ALTERA_SPI_CONTROL_SSO_MSK 0x400 | |
42 | ||
3c651973 XY |
43 | static int altr_spi_writel(struct altera_spi *hw, unsigned int reg, |
44 | unsigned int val) | |
45 | { | |
46 | int ret; | |
47 | ||
3820061d | 48 | ret = regmap_write(hw->regmap, hw->regoff + reg, val); |
3c651973 XY |
49 | if (ret) |
50 | dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n", | |
51 | reg, val, ret); | |
52 | ||
53 | return ret; | |
54 | } | |
55 | ||
56 | static int altr_spi_readl(struct altera_spi *hw, unsigned int reg, | |
57 | unsigned int *val) | |
58 | { | |
59 | int ret; | |
60 | ||
3820061d | 61 | ret = regmap_read(hw->regmap, hw->regoff + reg, val); |
3c651973 XY |
62 | if (ret) |
63 | dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret); | |
64 | ||
65 | return ret; | |
66 | } | |
67 | ||
0b782531 TC |
68 | static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) |
69 | { | |
70 | return spi_master_get_devdata(sdev->master); | |
71 | } | |
72 | ||
e19b63cd | 73 | static void altera_spi_set_cs(struct spi_device *spi, bool is_high) |
0b782531 TC |
74 | { |
75 | struct altera_spi *hw = altera_spi_to_hw(spi); | |
76 | ||
e19b63cd LPC |
77 | if (is_high) { |
78 | hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; | |
3c651973 XY |
79 | altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
80 | altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0); | |
0b782531 | 81 | } else { |
3c651973 XY |
82 | altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, |
83 | BIT(spi->chip_select)); | |
e19b63cd | 84 | hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; |
3c651973 | 85 | altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
0b782531 TC |
86 | } |
87 | } | |
88 | ||
b64836a5 | 89 | static void altera_spi_tx_word(struct altera_spi *hw) |
0b782531 | 90 | { |
b64836a5 LPC |
91 | unsigned int txd = 0; |
92 | ||
0b782531 TC |
93 | if (hw->tx) { |
94 | switch (hw->bytes_per_word) { | |
95 | case 1: | |
b64836a5 LPC |
96 | txd = hw->tx[hw->count]; |
97 | break; | |
0b782531 | 98 | case 2: |
b64836a5 LPC |
99 | txd = (hw->tx[hw->count * 2] |
100 | | (hw->tx[hw->count * 2 + 1] << 8)); | |
101 | break; | |
3011d314 XY |
102 | case 4: |
103 | txd = (hw->tx[hw->count * 4] | |
104 | | (hw->tx[hw->count * 4 + 1] << 8) | |
105 | | (hw->tx[hw->count * 4 + 2] << 16) | |
106 | | (hw->tx[hw->count * 4 + 3] << 24)); | |
107 | break; | |
108 | ||
0b782531 TC |
109 | } |
110 | } | |
b64836a5 | 111 | |
3c651973 | 112 | altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd); |
b64836a5 LPC |
113 | } |
114 | ||
115 | static void altera_spi_rx_word(struct altera_spi *hw) | |
116 | { | |
117 | unsigned int rxd; | |
118 | ||
3c651973 | 119 | altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd); |
b64836a5 LPC |
120 | if (hw->rx) { |
121 | switch (hw->bytes_per_word) { | |
122 | case 1: | |
123 | hw->rx[hw->count] = rxd; | |
124 | break; | |
125 | case 2: | |
126 | hw->rx[hw->count * 2] = rxd; | |
127 | hw->rx[hw->count * 2 + 1] = rxd >> 8; | |
128 | break; | |
3011d314 XY |
129 | case 4: |
130 | hw->rx[hw->count * 4] = rxd; | |
131 | hw->rx[hw->count * 4 + 1] = rxd >> 8; | |
132 | hw->rx[hw->count * 4 + 2] = rxd >> 16; | |
133 | hw->rx[hw->count * 4 + 3] = rxd >> 24; | |
134 | break; | |
135 | ||
b64836a5 LPC |
136 | } |
137 | } | |
138 | ||
139 | hw->count++; | |
0b782531 TC |
140 | } |
141 | ||
e19b63cd LPC |
142 | static int altera_spi_txrx(struct spi_master *master, |
143 | struct spi_device *spi, struct spi_transfer *t) | |
0b782531 | 144 | { |
e19b63cd | 145 | struct altera_spi *hw = spi_master_get_devdata(master); |
3c651973 | 146 | u32 val; |
0b782531 TC |
147 | |
148 | hw->tx = t->tx_buf; | |
149 | hw->rx = t->rx_buf; | |
150 | hw->count = 0; | |
f073d37d | 151 | hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); |
0b782531 TC |
152 | hw->len = t->len / hw->bytes_per_word; |
153 | ||
154 | if (hw->irq >= 0) { | |
155 | /* enable receive interrupt */ | |
156 | hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; | |
3c651973 | 157 | altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
0b782531 TC |
158 | |
159 | /* send the first byte */ | |
b64836a5 | 160 | altera_spi_tx_word(hw); |
72be0ee4 | 161 | |
ede090f5 XY |
162 | return 1; |
163 | } | |
3c651973 | 164 | |
ede090f5 XY |
165 | while (hw->count < hw->len) { |
166 | altera_spi_tx_word(hw); | |
0b782531 | 167 | |
ede090f5 XY |
168 | for (;;) { |
169 | altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); | |
170 | if (val & ALTERA_SPI_STATUS_RRDY_MSK) | |
171 | break; | |
172 | ||
173 | cpu_relax(); | |
0b782531 | 174 | } |
ede090f5 XY |
175 | |
176 | altera_spi_rx_word(hw); | |
0b782531 | 177 | } |
ede090f5 | 178 | spi_finalize_current_transfer(master); |
0b782531 | 179 | |
ede090f5 | 180 | return 0; |
0b782531 TC |
181 | } |
182 | ||
b0c3d935 | 183 | irqreturn_t altera_spi_irq(int irq, void *dev) |
0b782531 | 184 | { |
e19b63cd LPC |
185 | struct spi_master *master = dev; |
186 | struct altera_spi *hw = spi_master_get_devdata(master); | |
0b782531 | 187 | |
b64836a5 | 188 | altera_spi_rx_word(hw); |
0b782531 | 189 | |
e19b63cd | 190 | if (hw->count < hw->len) { |
b64836a5 | 191 | altera_spi_tx_word(hw); |
e19b63cd LPC |
192 | } else { |
193 | /* disable receive interrupt */ | |
194 | hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; | |
3c651973 | 195 | altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
e19b63cd LPC |
196 | |
197 | spi_finalize_current_transfer(master); | |
198 | } | |
0b782531 TC |
199 | |
200 | return IRQ_HANDLED; | |
201 | } | |
b0c3d935 | 202 | EXPORT_SYMBOL_GPL(altera_spi_irq); |
0b782531 | 203 | |
b0c3d935 | 204 | void altera_spi_init_master(struct spi_master *master) |
0b782531 | 205 | { |
b0c3d935 | 206 | struct altera_spi *hw = spi_master_get_devdata(master); |
3c651973 | 207 | u32 val; |
8e04187c | 208 | |
e19b63cd LPC |
209 | master->transfer_one = altera_spi_txrx; |
210 | master->set_cs = altera_spi_set_cs; | |
0b782531 | 211 | |
0b782531 TC |
212 | /* program defaults into the registers */ |
213 | hw->imr = 0; /* disable spi interrupts */ | |
3c651973 XY |
214 | altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); |
215 | altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */ | |
216 | altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); | |
217 | if (val & ALTERA_SPI_STATUS_RRDY_MSK) | |
218 | altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */ | |
0b782531 | 219 | } |
b0c3d935 | 220 | EXPORT_SYMBOL_GPL(altera_spi_init_master); |
0b782531 | 221 | |
0b782531 | 222 | MODULE_LICENSE("GPL"); |