2 * Driver for Amlogic Meson SPI flash controller (SPIFC)
4 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
10 * You should have received a copy of the GNU General Public License
11 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #include <linux/clk.h>
15 #include <linux/delay.h>
16 #include <linux/device.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/pm_runtime.h>
23 #include <linux/regmap.h>
24 #include <linux/spi/spi.h>
25 #include <linux/types.h>
31 #define REG_CTRL1 0x0c
32 #define REG_STATUS 0x10
33 #define REG_CTRL2 0x14
34 #define REG_CLOCK 0x18
36 #define REG_USER1 0x20
37 #define REG_USER2 0x24
38 #define REG_USER3 0x28
39 #define REG_USER4 0x2c
40 #define REG_SLAVE 0x30
41 #define REG_SLAVE1 0x34
42 #define REG_SLAVE2 0x38
43 #define REG_SLAVE3 0x3c
49 #define CMD_USER BIT(18)
50 #define CTRL_ENABLE_AHB BIT(17)
51 #define CLOCK_SOURCE BIT(31)
52 #define CLOCK_DIV_SHIFT 12
53 #define CLOCK_DIV_MASK (0x3f << CLOCK_DIV_SHIFT)
54 #define CLOCK_CNT_HIGH_SHIFT 6
55 #define CLOCK_CNT_HIGH_MASK (0x3f << CLOCK_CNT_HIGH_SHIFT)
56 #define CLOCK_CNT_LOW_SHIFT 0
57 #define CLOCK_CNT_LOW_MASK (0x3f << CLOCK_CNT_LOW_SHIFT)
58 #define USER_DIN_EN_MS BIT(0)
59 #define USER_CMP_MODE BIT(2)
60 #define USER_UC_DOUT_SEL BIT(27)
61 #define USER_UC_DIN_SEL BIT(28)
62 #define USER_UC_MASK ((BIT(5) - 1) << 27)
63 #define USER1_BN_UC_DOUT_SHIFT 17
64 #define USER1_BN_UC_DOUT_MASK (0xff << 16)
65 #define USER1_BN_UC_DIN_SHIFT 8
66 #define USER1_BN_UC_DIN_MASK (0xff << 8)
67 #define USER4_CS_ACT BIT(30)
68 #define SLAVE_TRST_DONE BIT(4)
69 #define SLAVE_OP_MODE BIT(30)
70 #define SLAVE_SW_RST BIT(31)
72 #define SPIFC_BUFFER_SIZE 64
76 * @master: the SPI master
77 * @regmap: regmap for device registers
78 * @clk: input clock of the built-in baud rate generator
79 * @device: the device structure
82 struct spi_master
*master
;
83 struct regmap
*regmap
;
88 static struct regmap_config spifc_regmap_config
= {
92 .max_register
= REG_MAX
,
96 * meson_spifc_wait_ready() - wait for the current operation to terminate
97 * @spifc: the Meson SPI device
98 * Return: 0 on success, a negative value on error
100 static int meson_spifc_wait_ready(struct meson_spifc
*spifc
)
102 unsigned long deadline
= jiffies
+ msecs_to_jiffies(5);
106 regmap_read(spifc
->regmap
, REG_SLAVE
, &data
);
107 if (data
& SLAVE_TRST_DONE
)
110 } while (!time_after(jiffies
, deadline
));
116 * meson_spifc_drain_buffer() - copy data from device buffer to memory
117 * @spifc: the Meson SPI device
118 * @buf: the destination buffer
119 * @len: number of bytes to copy
121 static void meson_spifc_drain_buffer(struct meson_spifc
*spifc
, u8
*buf
,
128 regmap_read(spifc
->regmap
, REG_C0
+ i
, &data
);
131 *((u32
*)buf
) = data
;
134 memcpy(buf
, &data
, len
- i
);
142 * meson_spifc_fill_buffer() - copy data from memory to device buffer
143 * @spifc: the Meson SPI device
144 * @buf: the source buffer
145 * @len: number of bytes to copy
147 static void meson_spifc_fill_buffer(struct meson_spifc
*spifc
, const u8
*buf
,
157 memcpy(&data
, buf
, len
- i
);
159 regmap_write(spifc
->regmap
, REG_C0
+ i
, data
);
167 * meson_spifc_setup_speed() - program the clock divider
168 * @spifc: the Meson SPI device
169 * @speed: desired speed in Hz
171 static void meson_spifc_setup_speed(struct meson_spifc
*spifc
, u32 speed
)
173 unsigned long parent
, value
;
176 parent
= clk_get_rate(spifc
->clk
);
177 n
= max_t(int, parent
/ speed
- 1, 1);
179 dev_dbg(spifc
->dev
, "parent %lu, speed %u, n %d\n", parent
,
182 value
= (n
<< CLOCK_DIV_SHIFT
) & CLOCK_DIV_MASK
;
183 value
|= (n
<< CLOCK_CNT_LOW_SHIFT
) & CLOCK_CNT_LOW_MASK
;
184 value
|= (((n
+ 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT
) &
187 regmap_write(spifc
->regmap
, REG_CLOCK
, value
);
191 * meson_spifc_txrx() - transfer a chunk of data
192 * @spifc: the Meson SPI device
193 * @xfer: the current SPI transfer
194 * @offset: offset of the data to transfer
195 * @len: length of the data to transfer
196 * @last_xfer: whether this is the last transfer of the message
197 * @last_chunk: whether this is the last chunk of the transfer
198 * Return: 0 on success, a negative value on error
200 static int meson_spifc_txrx(struct meson_spifc
*spifc
,
201 struct spi_transfer
*xfer
,
202 int offset
, int len
, bool last_xfer
,
209 meson_spifc_fill_buffer(spifc
, xfer
->tx_buf
+ offset
, len
);
211 /* enable DOUT stage */
212 regmap_update_bits(spifc
->regmap
, REG_USER
, USER_UC_MASK
,
214 regmap_write(spifc
->regmap
, REG_USER1
,
215 (8 * len
- 1) << USER1_BN_UC_DOUT_SHIFT
);
217 /* enable data input during DOUT */
218 regmap_update_bits(spifc
->regmap
, REG_USER
, USER_DIN_EN_MS
,
223 keep_cs
= xfer
->cs_change
;
225 keep_cs
= !xfer
->cs_change
;
228 regmap_update_bits(spifc
->regmap
, REG_USER4
, USER4_CS_ACT
,
229 keep_cs
? USER4_CS_ACT
: 0);
231 /* clear transition done bit */
232 regmap_update_bits(spifc
->regmap
, REG_SLAVE
, SLAVE_TRST_DONE
, 0);
234 regmap_update_bits(spifc
->regmap
, REG_CMD
, CMD_USER
, CMD_USER
);
236 ret
= meson_spifc_wait_ready(spifc
);
238 if (!ret
&& xfer
->rx_buf
)
239 meson_spifc_drain_buffer(spifc
, xfer
->rx_buf
+ offset
, len
);
245 * meson_spifc_transfer_one() - perform a single transfer
246 * @master: the SPI master
247 * @spi: the SPI device
248 * @xfer: the current SPI transfer
249 * Return: 0 on success, a negative value on error
251 static int meson_spifc_transfer_one(struct spi_master
*master
,
252 struct spi_device
*spi
,
253 struct spi_transfer
*xfer
)
255 struct meson_spifc
*spifc
= spi_master_get_devdata(master
);
256 int len
, done
= 0, ret
= 0;
258 meson_spifc_setup_speed(spifc
, xfer
->speed_hz
);
260 regmap_update_bits(spifc
->regmap
, REG_CTRL
, CTRL_ENABLE_AHB
, 0);
262 while (done
< xfer
->len
&& !ret
) {
263 len
= min_t(int, xfer
->len
- done
, SPIFC_BUFFER_SIZE
);
264 ret
= meson_spifc_txrx(spifc
, xfer
, done
, len
,
265 spi_transfer_is_last(master
, xfer
),
266 done
+ len
>= xfer
->len
);
270 regmap_update_bits(spifc
->regmap
, REG_CTRL
, CTRL_ENABLE_AHB
,
277 * meson_spifc_hw_init() - reset and initialize the SPI controller
278 * @spifc: the Meson SPI device
280 static void meson_spifc_hw_init(struct meson_spifc
*spifc
)
283 regmap_update_bits(spifc
->regmap
, REG_SLAVE
, SLAVE_SW_RST
,
285 /* disable compatible mode */
286 regmap_update_bits(spifc
->regmap
, REG_USER
, USER_CMP_MODE
, 0);
287 /* set master mode */
288 regmap_update_bits(spifc
->regmap
, REG_SLAVE
, SLAVE_OP_MODE
, 0);
291 static int meson_spifc_probe(struct platform_device
*pdev
)
293 struct spi_master
*master
;
294 struct meson_spifc
*spifc
;
295 struct resource
*res
;
300 master
= spi_alloc_master(&pdev
->dev
, sizeof(struct meson_spifc
));
304 platform_set_drvdata(pdev
, master
);
306 spifc
= spi_master_get_devdata(master
);
307 spifc
->dev
= &pdev
->dev
;
309 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
310 base
= devm_ioremap_resource(spifc
->dev
, res
);
316 spifc
->regmap
= devm_regmap_init_mmio(spifc
->dev
, base
,
317 &spifc_regmap_config
);
318 if (IS_ERR(spifc
->regmap
)) {
319 ret
= PTR_ERR(spifc
->regmap
);
323 spifc
->clk
= devm_clk_get(spifc
->dev
, NULL
);
324 if (IS_ERR(spifc
->clk
)) {
325 dev_err(spifc
->dev
, "missing clock\n");
326 ret
= PTR_ERR(spifc
->clk
);
330 ret
= clk_prepare_enable(spifc
->clk
);
332 dev_err(spifc
->dev
, "can't prepare clock\n");
336 rate
= clk_get_rate(spifc
->clk
);
338 master
->num_chipselect
= 1;
339 master
->dev
.of_node
= pdev
->dev
.of_node
;
340 master
->bits_per_word_mask
= SPI_BPW_MASK(8);
341 master
->auto_runtime_pm
= true;
342 master
->transfer_one
= meson_spifc_transfer_one
;
343 master
->min_speed_hz
= rate
>> 6;
344 master
->max_speed_hz
= rate
>> 1;
346 meson_spifc_hw_init(spifc
);
348 pm_runtime_set_active(spifc
->dev
);
349 pm_runtime_enable(spifc
->dev
);
351 ret
= devm_spi_register_master(spifc
->dev
, master
);
353 dev_err(spifc
->dev
, "failed to register spi master\n");
359 clk_disable_unprepare(spifc
->clk
);
361 spi_master_put(master
);
365 static int meson_spifc_remove(struct platform_device
*pdev
)
367 struct spi_master
*master
= platform_get_drvdata(pdev
);
368 struct meson_spifc
*spifc
= spi_master_get_devdata(master
);
370 pm_runtime_get_sync(&pdev
->dev
);
371 clk_disable_unprepare(spifc
->clk
);
372 pm_runtime_disable(&pdev
->dev
);
377 #ifdef CONFIG_PM_SLEEP
378 static int meson_spifc_suspend(struct device
*dev
)
380 struct spi_master
*master
= dev_get_drvdata(dev
);
381 struct meson_spifc
*spifc
= spi_master_get_devdata(master
);
384 ret
= spi_master_suspend(master
);
388 if (!pm_runtime_suspended(dev
))
389 clk_disable_unprepare(spifc
->clk
);
394 static int meson_spifc_resume(struct device
*dev
)
396 struct spi_master
*master
= dev_get_drvdata(dev
);
397 struct meson_spifc
*spifc
= spi_master_get_devdata(master
);
400 if (!pm_runtime_suspended(dev
)) {
401 ret
= clk_prepare_enable(spifc
->clk
);
406 meson_spifc_hw_init(spifc
);
408 ret
= spi_master_resume(master
);
410 clk_disable_unprepare(spifc
->clk
);
414 #endif /* CONFIG_PM_SLEEP */
417 static int meson_spifc_runtime_suspend(struct device
*dev
)
419 struct spi_master
*master
= dev_get_drvdata(dev
);
420 struct meson_spifc
*spifc
= spi_master_get_devdata(master
);
422 clk_disable_unprepare(spifc
->clk
);
427 static int meson_spifc_runtime_resume(struct device
*dev
)
429 struct spi_master
*master
= dev_get_drvdata(dev
);
430 struct meson_spifc
*spifc
= spi_master_get_devdata(master
);
432 return clk_prepare_enable(spifc
->clk
);
434 #endif /* CONFIG_PM */
436 static const struct dev_pm_ops meson_spifc_pm_ops
= {
437 SET_SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend
, meson_spifc_resume
)
438 SET_RUNTIME_PM_OPS(meson_spifc_runtime_suspend
,
439 meson_spifc_runtime_resume
,
443 static const struct of_device_id meson_spifc_dt_match
[] = {
444 { .compatible
= "amlogic,meson6-spifc", },
448 static struct platform_driver meson_spifc_driver
= {
449 .probe
= meson_spifc_probe
,
450 .remove
= meson_spifc_remove
,
452 .name
= "meson-spifc",
453 .of_match_table
= of_match_ptr(meson_spifc_dt_match
),
454 .pm
= &meson_spifc_pm_ops
,
458 module_platform_driver(meson_spifc_driver
);
460 MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
461 MODULE_DESCRIPTION("Amlogic Meson SPIFC driver");
462 MODULE_LICENSE("GPL v2");