Commit | Line | Data |
---|---|---|
bbb336f3 SM |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | // | |
3 | // AMD SPI controller driver | |
4 | // | |
5 | // Copyright (c) 2020, Advanced Micro Devices, Inc. | |
6 | // | |
7 | // Author: Sanjay R Mehta <sanju.mehta@amd.com> | |
8 | ||
9 | #include <linux/acpi.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/spi/spi.h> | |
15 | ||
16 | #define AMD_SPI_CTRL0_REG 0x00 | |
17 | #define AMD_SPI_EXEC_CMD BIT(16) | |
18 | #define AMD_SPI_FIFO_CLEAR BIT(20) | |
19 | #define AMD_SPI_BUSY BIT(31) | |
20 | ||
21 | #define AMD_SPI_OPCODE_MASK 0xFF | |
22 | ||
23 | #define AMD_SPI_ALT_CS_REG 0x1D | |
24 | #define AMD_SPI_ALT_CS_MASK 0x3 | |
25 | ||
26 | #define AMD_SPI_FIFO_BASE 0x80 | |
27 | #define AMD_SPI_TX_COUNT_REG 0x48 | |
28 | #define AMD_SPI_RX_COUNT_REG 0x4B | |
29 | #define AMD_SPI_STATUS_REG 0x4C | |
30 | ||
31 | #define AMD_SPI_MEM_SIZE 200 | |
32 | ||
33 | /* M_CMD OP codes for SPI */ | |
34 | #define AMD_SPI_XFER_TX 1 | |
35 | #define AMD_SPI_XFER_RX 2 | |
36 | ||
37 | struct amd_spi { | |
38 | void __iomem *io_remap_addr; | |
39 | unsigned long io_base_addr; | |
40 | u32 rom_addr; | |
bbb336f3 SM |
41 | u8 chip_select; |
42 | }; | |
43 | ||
44 | static inline u8 amd_spi_readreg8(struct spi_master *master, int idx) | |
45 | { | |
46 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
47 | ||
48 | return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx); | |
49 | } | |
50 | ||
51 | static inline void amd_spi_writereg8(struct spi_master *master, int idx, | |
52 | u8 val) | |
53 | { | |
54 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
55 | ||
56 | iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); | |
57 | } | |
58 | ||
59 | static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx, | |
60 | u8 set, u8 clear) | |
61 | { | |
62 | u8 tmp = amd_spi_readreg8(master, idx); | |
63 | ||
64 | tmp = (tmp & ~clear) | set; | |
65 | amd_spi_writereg8(master, idx, tmp); | |
66 | } | |
67 | ||
68 | static inline u32 amd_spi_readreg32(struct spi_master *master, int idx) | |
69 | { | |
70 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
71 | ||
72 | return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx); | |
73 | } | |
74 | ||
75 | static inline void amd_spi_writereg32(struct spi_master *master, int idx, | |
76 | u32 val) | |
77 | { | |
78 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
79 | ||
80 | iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); | |
81 | } | |
82 | ||
83 | static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx, | |
84 | u32 set, u32 clear) | |
85 | { | |
86 | u32 tmp = amd_spi_readreg32(master, idx); | |
87 | ||
88 | tmp = (tmp & ~clear) | set; | |
89 | amd_spi_writereg32(master, idx, tmp); | |
90 | } | |
91 | ||
92 | static void amd_spi_select_chip(struct spi_master *master) | |
93 | { | |
94 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
95 | u8 chip_select = amd_spi->chip_select; | |
96 | ||
97 | amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select, | |
98 | AMD_SPI_ALT_CS_MASK); | |
99 | } | |
100 | ||
101 | static void amd_spi_clear_fifo_ptr(struct spi_master *master) | |
102 | { | |
103 | amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, | |
104 | AMD_SPI_FIFO_CLEAR); | |
105 | } | |
106 | ||
107 | static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode) | |
108 | { | |
109 | amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode, | |
110 | AMD_SPI_OPCODE_MASK); | |
111 | } | |
112 | ||
113 | static inline void amd_spi_set_rx_count(struct spi_master *master, | |
114 | u8 rx_count) | |
115 | { | |
116 | amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); | |
117 | } | |
118 | ||
119 | static inline void amd_spi_set_tx_count(struct spi_master *master, | |
120 | u8 tx_count) | |
121 | { | |
122 | amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); | |
123 | } | |
124 | ||
125 | static inline int amd_spi_busy_wait(struct amd_spi *amd_spi) | |
126 | { | |
127 | bool spi_busy; | |
128 | int timeout = 100000; | |
129 | ||
130 | /* poll for SPI bus to become idle */ | |
131 | spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + | |
132 | AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; | |
133 | while (spi_busy) { | |
134 | usleep_range(10, 20); | |
135 | if (timeout-- < 0) | |
136 | return -ETIMEDOUT; | |
137 | ||
138 | spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + | |
139 | AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; | |
140 | } | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | static void amd_spi_execute_opcode(struct spi_master *master) | |
146 | { | |
147 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
148 | ||
149 | /* Set ExecuteOpCode bit in the CTRL0 register */ | |
150 | amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, | |
151 | AMD_SPI_EXEC_CMD); | |
152 | ||
153 | amd_spi_busy_wait(amd_spi); | |
154 | } | |
155 | ||
156 | static int amd_spi_master_setup(struct spi_device *spi) | |
157 | { | |
158 | struct spi_master *master = spi->master; | |
159 | ||
160 | amd_spi_clear_fifo_ptr(master); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, | |
36c72a58 | 166 | struct spi_master *master, |
bbb336f3 SM |
167 | struct spi_message *message) |
168 | { | |
bbb336f3 | 169 | struct spi_transfer *xfer = NULL; |
68d047cb | 170 | u8 cmd_opcode; |
bbb336f3 SM |
171 | u8 *buf = NULL; |
172 | u32 m_cmd = 0; | |
173 | u32 i = 0; | |
174 | u32 tx_len = 0, rx_len = 0; | |
175 | ||
176 | list_for_each_entry(xfer, &message->transfers, | |
177 | transfer_list) { | |
178 | if (xfer->rx_buf) | |
179 | m_cmd = AMD_SPI_XFER_RX; | |
180 | if (xfer->tx_buf) | |
181 | m_cmd = AMD_SPI_XFER_TX; | |
182 | ||
183 | if (m_cmd & AMD_SPI_XFER_TX) { | |
184 | buf = (u8 *)xfer->tx_buf; | |
185 | tx_len = xfer->len - 1; | |
186 | cmd_opcode = *(u8 *)xfer->tx_buf; | |
187 | buf++; | |
188 | amd_spi_set_opcode(master, cmd_opcode); | |
189 | ||
190 | /* Write data into the FIFO. */ | |
191 | for (i = 0; i < tx_len; i++) { | |
192 | iowrite8(buf[i], | |
193 | ((u8 __iomem *)amd_spi->io_remap_addr + | |
194 | AMD_SPI_FIFO_BASE + i)); | |
195 | } | |
196 | ||
197 | amd_spi_set_tx_count(master, tx_len); | |
198 | amd_spi_clear_fifo_ptr(master); | |
199 | /* Execute command */ | |
200 | amd_spi_execute_opcode(master); | |
201 | } | |
202 | if (m_cmd & AMD_SPI_XFER_RX) { | |
203 | /* | |
204 | * Store no. of bytes to be received from | |
205 | * FIFO | |
206 | */ | |
207 | rx_len = xfer->len; | |
208 | buf = (u8 *)xfer->rx_buf; | |
209 | amd_spi_set_rx_count(master, rx_len); | |
210 | amd_spi_clear_fifo_ptr(master); | |
211 | /* Execute command */ | |
212 | amd_spi_execute_opcode(master); | |
213 | /* Read data from FIFO to receive buffer */ | |
214 | for (i = 0; i < rx_len; i++) | |
215 | buf[i] = amd_spi_readreg8(master, | |
216 | AMD_SPI_FIFO_BASE + | |
217 | tx_len + i); | |
218 | } | |
219 | } | |
220 | ||
221 | /* Update statistics */ | |
222 | message->actual_length = tx_len + rx_len + 1; | |
223 | /* complete the transaction */ | |
224 | message->status = 0; | |
225 | spi_finalize_current_message(master); | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | static int amd_spi_master_transfer(struct spi_master *master, | |
231 | struct spi_message *msg) | |
232 | { | |
233 | struct amd_spi *amd_spi = spi_master_get_devdata(master); | |
234 | struct spi_device *spi = msg->spi; | |
235 | ||
236 | amd_spi->chip_select = spi->chip_select; | |
237 | amd_spi_select_chip(master); | |
238 | ||
239 | /* | |
240 | * Extract spi_transfers from the spi message and | |
241 | * program the controller. | |
242 | */ | |
36c72a58 | 243 | amd_spi_fifo_xfer(amd_spi, master, msg); |
bbb336f3 SM |
244 | |
245 | return 0; | |
246 | } | |
247 | ||
248 | static int amd_spi_probe(struct platform_device *pdev) | |
249 | { | |
250 | struct device *dev = &pdev->dev; | |
251 | struct spi_master *master; | |
252 | struct amd_spi *amd_spi; | |
bbb336f3 SM |
253 | int err = 0; |
254 | ||
255 | /* Allocate storage for spi_master and driver private data */ | |
256 | master = spi_alloc_master(dev, sizeof(struct amd_spi)); | |
257 | if (!master) { | |
258 | dev_err(dev, "Error allocating SPI master\n"); | |
259 | return -ENOMEM; | |
260 | } | |
261 | ||
262 | amd_spi = spi_master_get_devdata(master); | |
2ed6e3ba | 263 | amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0); |
f84b604d WY |
264 | if (IS_ERR(amd_spi->io_remap_addr)) { |
265 | err = PTR_ERR(amd_spi->io_remap_addr); | |
bbb336f3 | 266 | dev_err(dev, "error %d ioremap of SPI registers failed\n", err); |
bbb336f3 SM |
267 | goto err_free_master; |
268 | } | |
269 | dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); | |
270 | ||
271 | /* Initialize the spi_master fields */ | |
272 | master->bus_num = 0; | |
273 | master->num_chipselect = 4; | |
274 | master->mode_bits = 0; | |
275 | master->flags = SPI_MASTER_HALF_DUPLEX; | |
276 | master->setup = amd_spi_master_setup; | |
277 | master->transfer_one_message = amd_spi_master_transfer; | |
278 | ||
279 | /* Register the controller with SPI framework */ | |
7b9c94bd | 280 | err = devm_spi_register_master(dev, master); |
bbb336f3 SM |
281 | if (err) { |
282 | dev_err(dev, "error %d registering SPI controller\n", err); | |
2b60c49f | 283 | goto err_free_master; |
bbb336f3 | 284 | } |
bbb336f3 SM |
285 | |
286 | return 0; | |
287 | ||
bbb336f3 SM |
288 | err_free_master: |
289 | spi_master_put(master); | |
290 | ||
cc17fbec | 291 | return err; |
bbb336f3 SM |
292 | } |
293 | ||
85ed0f63 | 294 | #ifdef CONFIG_ACPI |
bbb336f3 SM |
295 | static const struct acpi_device_id spi_acpi_match[] = { |
296 | { "AMDI0061", 0 }, | |
297 | {}, | |
298 | }; | |
299 | MODULE_DEVICE_TABLE(acpi, spi_acpi_match); | |
85ed0f63 | 300 | #endif |
bbb336f3 SM |
301 | |
302 | static struct platform_driver amd_spi_driver = { | |
303 | .driver = { | |
304 | .name = "amd_spi", | |
305 | .acpi_match_table = ACPI_PTR(spi_acpi_match), | |
306 | }, | |
307 | .probe = amd_spi_probe, | |
bbb336f3 SM |
308 | }; |
309 | ||
310 | module_platform_driver(amd_spi_driver); | |
311 | ||
312 | MODULE_LICENSE("Dual BSD/GPL"); | |
313 | MODULE_AUTHOR("Sanjay Mehta <sanju.mehta@amd.com>"); | |
314 | MODULE_DESCRIPTION("AMD SPI Master Controller Driver"); |