blob: d7ebb6bf1ac7f1fee6f47d93db3893018ed2d25e [file] [log] [blame]
Neil Armstrong5c162172018-11-22 11:01:05 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
4 * Copyright (C) 2018 BayLibre, SAS
5 * Author: Neil Armstrong <narmstrong@baylibre.com>
6 *
7 * Amlogic Meson SPI Flash Controller driver
8 */
9
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Neil Armstrong5c162172018-11-22 11:01:05 +010011#include <spi.h>
12#include <clk.h>
13#include <dm.h>
14#include <regmap.h>
15#include <errno.h>
16#include <asm/io.h>
17#include <linux/bitfield.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060018#include <linux/bitops.h>
Neil Armstrong5c162172018-11-22 11:01:05 +010019
20/* register map */
21#define REG_CMD 0x00
22#define REG_ADDR 0x04
23#define REG_CTRL 0x08
24#define REG_CTRL1 0x0c
25#define REG_STATUS 0x10
26#define REG_CTRL2 0x14
27#define REG_CLOCK 0x18
28#define REG_USER 0x1c
29#define REG_USER1 0x20
30#define REG_USER2 0x24
31#define REG_USER3 0x28
32#define REG_USER4 0x2c
33#define REG_SLAVE 0x30
34#define REG_SLAVE1 0x34
35#define REG_SLAVE2 0x38
36#define REG_SLAVE3 0x3c
37#define REG_C0 0x40
38#define REG_B8 0x60
39#define REG_MAX 0x7c
40
41/* register fields */
42#define CMD_USER BIT(18)
43#define CTRL_ENABLE_AHB BIT(17)
44#define CLOCK_SOURCE BIT(31)
45#define CLOCK_DIV_SHIFT 12
46#define CLOCK_DIV_MASK (0x3f << CLOCK_DIV_SHIFT)
47#define CLOCK_CNT_HIGH_SHIFT 6
48#define CLOCK_CNT_HIGH_MASK (0x3f << CLOCK_CNT_HIGH_SHIFT)
49#define CLOCK_CNT_LOW_SHIFT 0
50#define CLOCK_CNT_LOW_MASK (0x3f << CLOCK_CNT_LOW_SHIFT)
51#define USER_DIN_EN_MS BIT(0)
52#define USER_CMP_MODE BIT(2)
53#define USER_CLK_NOT_INV BIT(7)
54#define USER_UC_DOUT_SEL BIT(27)
55#define USER_UC_DIN_SEL BIT(28)
56#define USER_UC_MASK ((BIT(5) - 1) << 27)
57#define USER1_BN_UC_DOUT_SHIFT 17
58#define USER1_BN_UC_DOUT_MASK (0xff << 16)
59#define USER1_BN_UC_DIN_SHIFT 8
60#define USER1_BN_UC_DIN_MASK (0xff << 8)
61#define USER4_CS_POL_HIGH BIT(23)
62#define USER4_IDLE_CLK_HIGH BIT(29)
63#define USER4_CS_ACT BIT(30)
64#define SLAVE_TRST_DONE BIT(4)
65#define SLAVE_OP_MODE BIT(30)
66#define SLAVE_SW_RST BIT(31)
67
68#define SPIFC_BUFFER_SIZE 64
69
70struct meson_spifc_priv {
71 struct regmap *regmap;
72 struct clk clk;
73};
74
75/**
76 * meson_spifc_drain_buffer() - copy data from device buffer to memory
77 * @spifc: the Meson SPI device
78 * @buf: the destination buffer
79 * @len: number of bytes to copy
80 */
81static void meson_spifc_drain_buffer(struct meson_spifc_priv *spifc,
82 u8 *buf, int len)
83{
84 u32 data;
85 int i = 0;
86
87 while (i < len) {
88 regmap_read(spifc->regmap, REG_C0 + i, &data);
89
90 if (len - i >= 4) {
91 *((u32 *)buf) = data;
92 buf += 4;
93 } else {
94 memcpy(buf, &data, len - i);
95 break;
96 }
97 i += 4;
98 }
99}
100
101/**
102 * meson_spifc_fill_buffer() - copy data from memory to device buffer
103 * @spifc: the Meson SPI device
104 * @buf: the source buffer
105 * @len: number of bytes to copy
106 */
107static void meson_spifc_fill_buffer(struct meson_spifc_priv *spifc,
108 const u8 *buf, int len)
109{
110 u32 data = 0;
111 int i = 0;
112
113 while (i < len) {
114 if (len - i >= 4)
115 data = *(u32 *)buf;
116 else
117 memcpy(&data, buf, len - i);
118
119 regmap_write(spifc->regmap, REG_C0 + i, data);
120
121 buf += 4;
122 i += 4;
123 }
124}
125
126/**
127 * meson_spifc_txrx() - transfer a chunk of data
128 * @spifc: the Meson SPI device
129 * @dout: data buffer for TX
130 * @din: data buffer for RX
131 * @offset: offset of the data to transfer
132 * @len: length of the data to transfer
133 * @last_xfer: whether this is the last transfer of the message
134 * @last_chunk: whether this is the last chunk of the transfer
135 * Return: 0 on success, a negative value on error
136 */
137static int meson_spifc_txrx(struct meson_spifc_priv *spifc,
138 const u8 *dout, u8 *din, int offset,
139 int len, bool last_xfer, bool last_chunk)
140{
141 bool keep_cs = true;
142 u32 data;
143 int ret;
144
145 if (dout)
146 meson_spifc_fill_buffer(spifc, dout + offset, len);
147
148 /* enable DOUT stage */
149 regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK,
150 USER_UC_DOUT_SEL);
151 regmap_write(spifc->regmap, REG_USER1,
152 (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT);
153
154 /* enable data input during DOUT */
155 regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS,
156 USER_DIN_EN_MS);
157
158 if (last_chunk && last_xfer)
159 keep_cs = false;
160
161 regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT,
162 keep_cs ? USER4_CS_ACT : 0);
163
164 /* clear transition done bit */
165 regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0);
166 /* start transfer */
167 regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
168
169 /* wait for the current operation to terminate */
170 ret = regmap_read_poll_timeout(spifc->regmap, REG_SLAVE, data,
171 (data & SLAVE_TRST_DONE),
172 0, 5 * CONFIG_SYS_HZ);
173
174 if (!ret && din)
175 meson_spifc_drain_buffer(spifc, din + offset, len);
176
177 return ret;
178}
179
180/**
181 * meson_spifc_xfer() - perform a single transfer
182 * @dev: the SPI controller device
183 * @bitlen: length of the transfer
184 * @dout: data buffer for TX
185 * @din: data buffer for RX
186 * @flags: transfer flags
187 * Return: 0 on success, a negative value on error
188 */
189static int meson_spifc_xfer(struct udevice *slave, unsigned int bitlen,
190 const void *dout, void *din, unsigned long flags)
191{
192 struct meson_spifc_priv *spifc = dev_get_priv(slave->parent);
193 int blen = bitlen / 8;
194 int len, done = 0, ret = 0;
195
196 if (bitlen % 8)
197 return -EINVAL;
198
199 debug("xfer len %d (%d) dout %p din %p\n", bitlen, blen, dout, din);
200
201 regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0);
202
203 while (done < blen && !ret) {
204 len = min_t(int, blen - done, SPIFC_BUFFER_SIZE);
205 ret = meson_spifc_txrx(spifc, dout, din, done, len,
206 flags & SPI_XFER_END,
207 done + len >= blen);
208 done += len;
209 }
210
211 regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB,
212 CTRL_ENABLE_AHB);
213
214 return ret;
215}
216
217/**
218 * meson_spifc_set_speed() - program the clock divider
219 * @dev: the SPI controller device
220 * @speed: desired speed in Hz
221 */
222static int meson_spifc_set_speed(struct udevice *dev, uint speed)
223{
224 struct meson_spifc_priv *spifc = dev_get_priv(dev);
225 unsigned long parent, value;
226 int n;
227
228 parent = clk_get_rate(&spifc->clk);
229 n = max_t(int, parent / speed - 1, 1);
230
231 debug("parent %lu, speed %u, n %d\n", parent, speed, n);
232
233 value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
234 value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
235 value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
236 CLOCK_CNT_HIGH_MASK;
237
238 regmap_write(spifc->regmap, REG_CLOCK, value);
239
240 return 0;
241}
242
243/**
244 * meson_spifc_set_mode() - setups the SPI bus mode
245 * @dev: the SPI controller device
246 * @mode: desired mode bitfield
247 * Return: 0 on success, -ENODEV on error
248 */
249static int meson_spifc_set_mode(struct udevice *dev, uint mode)
250{
251 struct meson_spifc_priv *spifc = dev_get_priv(dev);
252
253 if (mode & (SPI_CPHA | SPI_RX_QUAD | SPI_RX_DUAL |
254 SPI_TX_QUAD | SPI_TX_DUAL))
255 return -ENODEV;
256
257 regmap_update_bits(spifc->regmap, REG_USER, USER_CLK_NOT_INV,
258 mode & SPI_CPOL ? USER_CLK_NOT_INV : 0);
259
260 regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_POL_HIGH,
261 mode & SPI_CS_HIGH ? USER4_CS_POL_HIGH : 0);
262
263 return 0;
264}
265
266/**
267 * meson_spifc_hw_init() - reset and initialize the SPI controller
268 * @spifc: the Meson SPI device
269 */
270static void meson_spifc_hw_init(struct meson_spifc_priv *spifc)
271{
272 /* reset device */
273 regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST,
274 SLAVE_SW_RST);
275 /* disable compatible mode */
276 regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0);
277 /* set master mode */
278 regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0);
279}
280
281static const struct dm_spi_ops meson_spifc_ops = {
282 .xfer = meson_spifc_xfer,
283 .set_speed = meson_spifc_set_speed,
284 .set_mode = meson_spifc_set_mode,
285};
286
287static int meson_spifc_probe(struct udevice *dev)
288{
289 struct meson_spifc_priv *priv = dev_get_priv(dev);
290 int ret;
291
292 ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
293 if (ret)
294 return ret;
295
296 ret = clk_get_by_index(dev, 0, &priv->clk);
297 if (ret)
298 return ret;
299
300 ret = clk_enable(&priv->clk);
301 if (ret)
302 return ret;
303
304 meson_spifc_hw_init(priv);
305
306 return 0;
307}
308
309static const struct udevice_id meson_spifc_ids[] = {
310 { .compatible = "amlogic,meson-gxbb-spifc", },
311 { }
312};
313
314U_BOOT_DRIVER(meson_spifc) = {
315 .name = "meson_spifc",
316 .id = UCLASS_SPI,
317 .of_match = meson_spifc_ids,
318 .ops = &meson_spifc_ops,
319 .probe = meson_spifc_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700320 .priv_auto = sizeof(struct meson_spifc_priv),
Neil Armstrong5c162172018-11-22 11:01:05 +0100321};