1499853d6Sryan_chen // SPDX-License-Identifier: GPL-2.0+
2499853d6Sryan_chen /*
3499853d6Sryan_chen * ASPEED AST2500 FMC/SPI Controller driver
4499853d6Sryan_chen *
5499853d6Sryan_chen * Copyright (c) 2015-2018, IBM Corporation.
6499853d6Sryan_chen */
7499853d6Sryan_chen
8499853d6Sryan_chen #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9499853d6Sryan_chen
10499853d6Sryan_chen #include <common.h>
11499853d6Sryan_chen #include <clk.h>
12499853d6Sryan_chen #include <dm.h>
13499853d6Sryan_chen #include <spi.h>
14499853d6Sryan_chen #include <spi_flash.h>
15499853d6Sryan_chen #include <asm/io.h>
16499853d6Sryan_chen #include <linux/ioport.h>
17edaef9d2SChin-Ting Kuo #include <malloc.h>
18499853d6Sryan_chen
19499853d6Sryan_chen #define ASPEED_SPI_MAX_CS 3
20beec505fSChin-Ting Kuo #define FLASH_CALIBRATION_LEN 0x400
21499853d6Sryan_chen
22499853d6Sryan_chen struct aspeed_spi_regs {
23499853d6Sryan_chen u32 conf; /* 0x00 CE Type Setting */
24499853d6Sryan_chen u32 ctrl; /* 0x04 Control */
25499853d6Sryan_chen u32 intr_ctrl; /* 0x08 Interrupt Control and Status */
26499853d6Sryan_chen u32 cmd_ctrl; /* 0x0c Command Control */
27499853d6Sryan_chen u32 ce_ctrl[ASPEED_SPI_MAX_CS]; /* 0x10 .. 0x18 CEx Control */
28499853d6Sryan_chen u32 _reserved0[5]; /* .. */
29499853d6Sryan_chen u32 segment_addr[ASPEED_SPI_MAX_CS];
30499853d6Sryan_chen /* 0x30 .. 0x38 Segment Address */
310a73b911SChin-Ting Kuo u32 _reserved1[5]; /* .. */
320a73b911SChin-Ting Kuo u32 soft_rst_cmd_ctrl; /* 0x50 Auto Soft-Reset Command Control */
330a73b911SChin-Ting Kuo u32 _reserved2[11]; /* .. */
34499853d6Sryan_chen u32 dma_ctrl; /* 0x80 DMA Control/Status */
35499853d6Sryan_chen u32 dma_flash_addr; /* 0x84 DMA Flash Side Address */
36499853d6Sryan_chen u32 dma_dram_addr; /* 0x88 DMA DRAM Side Address */
37499853d6Sryan_chen u32 dma_len; /* 0x8c DMA Length Register */
38499853d6Sryan_chen u32 dma_checksum; /* 0x90 Checksum Calculation Result */
39499853d6Sryan_chen u32 timings; /* 0x94 Read Timing Compensation */
404c8f336cSChin-Ting Kuo u32 _reserved3[1];
41499853d6Sryan_chen /* not used */
42499853d6Sryan_chen u32 soft_strap_status; /* 0x9c Software Strap Status */
43499853d6Sryan_chen u32 write_cmd_filter_ctrl; /* 0xa0 Write Command Filter Control */
44499853d6Sryan_chen u32 write_addr_filter_ctrl; /* 0xa4 Write Address Filter Control */
45499853d6Sryan_chen u32 lock_ctrl_reset; /* 0xa8 Lock Control (SRST#) */
46499853d6Sryan_chen u32 lock_ctrl_wdt; /* 0xac Lock Control (Watchdog) */
474c8f336cSChin-Ting Kuo u32 write_addr_filter[8]; /* 0xb0 Write Address Filter */
484c8f336cSChin-Ting Kuo u32 _reserved4[12];
494c8f336cSChin-Ting Kuo u32 fully_qualified_cmd[20]; /* 0x100 Fully Qualified Command */
504c8f336cSChin-Ting Kuo u32 addr_qualified_cmd[12]; /* 0x150 Address Qualified Command */
51499853d6Sryan_chen };
52499853d6Sryan_chen
53499853d6Sryan_chen /* CE Type Setting Register */
54499853d6Sryan_chen #define CONF_ENABLE_W2 BIT(18)
55499853d6Sryan_chen #define CONF_ENABLE_W1 BIT(17)
56499853d6Sryan_chen #define CONF_ENABLE_W0 BIT(16)
57499853d6Sryan_chen #define CONF_FLASH_TYPE2 4
58499853d6Sryan_chen #define CONF_FLASH_TYPE1 2 /* Hardwired to SPI */
59499853d6Sryan_chen #define CONF_FLASH_TYPE0 0 /* Hardwired to SPI */
60499853d6Sryan_chen #define CONF_FLASH_TYPE_NOR 0x0
61499853d6Sryan_chen #define CONF_FLASH_TYPE_SPI 0x2
62499853d6Sryan_chen
63499853d6Sryan_chen /* CE Control Register */
64499853d6Sryan_chen #define CTRL_EXTENDED2 BIT(2) /* 32 bit addressing for SPI */
65499853d6Sryan_chen #define CTRL_EXTENDED1 BIT(1) /* 32 bit addressing for SPI */
66499853d6Sryan_chen #define CTRL_EXTENDED0 BIT(0) /* 32 bit addressing for SPI */
67499853d6Sryan_chen
68499853d6Sryan_chen /* Interrupt Control and Status Register */
69499853d6Sryan_chen #define INTR_CTRL_DMA_STATUS BIT(11)
70499853d6Sryan_chen #define INTR_CTRL_CMD_ABORT_STATUS BIT(10)
71499853d6Sryan_chen #define INTR_CTRL_WRITE_PROTECT_STATUS BIT(9)
72499853d6Sryan_chen #define INTR_CTRL_DMA_EN BIT(3)
73499853d6Sryan_chen #define INTR_CTRL_CMD_ABORT_EN BIT(2)
74499853d6Sryan_chen #define INTR_CTRL_WRITE_PROTECT_EN BIT(1)
75499853d6Sryan_chen
76499853d6Sryan_chen /* CEx Control Register */
77499853d6Sryan_chen #define CE_CTRL_IO_MODE_MASK GENMASK(31, 28)
7876e3c7a9Sryan_chen #define CE_CTRL_IO_QPI_DATA BIT(31)
79499853d6Sryan_chen #define CE_CTRL_IO_DUAL_DATA BIT(29)
80cd800046SChin-Ting Kuo #define CE_CTRL_IO_SINGLE 0
81499853d6Sryan_chen #define CE_CTRL_IO_DUAL_ADDR_DATA (BIT(29) | BIT(28))
82499853d6Sryan_chen #define CE_CTRL_IO_QUAD_DATA BIT(30)
83499853d6Sryan_chen #define CE_CTRL_IO_QUAD_ADDR_DATA (BIT(30) | BIT(28))
84499853d6Sryan_chen #define CE_CTRL_CMD_SHIFT 16
85499853d6Sryan_chen #define CE_CTRL_CMD_MASK 0xff
86499853d6Sryan_chen #define CE_CTRL_CMD(cmd) \
87499853d6Sryan_chen (((cmd) & CE_CTRL_CMD_MASK) << CE_CTRL_CMD_SHIFT)
88499853d6Sryan_chen #define CE_CTRL_DUMMY_HIGH_SHIFT 14
89499853d6Sryan_chen #define CE_CTRL_DUMMY_HIGH_MASK 0x1
90499853d6Sryan_chen #define CE_CTRL_CLOCK_FREQ_SHIFT 8
91499853d6Sryan_chen #define CE_CTRL_CLOCK_FREQ_MASK 0xf
92499853d6Sryan_chen #define CE_CTRL_CLOCK_FREQ(div) \
93499853d6Sryan_chen (((div) & CE_CTRL_CLOCK_FREQ_MASK) << CE_CTRL_CLOCK_FREQ_SHIFT)
947d182336Sryan_chen #define CE_G6_CTRL_CLOCK_FREQ(div) \
9541629eddSChin-Ting Kuo ((((div) & CE_CTRL_CLOCK_FREQ_MASK) << CE_CTRL_CLOCK_FREQ_SHIFT) | (((div) & 0xf0) << 20))
96499853d6Sryan_chen #define CE_CTRL_DUMMY_LOW_SHIFT 6 /* 2 bits [7:6] */
97499853d6Sryan_chen #define CE_CTRL_DUMMY_LOW_MASK 0x3
98499853d6Sryan_chen #define CE_CTRL_DUMMY(dummy) \
99499853d6Sryan_chen (((((dummy) >> 2) & CE_CTRL_DUMMY_HIGH_MASK) \
100499853d6Sryan_chen << CE_CTRL_DUMMY_HIGH_SHIFT) | \
101499853d6Sryan_chen (((dummy) & CE_CTRL_DUMMY_LOW_MASK) << CE_CTRL_DUMMY_LOW_SHIFT))
102499853d6Sryan_chen #define CE_CTRL_STOP_ACTIVE BIT(2)
103499853d6Sryan_chen #define CE_CTRL_MODE_MASK 0x3
104499853d6Sryan_chen #define CE_CTRL_READMODE 0x0
105499853d6Sryan_chen #define CE_CTRL_FREADMODE 0x1
106499853d6Sryan_chen #define CE_CTRL_WRITEMODE 0x2
107499853d6Sryan_chen #define CE_CTRL_USERMODE 0x3
108edaef9d2SChin-Ting Kuo #define CE_CTRL_FREQ_MASK 0xf0fff0ff
109499853d6Sryan_chen
110cd800046SChin-Ting Kuo #define SPI_READ_FROM_FLASH 0x00000001
111cd800046SChin-Ting Kuo #define SPI_WRITE_TO_FLASH 0x00000002
112cd800046SChin-Ting Kuo
1130a73b911SChin-Ting Kuo /* Auto Soft-Reset Command Control */
1140a73b911SChin-Ting Kuo #define SOFT_RST_CMD_EN GENMASK(1, 0)
1150a73b911SChin-Ting Kuo
116499853d6Sryan_chen /*
117499853d6Sryan_chen * The Segment Register uses a 8MB unit to encode the start address
118499853d6Sryan_chen * and the end address of the AHB window of a SPI flash device.
119499853d6Sryan_chen * Default segment addresses are :
120499853d6Sryan_chen *
121499853d6Sryan_chen * CE0 0x20000000 - 0x2fffffff 128MB
122499853d6Sryan_chen * CE1 0x28000000 - 0x29ffffff 32MB
123499853d6Sryan_chen * CE2 0x2a000000 - 0x2bffffff 32MB
124499853d6Sryan_chen *
125499853d6Sryan_chen * The full address space of the AHB window of the controller is
126499853d6Sryan_chen * covered and CE0 start address and CE2 end addresses are read-only.
127499853d6Sryan_chen */
128499853d6Sryan_chen #define SEGMENT_ADDR_START(reg) ((((reg) >> 16) & 0xff) << 23)
129499853d6Sryan_chen #define SEGMENT_ADDR_END(reg) ((((reg) >> 24) & 0xff) << 23)
130499853d6Sryan_chen #define SEGMENT_ADDR_VALUE(start, end) \
131499853d6Sryan_chen (((((start) >> 23) & 0xff) << 16) | ((((end) >> 23) & 0xff) << 24))
132499853d6Sryan_chen
1338fbbfa7dSChin-Ting Kuo #define G6_SEGMENT_ADDR_START(reg) (((reg) << 16) & 0x0ff00000)
1348fbbfa7dSChin-Ting Kuo #define G6_SEGMENT_ADDR_END(reg) (((reg) & 0x0ff00000) + 0x100000)
135da83dd7eSryan_chen #define G6_SEGMENT_ADDR_VALUE(start, end) \
1368fbbfa7dSChin-Ting Kuo ((((start) & 0x0ff00000) >> 16) | (((end) - 0x100000) & 0xffff0000))
137da83dd7eSryan_chen
138499853d6Sryan_chen /* DMA Control/Status Register */
139499853d6Sryan_chen #define DMA_CTRL_DELAY_SHIFT 8
140499853d6Sryan_chen #define DMA_CTRL_DELAY_MASK 0xf
141beec505fSChin-Ting Kuo #define G6_DMA_CTRL_DELAY_MASK 0xff
142499853d6Sryan_chen #define DMA_CTRL_FREQ_SHIFT 4
143f87fadc3Sryan_chen #define G6_DMA_CTRL_FREQ_SHIFT 16
144f87fadc3Sryan_chen
145499853d6Sryan_chen #define DMA_CTRL_FREQ_MASK 0xf
146499853d6Sryan_chen #define TIMING_MASK(div, delay) \
147499853d6Sryan_chen (((delay & DMA_CTRL_DELAY_MASK) << DMA_CTRL_DELAY_SHIFT) | \
148499853d6Sryan_chen ((div & DMA_CTRL_FREQ_MASK) << DMA_CTRL_FREQ_SHIFT))
149f87fadc3Sryan_chen #define G6_TIMING_MASK(div, delay) \
150beec505fSChin-Ting Kuo (((delay & G6_DMA_CTRL_DELAY_MASK) << DMA_CTRL_DELAY_SHIFT) | \
151f87fadc3Sryan_chen ((div & DMA_CTRL_FREQ_MASK) << G6_DMA_CTRL_FREQ_SHIFT))
152edaef9d2SChin-Ting Kuo #define DAM_CTRL_REQUEST BIT(31)
153edaef9d2SChin-Ting Kuo #define DAM_CTRL_GRANT BIT(30)
154499853d6Sryan_chen #define DMA_CTRL_CALIB BIT(3)
155499853d6Sryan_chen #define DMA_CTRL_CKSUM BIT(2)
156499853d6Sryan_chen #define DMA_CTRL_WRITE BIT(1)
157499853d6Sryan_chen #define DMA_CTRL_ENABLE BIT(0)
158499853d6Sryan_chen
159edaef9d2SChin-Ting Kuo #define DMA_GET_REQ_MAGIC 0xaeed0000
160edaef9d2SChin-Ting Kuo #define DMA_DISCARD_REQ_MAGIC 0xdeea0000
161edaef9d2SChin-Ting Kuo
1620a73b911SChin-Ting Kuo /* for ast2600 setting */
1630a73b911SChin-Ting Kuo #define SPI_3B_AUTO_CLR_REG 0x1e6e2510
1640a73b911SChin-Ting Kuo #define SPI_3B_AUTO_CLR BIT(9)
1650a73b911SChin-Ting Kuo
1660a73b911SChin-Ting Kuo
167499853d6Sryan_chen /*
1680a73b911SChin-Ting Kuo * flash related info
169499853d6Sryan_chen */
170499853d6Sryan_chen struct aspeed_spi_flash {
171499853d6Sryan_chen u8 cs;
172edaef9d2SChin-Ting Kuo /* Initialized when the SPI bus is
173499853d6Sryan_chen * first claimed
174499853d6Sryan_chen */
175edaef9d2SChin-Ting Kuo bool init;
176499853d6Sryan_chen void __iomem *ahb_base; /* AHB Window for this device */
177499853d6Sryan_chen u32 ahb_size; /* AHB Window segment size */
178499853d6Sryan_chen u32 ce_ctrl_user; /* CE Control Register for USER mode */
179499853d6Sryan_chen u32 ce_ctrl_fread; /* CE Control Register for FREAD mode */
180cd800046SChin-Ting Kuo u32 read_iomode;
181cd800046SChin-Ting Kuo u32 write_iomode;
182edaef9d2SChin-Ting Kuo u32 max_freq;
183499853d6Sryan_chen struct spi_flash *spi; /* Associated SPI Flash device */
184499853d6Sryan_chen };
185499853d6Sryan_chen
186bbe907dbSChin-Ting Kuo enum aspeed_spi_dir {
187bbe907dbSChin-Ting Kuo ASPEED_SPI_DIR_IN,
188bbe907dbSChin-Ting Kuo ASPEED_SPI_DIR_OUT,
189bbe907dbSChin-Ting Kuo };
190bbe907dbSChin-Ting Kuo
191bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_CMD(__opcode) \
192bbe907dbSChin-Ting Kuo { \
193bbe907dbSChin-Ting Kuo .opcode = __opcode, \
194bbe907dbSChin-Ting Kuo }
195bbe907dbSChin-Ting Kuo
196bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_ADDR(__nbytes, __val) \
197bbe907dbSChin-Ting Kuo { \
198bbe907dbSChin-Ting Kuo .nbytes = __nbytes, \
199bbe907dbSChin-Ting Kuo .val = __val, \
200bbe907dbSChin-Ting Kuo }
201bbe907dbSChin-Ting Kuo
202bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_NO_ADDR { }
203bbe907dbSChin-Ting Kuo
204bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_DUMMY(__nbytes) \
205bbe907dbSChin-Ting Kuo { \
206bbe907dbSChin-Ting Kuo .nbytes = __nbytes, \
207bbe907dbSChin-Ting Kuo }
208bbe907dbSChin-Ting Kuo
209bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_NO_DUMMY { }
210bbe907dbSChin-Ting Kuo
211bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_DATA_IN(__nbytes, __buf) \
212bbe907dbSChin-Ting Kuo { \
213bbe907dbSChin-Ting Kuo .dir = ASPEED_SPI_DIR_IN, \
214bbe907dbSChin-Ting Kuo .nbytes = __nbytes, \
215bbe907dbSChin-Ting Kuo .buf.in = __buf, \
216bbe907dbSChin-Ting Kuo }
217bbe907dbSChin-Ting Kuo
218bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_DATA_OUT(__nbytes, __buf) \
219bbe907dbSChin-Ting Kuo { \
220bbe907dbSChin-Ting Kuo .dir = ASPEED_SPI_DIR_OUT, \
221bbe907dbSChin-Ting Kuo .nbytes = __nbytes, \
222bbe907dbSChin-Ting Kuo .buf.out = __buf, \
223bbe907dbSChin-Ting Kuo }
224bbe907dbSChin-Ting Kuo
225bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP_NO_DATA { }
226bbe907dbSChin-Ting Kuo
227bbe907dbSChin-Ting Kuo #define ASPEED_SPI_OP(__io_mode, __cmd, __addr, __dummy, __data) \
228bbe907dbSChin-Ting Kuo { \
229bbe907dbSChin-Ting Kuo .io_mode = __io_mode, \
230bbe907dbSChin-Ting Kuo .cmd = __cmd, \
231bbe907dbSChin-Ting Kuo .addr = __addr, \
232bbe907dbSChin-Ting Kuo .dummy = __dummy, \
233bbe907dbSChin-Ting Kuo .data = __data, \
234bbe907dbSChin-Ting Kuo }
235bbe907dbSChin-Ting Kuo
236bbe907dbSChin-Ting Kuo struct aspeed_spi_op {
237bbe907dbSChin-Ting Kuo u32 io_mode;
238bbe907dbSChin-Ting Kuo
239bbe907dbSChin-Ting Kuo struct {
240bbe907dbSChin-Ting Kuo u16 opcode;
241bbe907dbSChin-Ting Kuo } cmd;
242bbe907dbSChin-Ting Kuo
243bbe907dbSChin-Ting Kuo struct {
244bbe907dbSChin-Ting Kuo u8 nbytes;
245bbe907dbSChin-Ting Kuo u32 val;
246bbe907dbSChin-Ting Kuo } addr;
247bbe907dbSChin-Ting Kuo
248bbe907dbSChin-Ting Kuo struct {
249bbe907dbSChin-Ting Kuo u8 nbytes;
250bbe907dbSChin-Ting Kuo } dummy;
251bbe907dbSChin-Ting Kuo
252bbe907dbSChin-Ting Kuo struct {
253bbe907dbSChin-Ting Kuo enum aspeed_spi_dir dir;
254bbe907dbSChin-Ting Kuo unsigned int nbytes;
255bbe907dbSChin-Ting Kuo union {
256bbe907dbSChin-Ting Kuo void *in;
257bbe907dbSChin-Ting Kuo const void *out;
258bbe907dbSChin-Ting Kuo } buf;
259bbe907dbSChin-Ting Kuo } data;
260bbe907dbSChin-Ting Kuo };
261bbe907dbSChin-Ting Kuo
262499853d6Sryan_chen struct aspeed_spi_priv {
263499853d6Sryan_chen struct aspeed_spi_regs *regs;
264499853d6Sryan_chen void __iomem *ahb_base; /* AHB Window for all flash devices */
265499853d6Sryan_chen int new_ver;
266499853d6Sryan_chen u32 ahb_size; /* AHB Window segments size */
267499853d6Sryan_chen ulong hclk_rate; /* AHB clock rate */
268499853d6Sryan_chen u8 num_cs;
269499853d6Sryan_chen bool is_fmc;
270499853d6Sryan_chen
271499853d6Sryan_chen struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
272499853d6Sryan_chen u32 flash_count;
273499853d6Sryan_chen
274499853d6Sryan_chen u8 cmd_buf[16]; /* SPI command in progress */
275499853d6Sryan_chen size_t cmd_len;
276bbe907dbSChin-Ting Kuo u8 *tmp_buf;
277bbe907dbSChin-Ting Kuo int (*spi_exec_op_cmd)(struct aspeed_spi_priv *priv,
278bbe907dbSChin-Ting Kuo struct aspeed_spi_flash *flash,
279bbe907dbSChin-Ting Kuo struct aspeed_spi_op *op);
280499853d6Sryan_chen };
281499853d6Sryan_chen
282bbe907dbSChin-Ting Kuo static u32 aspeed_spi_flash_to_addr(struct aspeed_spi_flash *flash,
283bbe907dbSChin-Ting Kuo const u8 *cmdbuf, unsigned int cmdlen);
284bbe907dbSChin-Ting Kuo
aspeed_spi_get_flash(struct udevice * dev)285499853d6Sryan_chen static struct aspeed_spi_flash *aspeed_spi_get_flash(struct udevice *dev)
286499853d6Sryan_chen {
287499853d6Sryan_chen struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
288499853d6Sryan_chen struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
289499853d6Sryan_chen u8 cs = slave_plat->cs;
290499853d6Sryan_chen
291499853d6Sryan_chen if (cs >= priv->flash_count) {
292499853d6Sryan_chen pr_err("invalid CS %u\n", cs);
293499853d6Sryan_chen return NULL;
294499853d6Sryan_chen }
295499853d6Sryan_chen
296499853d6Sryan_chen return &priv->flashes[cs];
297499853d6Sryan_chen }
298499853d6Sryan_chen
aspeed_g6_spi_hclk_divisor(struct aspeed_spi_priv * priv,u32 max_hz)299ac86fa8bSryan_chen static u32 aspeed_g6_spi_hclk_divisor(struct aspeed_spi_priv *priv, u32 max_hz)
300499853d6Sryan_chen {
3017d182336Sryan_chen u32 hclk_rate = priv->hclk_rate;
302499853d6Sryan_chen /* HCLK/1 .. HCLK/16 */
303499853d6Sryan_chen const u8 hclk_masks[] = {
304499853d6Sryan_chen 15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0
305499853d6Sryan_chen };
306edaef9d2SChin-Ting Kuo u8 hclk_div = 0x4; /* default value */
307edaef9d2SChin-Ting Kuo bool found = false;
308f87fadc3Sryan_chen u32 i, j = 0;
309499853d6Sryan_chen
310edaef9d2SChin-Ting Kuo /* FMC/SPIR10[27:24] */
31141629eddSChin-Ting Kuo for (j = 0; j < 0xf; j++) {
3127d182336Sryan_chen for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
313edaef9d2SChin-Ting Kuo if (i == 0 && j == 0)
314edaef9d2SChin-Ting Kuo continue;
315f87fadc3Sryan_chen
316edaef9d2SChin-Ting Kuo if ((hclk_rate / ((i + 1) + j * 16)) <= max_hz) {
317edaef9d2SChin-Ting Kuo found = 1;
3187d182336Sryan_chen break;
3197d182336Sryan_chen }
3207d182336Sryan_chen }
321edaef9d2SChin-Ting Kuo
322edaef9d2SChin-Ting Kuo if (found)
323f87fadc3Sryan_chen break;
3247d182336Sryan_chen }
325499853d6Sryan_chen
326f87fadc3Sryan_chen debug("hclk=%d required=%d h_div %d, divisor is %d (mask %x) speed=%d\n",
327edaef9d2SChin-Ting Kuo hclk_rate, max_hz, j, i + 1, hclk_masks[i], hclk_rate / (i + 1 + j * 16));
3287d182336Sryan_chen
329edaef9d2SChin-Ting Kuo hclk_div = ((j << 4) | hclk_masks[i]);
3307d182336Sryan_chen
331edaef9d2SChin-Ting Kuo return hclk_div;
332ac86fa8bSryan_chen }
333ac86fa8bSryan_chen
aspeed_spi_hclk_divisor(struct aspeed_spi_priv * priv,u32 max_hz)334ac86fa8bSryan_chen static u32 aspeed_spi_hclk_divisor(struct aspeed_spi_priv *priv, u32 max_hz)
335ac86fa8bSryan_chen {
336ac86fa8bSryan_chen u32 hclk_rate = priv->hclk_rate;
337ac86fa8bSryan_chen /* HCLK/1 .. HCLK/16 */
338ac86fa8bSryan_chen const u8 hclk_masks[] = {
339ac86fa8bSryan_chen 15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0
340ac86fa8bSryan_chen };
341d32338fdSryan_chen u32 i;
342ac86fa8bSryan_chen u32 hclk_div_setting = 0;
343ac86fa8bSryan_chen
344499853d6Sryan_chen for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
345499853d6Sryan_chen if (max_hz >= (hclk_rate / (i + 1)))
346499853d6Sryan_chen break;
347499853d6Sryan_chen }
348499853d6Sryan_chen debug("hclk=%d required=%d divisor is %d (mask %x) speed=%d\n",
349499853d6Sryan_chen hclk_rate, max_hz, i + 1, hclk_masks[i], hclk_rate / (i + 1));
350499853d6Sryan_chen
3517d182336Sryan_chen hclk_div_setting = hclk_masks[i];
3527d182336Sryan_chen
3537d182336Sryan_chen return hclk_div_setting;
354499853d6Sryan_chen }
355499853d6Sryan_chen
356499853d6Sryan_chen /*
357499853d6Sryan_chen * Use some address/size under the first flash device CE0
358499853d6Sryan_chen */
aspeed_spi_fmc_checksum(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,u8 div,u8 delay)359edaef9d2SChin-Ting Kuo static u32 aspeed_spi_fmc_checksum(struct aspeed_spi_priv *priv,
360edaef9d2SChin-Ting Kuo struct aspeed_spi_flash *flash,
361edaef9d2SChin-Ting Kuo u8 div, u8 delay)
362499853d6Sryan_chen {
363edaef9d2SChin-Ting Kuo u32 flash_addr = (u32)flash->ahb_base + 0x10000;
364499853d6Sryan_chen u32 dma_ctrl;
365499853d6Sryan_chen u32 checksum;
366499853d6Sryan_chen
367499853d6Sryan_chen writel(flash_addr, &priv->regs->dma_flash_addr);
368edaef9d2SChin-Ting Kuo writel(FLASH_CALIBRATION_LEN, &priv->regs->dma_len);
369499853d6Sryan_chen
370499853d6Sryan_chen /*
371499853d6Sryan_chen * When doing calibration, the SPI clock rate in the CE0
372499853d6Sryan_chen * Control Register and the data input delay cycles in the
373499853d6Sryan_chen * Read Timing Compensation Register are replaced by bit[11:4].
374499853d6Sryan_chen */
375499853d6Sryan_chen dma_ctrl = DMA_CTRL_ENABLE | DMA_CTRL_CKSUM | DMA_CTRL_CALIB |
376499853d6Sryan_chen TIMING_MASK(div, delay);
377edaef9d2SChin-Ting Kuo
378499853d6Sryan_chen writel(dma_ctrl, &priv->regs->dma_ctrl);
379499853d6Sryan_chen while (!(readl(&priv->regs->intr_ctrl) & INTR_CTRL_DMA_STATUS))
380499853d6Sryan_chen ;
381499853d6Sryan_chen
382499853d6Sryan_chen writel(0x0, &priv->regs->intr_ctrl);
383499853d6Sryan_chen
384499853d6Sryan_chen checksum = readl(&priv->regs->dma_checksum);
385499853d6Sryan_chen
386499853d6Sryan_chen writel(0x0, &priv->regs->dma_ctrl);
387499853d6Sryan_chen return checksum;
388499853d6Sryan_chen }
389499853d6Sryan_chen
390edaef9d2SChin-Ting Kuo /*
391edaef9d2SChin-Ting Kuo * Use some address/size under the first flash device CE0
392499853d6Sryan_chen */
aspeed_g6_spi_fmc_checksum(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,u8 div,u8 delay)393edaef9d2SChin-Ting Kuo static u32 aspeed_g6_spi_fmc_checksum(struct aspeed_spi_priv *priv,
394edaef9d2SChin-Ting Kuo struct aspeed_spi_flash *flash,
395edaef9d2SChin-Ting Kuo u8 div, u8 delay)
396edaef9d2SChin-Ting Kuo {
397edaef9d2SChin-Ting Kuo u32 flash_addr = (u32)flash->ahb_base;
398edaef9d2SChin-Ting Kuo u32 dma_ctrl;
399edaef9d2SChin-Ting Kuo u32 checksum;
400edaef9d2SChin-Ting Kuo
401edaef9d2SChin-Ting Kuo writel(DMA_GET_REQ_MAGIC, &priv->regs->dma_ctrl);
402edaef9d2SChin-Ting Kuo if (readl(&priv->regs->dma_ctrl) & DAM_CTRL_REQUEST) {
403edaef9d2SChin-Ting Kuo while (!(readl(&priv->regs->dma_ctrl) & DAM_CTRL_GRANT))
404edaef9d2SChin-Ting Kuo ;
405edaef9d2SChin-Ting Kuo }
406edaef9d2SChin-Ting Kuo
407edaef9d2SChin-Ting Kuo writel(flash_addr, &priv->regs->dma_flash_addr);
408edaef9d2SChin-Ting Kuo writel(FLASH_CALIBRATION_LEN, &priv->regs->dma_len);
409edaef9d2SChin-Ting Kuo
410edaef9d2SChin-Ting Kuo /*
411edaef9d2SChin-Ting Kuo * When doing calibration, the SPI clock rate in the control
412edaef9d2SChin-Ting Kuo * register and the data input delay cycles in the
413edaef9d2SChin-Ting Kuo * read timing compensation register are replaced by bit[11:4].
414edaef9d2SChin-Ting Kuo */
415edaef9d2SChin-Ting Kuo dma_ctrl = DMA_CTRL_ENABLE | DMA_CTRL_CKSUM | DMA_CTRL_CALIB |
416edaef9d2SChin-Ting Kuo G6_TIMING_MASK(div, delay);
417edaef9d2SChin-Ting Kuo
418edaef9d2SChin-Ting Kuo writel(dma_ctrl, &priv->regs->dma_ctrl);
419edaef9d2SChin-Ting Kuo while (!(readl(&priv->regs->intr_ctrl) & INTR_CTRL_DMA_STATUS))
420edaef9d2SChin-Ting Kuo ;
421edaef9d2SChin-Ting Kuo
422edaef9d2SChin-Ting Kuo checksum = readl(&priv->regs->dma_checksum);
423edaef9d2SChin-Ting Kuo
424edaef9d2SChin-Ting Kuo writel(0x0, &priv->regs->intr_ctrl);
425edaef9d2SChin-Ting Kuo writel(0x0, &priv->regs->dma_ctrl);
426edaef9d2SChin-Ting Kuo writel(DMA_DISCARD_REQ_MAGIC, &priv->regs->dma_ctrl);
427edaef9d2SChin-Ting Kuo
428edaef9d2SChin-Ting Kuo return checksum;
429edaef9d2SChin-Ting Kuo }
430edaef9d2SChin-Ting Kuo
aspeed_spi_read_checksum(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,u8 div,u8 delay)431edaef9d2SChin-Ting Kuo static u32 aspeed_spi_read_checksum(struct aspeed_spi_priv *priv,
432edaef9d2SChin-Ting Kuo struct aspeed_spi_flash *flash,
433edaef9d2SChin-Ting Kuo u8 div, u8 delay)
434edaef9d2SChin-Ting Kuo {
435edaef9d2SChin-Ting Kuo if (priv->new_ver)
436edaef9d2SChin-Ting Kuo return aspeed_g6_spi_fmc_checksum(priv, flash, div, delay);
437edaef9d2SChin-Ting Kuo
438edaef9d2SChin-Ting Kuo /* for AST2500, */
439499853d6Sryan_chen if (!priv->is_fmc) {
440499853d6Sryan_chen pr_warn("No timing calibration support for SPI controllers");
441499853d6Sryan_chen return 0xbadc0de;
442499853d6Sryan_chen }
443499853d6Sryan_chen
444edaef9d2SChin-Ting Kuo return aspeed_spi_fmc_checksum(priv, flash, div, delay);
445499853d6Sryan_chen }
446499853d6Sryan_chen
447499853d6Sryan_chen #define TIMING_DELAY_DI_4NS BIT(3)
448499853d6Sryan_chen #define TIMING_DELAY_HCYCLE_MAX 5
449499853d6Sryan_chen
450edaef9d2SChin-Ting Kuo /*
451edaef9d2SChin-Ting Kuo * Check whether the data is not all 0 or 1 in order to
452edaef9d2SChin-Ting Kuo * avoid calibriate umount spi-flash.
453edaef9d2SChin-Ting Kuo */
aspeed_spi_calibriation_enable(const u8 * buf,u32 sz)454edaef9d2SChin-Ting Kuo static bool aspeed_spi_calibriation_enable(const u8 *buf, u32 sz)
455499853d6Sryan_chen {
456edaef9d2SChin-Ting Kuo const u32 *buf_32 = (const u32 *)buf;
457edaef9d2SChin-Ting Kuo u32 i;
458edaef9d2SChin-Ting Kuo u32 valid_count = 0;
459edaef9d2SChin-Ting Kuo
460edaef9d2SChin-Ting Kuo for (i = 0; i < (sz / 4); i++) {
461edaef9d2SChin-Ting Kuo if (buf_32[i] != 0 && buf_32[i] != 0xffffffff)
462edaef9d2SChin-Ting Kuo valid_count++;
463edaef9d2SChin-Ting Kuo if (valid_count > 100)
464edaef9d2SChin-Ting Kuo return true;
465edaef9d2SChin-Ting Kuo }
466edaef9d2SChin-Ting Kuo
467edaef9d2SChin-Ting Kuo return false;
468edaef9d2SChin-Ting Kuo }
469edaef9d2SChin-Ting Kuo
get_mid_point_of_longest_one(u8 * buf,u32 len)470edaef9d2SChin-Ting Kuo static int get_mid_point_of_longest_one(u8 *buf, u32 len)
471edaef9d2SChin-Ting Kuo {
472edaef9d2SChin-Ting Kuo int i;
473edaef9d2SChin-Ting Kuo int start = 0, mid_point = 0;
474edaef9d2SChin-Ting Kuo int max_cnt = 0, cnt = 0;
475edaef9d2SChin-Ting Kuo
476edaef9d2SChin-Ting Kuo for (i = 0; i < len; i++) {
477edaef9d2SChin-Ting Kuo if (buf[i] == 1) {
478edaef9d2SChin-Ting Kuo cnt++;
479edaef9d2SChin-Ting Kuo } else {
480edaef9d2SChin-Ting Kuo cnt = 0;
481edaef9d2SChin-Ting Kuo start = i;
482edaef9d2SChin-Ting Kuo }
483edaef9d2SChin-Ting Kuo
484edaef9d2SChin-Ting Kuo if (max_cnt < cnt) {
485edaef9d2SChin-Ting Kuo max_cnt = cnt;
486edaef9d2SChin-Ting Kuo mid_point = start + (cnt / 2);
487edaef9d2SChin-Ting Kuo }
488edaef9d2SChin-Ting Kuo }
489edaef9d2SChin-Ting Kuo
490edaef9d2SChin-Ting Kuo /*
491edaef9d2SChin-Ting Kuo * In order to get a stable SPI read timing,
492edaef9d2SChin-Ting Kuo * abandon the result if the length of longest
493edaef9d2SChin-Ting Kuo * consecutive good points is too short.
494edaef9d2SChin-Ting Kuo */
495edaef9d2SChin-Ting Kuo if (max_cnt < 4)
496edaef9d2SChin-Ting Kuo return -1;
497edaef9d2SChin-Ting Kuo
498edaef9d2SChin-Ting Kuo return mid_point;
499edaef9d2SChin-Ting Kuo }
500edaef9d2SChin-Ting Kuo
aspeed_spi_timing_calibration(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash)501edaef9d2SChin-Ting Kuo static int aspeed_spi_timing_calibration(struct aspeed_spi_priv *priv,
502edaef9d2SChin-Ting Kuo struct aspeed_spi_flash *flash)
503edaef9d2SChin-Ting Kuo {
504edaef9d2SChin-Ting Kuo u32 cs = flash->cs;
505499853d6Sryan_chen /* HCLK/5 .. HCLK/1 */
506499853d6Sryan_chen const u8 hclk_masks[] = {13, 6, 14, 7, 15};
507beec505fSChin-Ting Kuo u32 timing_reg;
508499853d6Sryan_chen u32 checksum, gold_checksum;
509edaef9d2SChin-Ting Kuo int i;
510edaef9d2SChin-Ting Kuo u32 hcycle, delay_ns;
511edaef9d2SChin-Ting Kuo u32 final_delay = 0;
512edaef9d2SChin-Ting Kuo u32 hclk_div = 0;
513edaef9d2SChin-Ting Kuo u32 max_freq = flash->max_freq;
514edaef9d2SChin-Ting Kuo u32 reg_val;
515edaef9d2SChin-Ting Kuo u8 *tmp_buf = NULL;
516edaef9d2SChin-Ting Kuo u8 *calib_res = NULL;
517edaef9d2SChin-Ting Kuo int calib_point;
518edaef9d2SChin-Ting Kuo bool pass;
519beec505fSChin-Ting Kuo
520edaef9d2SChin-Ting Kuo if (priv->new_ver) {
521edaef9d2SChin-Ting Kuo timing_reg = readl(&priv->regs->timings + cs);
522edaef9d2SChin-Ting Kuo if (timing_reg != 0)
523edaef9d2SChin-Ting Kuo return 0;
524edaef9d2SChin-Ting Kuo
525edaef9d2SChin-Ting Kuo /*
526edaef9d2SChin-Ting Kuo * use the related low frequency to get check calibration data
527edaef9d2SChin-Ting Kuo * and get golden data.
528edaef9d2SChin-Ting Kuo */
529edaef9d2SChin-Ting Kuo reg_val = flash->ce_ctrl_fread & CE_CTRL_FREQ_MASK;
530edaef9d2SChin-Ting Kuo writel(reg_val, &priv->regs->ce_ctrl[cs]);
531edaef9d2SChin-Ting Kuo tmp_buf = (u8 *)malloc(FLASH_CALIBRATION_LEN);
532edaef9d2SChin-Ting Kuo if (!tmp_buf)
533edaef9d2SChin-Ting Kuo return -ENOMEM;
534edaef9d2SChin-Ting Kuo
535edaef9d2SChin-Ting Kuo memcpy_fromio(tmp_buf, flash->ahb_base, FLASH_CALIBRATION_LEN);
536edaef9d2SChin-Ting Kuo if (!aspeed_spi_calibriation_enable(tmp_buf, FLASH_CALIBRATION_LEN)) {
537edaef9d2SChin-Ting Kuo debug("flash data is monotonous, skip calibration.\n");
538edaef9d2SChin-Ting Kuo goto no_calib;
539edaef9d2SChin-Ting Kuo }
540edaef9d2SChin-Ting Kuo
541edaef9d2SChin-Ting Kuo /* Compute reference checksum at lowest freq HCLK/16 */
542edaef9d2SChin-Ting Kuo gold_checksum = aspeed_spi_read_checksum(priv, flash, 0, 0);
543edaef9d2SChin-Ting Kuo
544edaef9d2SChin-Ting Kuo /*
545edaef9d2SChin-Ting Kuo * allocate a space to record calibration result for
546edaef9d2SChin-Ting Kuo * different timing compensation with fixed
547edaef9d2SChin-Ting Kuo * HCLK division.
548edaef9d2SChin-Ting Kuo */
549edaef9d2SChin-Ting Kuo calib_res = (u8 *)malloc(6 * 17);
550edaef9d2SChin-Ting Kuo if (!calib_res) {
551edaef9d2SChin-Ting Kuo free(tmp_buf);
552edaef9d2SChin-Ting Kuo return -ENOMEM;
553edaef9d2SChin-Ting Kuo }
554edaef9d2SChin-Ting Kuo
555edaef9d2SChin-Ting Kuo /* from HCLK/2 to HCLK/5 */
556edaef9d2SChin-Ting Kuo for (i = 0; i < ARRAY_SIZE(hclk_masks) - 1; i++) {
557edaef9d2SChin-Ting Kuo if (priv->hclk_rate / (i + 2) > max_freq) {
558edaef9d2SChin-Ting Kuo debug("skipping freq %ld\n", priv->hclk_rate / (i + 2));
559edaef9d2SChin-Ting Kuo continue;
560edaef9d2SChin-Ting Kuo }
561edaef9d2SChin-Ting Kuo
562edaef9d2SChin-Ting Kuo max_freq = (u32)priv->hclk_rate / (i + 2);
563edaef9d2SChin-Ting Kuo
564edaef9d2SChin-Ting Kuo memset(calib_res, 0x0, 6 * 17);
565edaef9d2SChin-Ting Kuo for (hcycle = 0; hcycle <= 5; hcycle++) {
566edaef9d2SChin-Ting Kuo /* increase DI delay by the step of 0.5ns */
567edaef9d2SChin-Ting Kuo debug("Delay Enable : hcycle %x\n", hcycle);
568edaef9d2SChin-Ting Kuo for (delay_ns = 0; delay_ns <= 0xf; delay_ns++) {
569edaef9d2SChin-Ting Kuo checksum = aspeed_g6_spi_fmc_checksum(priv, flash,
570edaef9d2SChin-Ting Kuo hclk_masks[3 - i],
571edaef9d2SChin-Ting Kuo TIMING_DELAY_DI_4NS | hcycle | (delay_ns << 4));
572edaef9d2SChin-Ting Kuo pass = (checksum == gold_checksum);
573edaef9d2SChin-Ting Kuo calib_res[hcycle * 17 + delay_ns] = pass;
574edaef9d2SChin-Ting Kuo debug("HCLK/%d, %d HCLK cycle, %d delay_ns : %s\n",
575edaef9d2SChin-Ting Kuo i + 2, hcycle, delay_ns, pass ? "PASS" : "FAIL");
576edaef9d2SChin-Ting Kuo }
577edaef9d2SChin-Ting Kuo }
578edaef9d2SChin-Ting Kuo
579edaef9d2SChin-Ting Kuo calib_point = get_mid_point_of_longest_one(calib_res, 6 * 17);
580edaef9d2SChin-Ting Kuo if (calib_point < 0) {
581edaef9d2SChin-Ting Kuo debug("cannot get good calibration point.\n");
582edaef9d2SChin-Ting Kuo continue;
583edaef9d2SChin-Ting Kuo }
584edaef9d2SChin-Ting Kuo
585edaef9d2SChin-Ting Kuo hcycle = calib_point / 17;
586edaef9d2SChin-Ting Kuo delay_ns = calib_point % 17;
587edaef9d2SChin-Ting Kuo debug("final hcycle: %d, delay_ns: %d\n", hcycle, delay_ns);
588edaef9d2SChin-Ting Kuo
589edaef9d2SChin-Ting Kuo final_delay = (TIMING_DELAY_DI_4NS | hcycle | (delay_ns << 4)) << (i * 8);
590edaef9d2SChin-Ting Kuo writel(final_delay, &priv->regs->timings + cs);
591edaef9d2SChin-Ting Kuo break;
592edaef9d2SChin-Ting Kuo }
593edaef9d2SChin-Ting Kuo
594edaef9d2SChin-Ting Kuo no_calib:
595edaef9d2SChin-Ting Kuo hclk_div = aspeed_g6_spi_hclk_divisor(priv, max_freq);
596edaef9d2SChin-Ting Kuo /* configure SPI clock frequency */
597edaef9d2SChin-Ting Kuo reg_val = readl(&priv->regs->ce_ctrl[cs]);
598edaef9d2SChin-Ting Kuo reg_val = (reg_val & CE_CTRL_FREQ_MASK) | CE_G6_CTRL_CLOCK_FREQ(hclk_div);
599edaef9d2SChin-Ting Kuo writel(reg_val, &priv->regs->ce_ctrl[cs]);
600edaef9d2SChin-Ting Kuo
601edaef9d2SChin-Ting Kuo /* add clock setting info for CE ctrl setting */
602edaef9d2SChin-Ting Kuo flash->ce_ctrl_user =
603edaef9d2SChin-Ting Kuo (flash->ce_ctrl_user & CE_CTRL_FREQ_MASK) | CE_G6_CTRL_CLOCK_FREQ(hclk_div);
604edaef9d2SChin-Ting Kuo flash->ce_ctrl_fread =
605edaef9d2SChin-Ting Kuo (flash->ce_ctrl_fread & CE_CTRL_FREQ_MASK) | CE_G6_CTRL_CLOCK_FREQ(hclk_div);
606edaef9d2SChin-Ting Kuo
607edaef9d2SChin-Ting Kuo debug("cs: %d, freq: %dMHz\n", cs, max_freq / 1000000);
608edaef9d2SChin-Ting Kuo
609edaef9d2SChin-Ting Kuo if (tmp_buf)
610edaef9d2SChin-Ting Kuo free(tmp_buf);
611edaef9d2SChin-Ting Kuo if (calib_res)
612edaef9d2SChin-Ting Kuo free(calib_res);
613edaef9d2SChin-Ting Kuo } else {
614beec505fSChin-Ting Kuo /* Use the ctrl setting in aspeed_spi_flash_init() to
615beec505fSChin-Ting Kuo * implement calibration process.
616beec505fSChin-Ting Kuo */
617beec505fSChin-Ting Kuo timing_reg = readl(&priv->regs->timings);
618beec505fSChin-Ting Kuo if (timing_reg != 0)
619beec505fSChin-Ting Kuo return 0;
620499853d6Sryan_chen
621499853d6Sryan_chen /* Compute reference checksum at lowest freq HCLK/16 */
622edaef9d2SChin-Ting Kuo gold_checksum = aspeed_spi_read_checksum(priv, flash, 0, 0);
623499853d6Sryan_chen
624499853d6Sryan_chen for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
625499853d6Sryan_chen u32 hdiv = 5 - i;
626499853d6Sryan_chen u32 hshift = (hdiv - 1) << 2;
627499853d6Sryan_chen bool pass = false;
628499853d6Sryan_chen u8 delay;
629499853d6Sryan_chen
630edaef9d2SChin-Ting Kuo if (priv->hclk_rate / hdiv > flash->max_freq) {
631499853d6Sryan_chen debug("skipping freq %ld\n", priv->hclk_rate / hdiv);
632499853d6Sryan_chen continue;
633499853d6Sryan_chen }
634499853d6Sryan_chen
635499853d6Sryan_chen /* Increase HCLK cycles until read succeeds */
636499853d6Sryan_chen for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
637499853d6Sryan_chen /* Try first with a 4ns DI delay */
638499853d6Sryan_chen delay = TIMING_DELAY_DI_4NS | hcycle;
639edaef9d2SChin-Ting Kuo checksum = aspeed_spi_read_checksum(priv, flash, hclk_masks[i],
640499853d6Sryan_chen delay);
641499853d6Sryan_chen pass = (checksum == gold_checksum);
642499853d6Sryan_chen debug(" HCLK/%d, 4ns DI delay, %d HCLK cycle : %s\n",
643499853d6Sryan_chen hdiv, hcycle, pass ? "PASS" : "FAIL");
644499853d6Sryan_chen
645499853d6Sryan_chen /* Try again with more HCLK cycles */
646499853d6Sryan_chen if (!pass)
647499853d6Sryan_chen continue;
648499853d6Sryan_chen
649499853d6Sryan_chen /* Try without the 4ns DI delay */
650499853d6Sryan_chen delay = hcycle;
651edaef9d2SChin-Ting Kuo checksum = aspeed_spi_read_checksum(priv, flash, hclk_masks[i],
652499853d6Sryan_chen delay);
653499853d6Sryan_chen pass = (checksum == gold_checksum);
654499853d6Sryan_chen debug(" HCLK/%d, no DI delay, %d HCLK cycle : %s\n",
655499853d6Sryan_chen hdiv, hcycle, pass ? "PASS" : "FAIL");
656499853d6Sryan_chen
657499853d6Sryan_chen /* All good for this freq */
658499853d6Sryan_chen if (pass)
659499853d6Sryan_chen break;
660499853d6Sryan_chen }
661499853d6Sryan_chen
662499853d6Sryan_chen if (pass) {
663499853d6Sryan_chen timing_reg &= ~(0xfu << hshift);
664499853d6Sryan_chen timing_reg |= delay << hshift;
665499853d6Sryan_chen }
666499853d6Sryan_chen }
667beec505fSChin-Ting Kuo
668499853d6Sryan_chen debug("Read Timing Compensation set to 0x%08x\n", timing_reg);
669499853d6Sryan_chen writel(timing_reg, &priv->regs->timings);
670edaef9d2SChin-Ting Kuo }
671499853d6Sryan_chen
672499853d6Sryan_chen return 0;
673499853d6Sryan_chen }
674499853d6Sryan_chen
aspeed_spi_controller_init(struct aspeed_spi_priv * priv)675499853d6Sryan_chen static int aspeed_spi_controller_init(struct aspeed_spi_priv *priv)
676499853d6Sryan_chen {
677beec505fSChin-Ting Kuo int cs;
678499853d6Sryan_chen
679499853d6Sryan_chen /*
680499853d6Sryan_chen * Enable write on all flash devices as USER command mode
681499853d6Sryan_chen * requires it.
682499853d6Sryan_chen */
683499853d6Sryan_chen setbits_le32(&priv->regs->conf,
684499853d6Sryan_chen CONF_ENABLE_W2 | CONF_ENABLE_W1 | CONF_ENABLE_W0);
685499853d6Sryan_chen
686499853d6Sryan_chen /*
687499853d6Sryan_chen * Set safe default settings for each device. These will be
688499853d6Sryan_chen * tuned after the SPI flash devices are probed.
689499853d6Sryan_chen */
690da83dd7eSryan_chen if (priv->new_ver) {
691499853d6Sryan_chen for (cs = 0; cs < priv->flash_count; cs++) {
692499853d6Sryan_chen struct aspeed_spi_flash *flash = &priv->flashes[cs];
693d32338fdSryan_chen u32 addr_config = 0;
694499853d6Sryan_chen switch(cs) {
695da83dd7eSryan_chen case 0:
6968fbbfa7dSChin-Ting Kuo flash->ahb_base = priv->ahb_base;
697d32338fdSryan_chen debug("cs0 mem-map : %x\n", (u32)flash->ahb_base);
698da83dd7eSryan_chen break;
699499853d6Sryan_chen case 1:
7008fbbfa7dSChin-Ting Kuo flash->ahb_base = priv->flashes[0].ahb_base + 0x4000000; /* cs0 + 64MB */
7018fbbfa7dSChin-Ting Kuo debug("cs1 mem-map : %x end %x\n",
7028fbbfa7dSChin-Ting Kuo (u32)flash->ahb_base, (u32)flash->ahb_base + 0x4000000);
703499853d6Sryan_chen break;
704499853d6Sryan_chen case 2:
7058fbbfa7dSChin-Ting Kuo flash->ahb_base = priv->flashes[0].ahb_base + 0x4000000 * 2; /* cs0 + 128MB : use 64MB */
7068fbbfa7dSChin-Ting Kuo debug("cs2 mem-map : %x end %x\n",
7078fbbfa7dSChin-Ting Kuo (u32)flash->ahb_base, (u32)flash->ahb_base + 0x4000000);
708499853d6Sryan_chen break;
709499853d6Sryan_chen }
7108fbbfa7dSChin-Ting Kuo addr_config =
7118fbbfa7dSChin-Ting Kuo G6_SEGMENT_ADDR_VALUE((u32)flash->ahb_base, (u32)flash->ahb_base + 0x4000000);
7128fbbfa7dSChin-Ting Kuo writel(addr_config, &priv->regs->segment_addr[cs]);
713da83dd7eSryan_chen flash->cs = cs;
714da83dd7eSryan_chen flash->ce_ctrl_user = CE_CTRL_USERMODE;
715da83dd7eSryan_chen flash->ce_ctrl_fread = CE_CTRL_READMODE;
716499853d6Sryan_chen }
717da83dd7eSryan_chen } else {
718da83dd7eSryan_chen for (cs = 0; cs < priv->flash_count; cs++) {
719da83dd7eSryan_chen struct aspeed_spi_flash *flash = &priv->flashes[cs];
720da83dd7eSryan_chen u32 seg_addr = readl(&priv->regs->segment_addr[cs]);
721499853d6Sryan_chen /*
722499853d6Sryan_chen * The start address of the AHB window of CE0 is
723499853d6Sryan_chen * read-only and is the same as the address of the
724499853d6Sryan_chen * overall AHB window of the controller for all flash
725499853d6Sryan_chen * devices.
726499853d6Sryan_chen */
727499853d6Sryan_chen flash->ahb_base = cs ? (void *)SEGMENT_ADDR_START(seg_addr) :
728499853d6Sryan_chen priv->ahb_base;
729499853d6Sryan_chen
730499853d6Sryan_chen flash->cs = cs;
731499853d6Sryan_chen flash->ce_ctrl_user = CE_CTRL_USERMODE;
732499853d6Sryan_chen flash->ce_ctrl_fread = CE_CTRL_READMODE;
733499853d6Sryan_chen }
734da83dd7eSryan_chen }
735499853d6Sryan_chen return 0;
736499853d6Sryan_chen }
737499853d6Sryan_chen
aspeed_spi_read_from_ahb(void __iomem * ahb_base,void * buf,size_t len)738499853d6Sryan_chen static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf,
739499853d6Sryan_chen size_t len)
740499853d6Sryan_chen {
741499853d6Sryan_chen size_t offset = 0;
742499853d6Sryan_chen
743499853d6Sryan_chen if (!((uintptr_t)buf % 4)) {
744499853d6Sryan_chen readsl(ahb_base, buf, len >> 2);
745499853d6Sryan_chen offset = len & ~0x3;
746499853d6Sryan_chen len -= offset;
747499853d6Sryan_chen }
748499853d6Sryan_chen readsb(ahb_base, (u8 *)buf + offset, len);
749499853d6Sryan_chen
750499853d6Sryan_chen return 0;
751499853d6Sryan_chen }
752499853d6Sryan_chen
aspeed_spi_write_to_ahb(void __iomem * ahb_base,const void * buf,size_t len)753499853d6Sryan_chen static int aspeed_spi_write_to_ahb(void __iomem *ahb_base, const void *buf,
754499853d6Sryan_chen size_t len)
755499853d6Sryan_chen {
756499853d6Sryan_chen size_t offset = 0;
757499853d6Sryan_chen
758499853d6Sryan_chen if (!((uintptr_t)buf % 4)) {
759499853d6Sryan_chen writesl(ahb_base, buf, len >> 2);
760499853d6Sryan_chen offset = len & ~0x3;
761499853d6Sryan_chen len -= offset;
762499853d6Sryan_chen }
763499853d6Sryan_chen writesb(ahb_base, (u8 *)buf + offset, len);
764499853d6Sryan_chen
765499853d6Sryan_chen return 0;
766499853d6Sryan_chen }
767499853d6Sryan_chen
aspeed_spi_start_user(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash)768499853d6Sryan_chen static void aspeed_spi_start_user(struct aspeed_spi_priv *priv,
769499853d6Sryan_chen struct aspeed_spi_flash *flash)
770499853d6Sryan_chen {
771499853d6Sryan_chen u32 ctrl_reg = flash->ce_ctrl_user | CE_CTRL_STOP_ACTIVE;
772499853d6Sryan_chen
773499853d6Sryan_chen /* Deselect CS and set USER command mode */
774499853d6Sryan_chen writel(ctrl_reg, &priv->regs->ce_ctrl[flash->cs]);
775499853d6Sryan_chen
776499853d6Sryan_chen /* Select CS */
777499853d6Sryan_chen clrbits_le32(&priv->regs->ce_ctrl[flash->cs], CE_CTRL_STOP_ACTIVE);
778499853d6Sryan_chen }
779499853d6Sryan_chen
aspeed_spi_stop_user(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash)780499853d6Sryan_chen static void aspeed_spi_stop_user(struct aspeed_spi_priv *priv,
781499853d6Sryan_chen struct aspeed_spi_flash *flash)
782499853d6Sryan_chen {
783499853d6Sryan_chen /* Deselect CS first */
784499853d6Sryan_chen setbits_le32(&priv->regs->ce_ctrl[flash->cs], CE_CTRL_STOP_ACTIVE);
785499853d6Sryan_chen
786499853d6Sryan_chen /* Restore default command mode */
787499853d6Sryan_chen writel(flash->ce_ctrl_fread, &priv->regs->ce_ctrl[flash->cs]);
788499853d6Sryan_chen }
789499853d6Sryan_chen
aspeed_spi_read_reg(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,u8 opcode,u8 * read_buf,int len)790499853d6Sryan_chen static int aspeed_spi_read_reg(struct aspeed_spi_priv *priv,
791499853d6Sryan_chen struct aspeed_spi_flash *flash,
792499853d6Sryan_chen u8 opcode, u8 *read_buf, int len)
793499853d6Sryan_chen {
794bbe907dbSChin-Ting Kuo struct aspeed_spi_op op =
795bbe907dbSChin-Ting Kuo ASPEED_SPI_OP(0,
796bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_CMD(opcode),
797bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_ADDR(0, 0),
798bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DUMMY(0),
799bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DATA_IN(len, read_buf));
800bbe907dbSChin-Ting Kuo
801bbe907dbSChin-Ting Kuo if (priv->spi_exec_op_cmd) {
802bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd(priv, flash, &op);
803bbe907dbSChin-Ting Kuo return 0;
804bbe907dbSChin-Ting Kuo }
805bbe907dbSChin-Ting Kuo
806499853d6Sryan_chen aspeed_spi_start_user(priv, flash);
807499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, &opcode, 1);
808499853d6Sryan_chen aspeed_spi_read_from_ahb(flash->ahb_base, read_buf, len);
809499853d6Sryan_chen aspeed_spi_stop_user(priv, flash);
810499853d6Sryan_chen
811499853d6Sryan_chen return 0;
812499853d6Sryan_chen }
813499853d6Sryan_chen
aspeed_spi_write_reg(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,u8 opcode,const u8 * write_buf,int len)814499853d6Sryan_chen static int aspeed_spi_write_reg(struct aspeed_spi_priv *priv,
815499853d6Sryan_chen struct aspeed_spi_flash *flash,
816499853d6Sryan_chen u8 opcode, const u8 *write_buf, int len)
817499853d6Sryan_chen {
818d6164108SChin-Ting Kuo int i;
819bbe907dbSChin-Ting Kuo struct aspeed_spi_op op =
820bbe907dbSChin-Ting Kuo ASPEED_SPI_OP(0,
821bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_CMD(opcode),
822bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_ADDR(0, 0),
823bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DUMMY(0),
824bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DATA_OUT(len, write_buf));
825bbe907dbSChin-Ting Kuo
826bbe907dbSChin-Ting Kuo if (priv->spi_exec_op_cmd) {
827d6164108SChin-Ting Kuo if (opcode == SPINOR_OP_BE_4K || opcode == SPINOR_OP_BE_4K_4B ||
828d6164108SChin-Ting Kuo opcode == SPINOR_OP_BE_32K || opcode == SPINOR_OP_BE_32K_4B ||
829d6164108SChin-Ting Kuo opcode == SPINOR_OP_SE || opcode == SPINOR_OP_SE_4B) {
830d6164108SChin-Ting Kuo op.addr.nbytes = len;
831d6164108SChin-Ting Kuo for (i = 0; i < len; i++) {
832d6164108SChin-Ting Kuo op.addr.val <<= 8;
833d6164108SChin-Ting Kuo op.addr.val |= (u32)write_buf[i];
834d6164108SChin-Ting Kuo }
835d6164108SChin-Ting Kuo op.data.nbytes = 0;
836d6164108SChin-Ting Kuo }
837d6164108SChin-Ting Kuo
838bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd(priv, flash, &op);
839bbe907dbSChin-Ting Kuo return 0;
840bbe907dbSChin-Ting Kuo }
841bbe907dbSChin-Ting Kuo
842499853d6Sryan_chen aspeed_spi_start_user(priv, flash);
843499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, &opcode, 1);
844499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, write_buf, len);
845499853d6Sryan_chen aspeed_spi_stop_user(priv, flash);
846499853d6Sryan_chen
847528cd552Sryan_chen debug("=== write opcode [%x] ==== \n", opcode);
848499853d6Sryan_chen switch(opcode) {
849499853d6Sryan_chen case SPINOR_OP_EN4B:
8500a73b911SChin-Ting Kuo /* For ast2600, if 2 chips ABR mode is enabled,
8510a73b911SChin-Ting Kuo * turn on 3B mode auto clear in order to avoid
8520a73b911SChin-Ting Kuo * the scenario where spi controller is at 4B mode
8530a73b911SChin-Ting Kuo * and flash site is at 3B mode after 3rd switch.
8540a73b911SChin-Ting Kuo */
8550a73b911SChin-Ting Kuo if (priv->new_ver == 1 && (readl(SPI_3B_AUTO_CLR_REG) & SPI_3B_AUTO_CLR))
8560a73b911SChin-Ting Kuo writel(readl(&priv->regs->soft_rst_cmd_ctrl) | SOFT_RST_CMD_EN,
8570a73b911SChin-Ting Kuo &priv->regs->soft_rst_cmd_ctrl);
8580a73b911SChin-Ting Kuo
859edbd932bSChin-Ting Kuo writel(readl(&priv->regs->ctrl) | (0x11 << flash->cs), &priv->regs->ctrl);
860499853d6Sryan_chen break;
861499853d6Sryan_chen case SPINOR_OP_EX4B:
862edbd932bSChin-Ting Kuo writel(readl(&priv->regs->ctrl) & ~(0x11 << flash->cs), &priv->regs->ctrl);
863499853d6Sryan_chen break;
864499853d6Sryan_chen }
865499853d6Sryan_chen return 0;
866499853d6Sryan_chen }
867499853d6Sryan_chen
aspeed_spi_send_cmd_addr(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,const u8 * cmdbuf,unsigned int cmdlen,uint32_t flag)868499853d6Sryan_chen static void aspeed_spi_send_cmd_addr(struct aspeed_spi_priv *priv,
869499853d6Sryan_chen struct aspeed_spi_flash *flash,
870cd800046SChin-Ting Kuo const u8 *cmdbuf, unsigned int cmdlen, uint32_t flag)
871499853d6Sryan_chen {
872499853d6Sryan_chen int i;
873499853d6Sryan_chen
874499853d6Sryan_chen /* First, send the opcode */
875499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, &cmdbuf[0], 1);
876499853d6Sryan_chen
877cd800046SChin-Ting Kuo if(flash->write_iomode == CE_CTRL_IO_QUAD_ADDR_DATA && (flag & SPI_WRITE_TO_FLASH))
878cd800046SChin-Ting Kuo writel(flash->ce_ctrl_user | flash->write_iomode, &priv->regs->ce_ctrl[flash->cs]);
879cd800046SChin-Ting Kuo else if(flash->read_iomode == CE_CTRL_IO_QUAD_ADDR_DATA && (flag & SPI_READ_FROM_FLASH))
880cd800046SChin-Ting Kuo writel(flash->ce_ctrl_user | flash->read_iomode, &priv->regs->ce_ctrl[flash->cs]);
881528cd552Sryan_chen
882499853d6Sryan_chen /* Then the address */
883499853d6Sryan_chen for (i = 1 ; i < cmdlen; i++)
884499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, &cmdbuf[i], 1);
885499853d6Sryan_chen }
886499853d6Sryan_chen
aspeed_spi_read_user(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,unsigned int cmdlen,const u8 * cmdbuf,unsigned int len,u8 * read_buf)887499853d6Sryan_chen static ssize_t aspeed_spi_read_user(struct aspeed_spi_priv *priv,
888499853d6Sryan_chen struct aspeed_spi_flash *flash,
889499853d6Sryan_chen unsigned int cmdlen, const u8 *cmdbuf,
890499853d6Sryan_chen unsigned int len, u8 *read_buf)
891499853d6Sryan_chen {
8928fbbfa7dSChin-Ting Kuo u8 dummy = 0x00;
893499853d6Sryan_chen int i;
894bbe907dbSChin-Ting Kuo struct aspeed_spi_op op =
895bbe907dbSChin-Ting Kuo ASPEED_SPI_OP(flash->read_iomode,
896bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_CMD(cmdbuf[0]),
897bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_ADDR(0, 0),
898bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DUMMY(flash->spi->read_dummy / 8),
899bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DATA_IN(len, read_buf));
900bbe907dbSChin-Ting Kuo
901bbe907dbSChin-Ting Kuo if (priv->spi_exec_op_cmd) {
902bbe907dbSChin-Ting Kuo op.addr.nbytes = cmdlen - 1 - op.dummy.nbytes;
903bbe907dbSChin-Ting Kuo op.addr.val = aspeed_spi_flash_to_addr(flash, cmdbuf, op.addr.nbytes + 1);
904bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd(priv, flash, &op);
905bbe907dbSChin-Ting Kuo return 0;
906bbe907dbSChin-Ting Kuo }
907499853d6Sryan_chen
908499853d6Sryan_chen aspeed_spi_start_user(priv, flash);
909499853d6Sryan_chen
910499853d6Sryan_chen /* cmd buffer = cmd + addr + dummies */
911499853d6Sryan_chen aspeed_spi_send_cmd_addr(priv, flash, cmdbuf,
912cd800046SChin-Ting Kuo cmdlen - (flash->spi->read_dummy / 8), SPI_READ_FROM_FLASH);
913499853d6Sryan_chen
914499853d6Sryan_chen for (i = 0; i < (flash->spi->read_dummy / 8); i++)
915499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, &dummy, 1);
916499853d6Sryan_chen
917cd800046SChin-Ting Kuo if (flash->read_iomode) {
918499853d6Sryan_chen clrbits_le32(&priv->regs->ce_ctrl[flash->cs],
919499853d6Sryan_chen CE_CTRL_IO_MODE_MASK);
920cd800046SChin-Ting Kuo setbits_le32(&priv->regs->ce_ctrl[flash->cs], flash->read_iomode);
921499853d6Sryan_chen }
922499853d6Sryan_chen
923499853d6Sryan_chen aspeed_spi_read_from_ahb(flash->ahb_base, read_buf, len);
924499853d6Sryan_chen aspeed_spi_stop_user(priv, flash);
925499853d6Sryan_chen
926499853d6Sryan_chen return 0;
927499853d6Sryan_chen }
928499853d6Sryan_chen
aspeed_spi_read_sfdp(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,unsigned int cmdlen,const u8 * cmdbuf,unsigned int len,u8 * read_buf)9298fbbfa7dSChin-Ting Kuo static ssize_t aspeed_spi_read_sfdp(struct aspeed_spi_priv *priv,
9308fbbfa7dSChin-Ting Kuo struct aspeed_spi_flash *flash,
9318fbbfa7dSChin-Ting Kuo unsigned int cmdlen, const u8 *cmdbuf,
9328fbbfa7dSChin-Ting Kuo unsigned int len, u8 *read_buf)
9338fbbfa7dSChin-Ting Kuo {
9348fbbfa7dSChin-Ting Kuo u8 dummy = 0x00;
9358fbbfa7dSChin-Ting Kuo int i;
936bbe907dbSChin-Ting Kuo struct aspeed_spi_op op =
937bbe907dbSChin-Ting Kuo ASPEED_SPI_OP(flash->read_iomode,
938bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_CMD(cmdbuf[0]),
939bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_ADDR(0, 3),
940bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DUMMY(flash->spi->read_dummy / 8),
941bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DATA_IN(len, read_buf));
942bbe907dbSChin-Ting Kuo
943bbe907dbSChin-Ting Kuo if (priv->spi_exec_op_cmd) {
944bbe907dbSChin-Ting Kuo op.addr.val = aspeed_spi_flash_to_addr(flash, cmdbuf, op.addr.nbytes + 1);
945bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd(priv, flash, &op);
946bbe907dbSChin-Ting Kuo return 0;
947bbe907dbSChin-Ting Kuo }
9488fbbfa7dSChin-Ting Kuo
9498fbbfa7dSChin-Ting Kuo /* only 1-1-1 mode is used to read SFDP */
9508fbbfa7dSChin-Ting Kuo aspeed_spi_start_user(priv, flash);
9518fbbfa7dSChin-Ting Kuo
9528fbbfa7dSChin-Ting Kuo /* cmd buffer = cmd + addr + dummies */
9538fbbfa7dSChin-Ting Kuo aspeed_spi_send_cmd_addr(priv, flash, cmdbuf,
9548fbbfa7dSChin-Ting Kuo cmdlen - (flash->spi->read_dummy / 8), 0);
9558fbbfa7dSChin-Ting Kuo
9568fbbfa7dSChin-Ting Kuo for (i = 0; i < (flash->spi->read_dummy / 8); i++)
9578fbbfa7dSChin-Ting Kuo aspeed_spi_write_to_ahb(flash->ahb_base, &dummy, 1);
9588fbbfa7dSChin-Ting Kuo
9598fbbfa7dSChin-Ting Kuo aspeed_spi_read_from_ahb(flash->ahb_base, read_buf, len);
9608fbbfa7dSChin-Ting Kuo aspeed_spi_stop_user(priv, flash);
9618fbbfa7dSChin-Ting Kuo
9628fbbfa7dSChin-Ting Kuo return 0;
9638fbbfa7dSChin-Ting Kuo }
9648fbbfa7dSChin-Ting Kuo
aspeed_spi_write_user(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,unsigned int cmdlen,const u8 * cmdbuf,unsigned int len,const u8 * write_buf)965499853d6Sryan_chen static ssize_t aspeed_spi_write_user(struct aspeed_spi_priv *priv,
966499853d6Sryan_chen struct aspeed_spi_flash *flash,
967499853d6Sryan_chen unsigned int cmdlen, const u8 *cmdbuf,
968499853d6Sryan_chen unsigned int len, const u8 *write_buf)
969499853d6Sryan_chen {
970bbe907dbSChin-Ting Kuo struct aspeed_spi_op op =
971bbe907dbSChin-Ting Kuo ASPEED_SPI_OP(flash->write_iomode,
972bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_CMD(cmdbuf[0]),
973bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_ADDR(0, 0),
974bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DUMMY(0),
975bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DATA_OUT(len, write_buf));
976bbe907dbSChin-Ting Kuo
977bbe907dbSChin-Ting Kuo if (priv->spi_exec_op_cmd) {
978bbe907dbSChin-Ting Kuo op.addr.nbytes = cmdlen - 1;
979bbe907dbSChin-Ting Kuo op.addr.val = aspeed_spi_flash_to_addr(flash, cmdbuf, op.addr.nbytes + 1);
980bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd(priv, flash, &op);
981bbe907dbSChin-Ting Kuo return 0;
982bbe907dbSChin-Ting Kuo }
983bbe907dbSChin-Ting Kuo
984499853d6Sryan_chen aspeed_spi_start_user(priv, flash);
985499853d6Sryan_chen
98676e3c7a9Sryan_chen /* cmd buffer = cmd + addr : normally cmd is use signle mode*/
987cd800046SChin-Ting Kuo aspeed_spi_send_cmd_addr(priv, flash, cmdbuf, cmdlen, SPI_WRITE_TO_FLASH);
98876e3c7a9Sryan_chen
98976e3c7a9Sryan_chen /* data will use io mode */
990cd800046SChin-Ting Kuo if(flash->write_iomode == CE_CTRL_IO_QUAD_DATA)
991cd800046SChin-Ting Kuo writel(flash->ce_ctrl_user | flash->write_iomode, &priv->regs->ce_ctrl[flash->cs]);
99276e3c7a9Sryan_chen
993499853d6Sryan_chen aspeed_spi_write_to_ahb(flash->ahb_base, write_buf, len);
994499853d6Sryan_chen
995499853d6Sryan_chen aspeed_spi_stop_user(priv, flash);
996499853d6Sryan_chen
997499853d6Sryan_chen return 0;
998499853d6Sryan_chen }
999499853d6Sryan_chen
aspeed_spi_flash_to_addr(struct aspeed_spi_flash * flash,const u8 * cmdbuf,unsigned int cmdlen)1000499853d6Sryan_chen static u32 aspeed_spi_flash_to_addr(struct aspeed_spi_flash *flash,
1001499853d6Sryan_chen const u8 *cmdbuf, unsigned int cmdlen)
1002499853d6Sryan_chen {
1003499853d6Sryan_chen u8 addrlen = cmdlen - 1;
1004499853d6Sryan_chen u32 addr = (cmdbuf[1] << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
1005499853d6Sryan_chen
1006499853d6Sryan_chen /*
1007499853d6Sryan_chen * U-Boot SPI Flash layer uses 3 bytes addresses, but it might
1008499853d6Sryan_chen * change one day
1009499853d6Sryan_chen */
1010499853d6Sryan_chen if (addrlen == 4)
1011499853d6Sryan_chen addr = (addr << 8) | cmdbuf[4];
1012499853d6Sryan_chen
1013499853d6Sryan_chen return addr;
1014499853d6Sryan_chen }
1015499853d6Sryan_chen
1016499853d6Sryan_chen /* TODO(clg@kaod.org): add support for XFER_MMAP instead ? */
aspeed_spi_read(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,unsigned int cmdlen,const u8 * cmdbuf,unsigned int len,u8 * read_buf)1017499853d6Sryan_chen static ssize_t aspeed_spi_read(struct aspeed_spi_priv *priv,
1018499853d6Sryan_chen struct aspeed_spi_flash *flash,
1019499853d6Sryan_chen unsigned int cmdlen, const u8 *cmdbuf,
1020499853d6Sryan_chen unsigned int len, u8 *read_buf)
1021499853d6Sryan_chen {
1022499853d6Sryan_chen /* cmd buffer = cmd + addr + dummies */
1023499853d6Sryan_chen u32 offset = aspeed_spi_flash_to_addr(flash, cmdbuf,
1024499853d6Sryan_chen cmdlen - (flash->spi->read_dummy/8));
1025bbe907dbSChin-Ting Kuo struct aspeed_spi_op op =
1026bbe907dbSChin-Ting Kuo ASPEED_SPI_OP(flash->read_iomode,
1027bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_CMD(cmdbuf[0]),
1028bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_ADDR(0, 0),
1029bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DUMMY(flash->spi->read_dummy / 8),
1030bbe907dbSChin-Ting Kuo ASPEED_SPI_OP_DATA_IN(len, read_buf));
1031bbe907dbSChin-Ting Kuo
1032bbe907dbSChin-Ting Kuo if (priv->spi_exec_op_cmd) {
1033bbe907dbSChin-Ting Kuo op.addr.nbytes = cmdlen - 1 - op.dummy.nbytes;
1034bbe907dbSChin-Ting Kuo op.addr.val = aspeed_spi_flash_to_addr(flash, cmdbuf, op.addr.nbytes + 1);
1035bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd(priv, flash, &op);
1036bbe907dbSChin-Ting Kuo return 0;
1037bbe907dbSChin-Ting Kuo }
1038499853d6Sryan_chen
1039499853d6Sryan_chen /*
10406424f9abSChin-Ting Kuo * Switch to USER command mode:
10418fbbfa7dSChin-Ting Kuo * - if read SFDP content.
10426424f9abSChin-Ting Kuo * - if the AHB window configured for the device is
10436424f9abSChin-Ting Kuo * too small for the read operation
10446424f9abSChin-Ting Kuo * - if read offset is smaller than the decoded start address
10458fbbfa7dSChin-Ting Kuo * and the decoded range is not multiple of flash size.
1046499853d6Sryan_chen */
10476424f9abSChin-Ting Kuo if ((offset + len >= flash->ahb_size) || \
10486424f9abSChin-Ting Kuo (offset < ((int)flash->ahb_base & 0x0FFFFFFF) && \
10496424f9abSChin-Ting Kuo (((int)flash->ahb_base & 0x0FFFFFFF) % flash->spi->size) != 0)) {
1050499853d6Sryan_chen return aspeed_spi_read_user(priv, flash, cmdlen, cmdbuf,
1051499853d6Sryan_chen len, read_buf);
1052499853d6Sryan_chen }
1053499853d6Sryan_chen
1054499853d6Sryan_chen memcpy_fromio(read_buf, flash->ahb_base + offset, len);
1055499853d6Sryan_chen
1056499853d6Sryan_chen return 0;
1057499853d6Sryan_chen }
1058499853d6Sryan_chen
aspeed_spi_exec_op_cmd_mode(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,struct aspeed_spi_op * op)1059bbe907dbSChin-Ting Kuo int aspeed_spi_exec_op_cmd_mode(struct aspeed_spi_priv *priv,
1060bbe907dbSChin-Ting Kuo struct aspeed_spi_flash *flash,
1061bbe907dbSChin-Ting Kuo struct aspeed_spi_op *op)
1062bbe907dbSChin-Ting Kuo {
1063bbe907dbSChin-Ting Kuo uint32_t cs = flash->cs;
1064bbe907dbSChin-Ting Kuo uint32_t ctrl_val;
1065bbe907dbSChin-Ting Kuo uint32_t addr_mode_reg, addr_mode_reg_backup;
1066bbe907dbSChin-Ting Kuo uint32_t addr_data_mask = 0;
1067bbe907dbSChin-Ting Kuo void __iomem *op_addr;
1068bbe907dbSChin-Ting Kuo const void *data_buf;
1069bbe907dbSChin-Ting Kuo uint32_t data_byte = 0;
1070bbe907dbSChin-Ting Kuo uint32_t dummy_data = 0;
1071bbe907dbSChin-Ting Kuo
1072bbe907dbSChin-Ting Kuo debug("iomode: %08x, cmd:%02x, addr:%08x, dummy:%d, data_len:%x, dir: %s\n",
1073bbe907dbSChin-Ting Kuo op->io_mode, op->cmd.opcode, op->addr.val, op->dummy.nbytes,
1074bbe907dbSChin-Ting Kuo op->data.nbytes, op->data.dir == ASPEED_SPI_DIR_IN ? "in" : "out");
1075bbe907dbSChin-Ting Kuo
1076bbe907dbSChin-Ting Kuo addr_mode_reg = readl(&priv->regs->ctrl);
1077bbe907dbSChin-Ting Kuo addr_mode_reg_backup = addr_mode_reg;
1078bbe907dbSChin-Ting Kuo addr_data_mask = readl(&priv->regs->cmd_ctrl);
1079bbe907dbSChin-Ting Kuo
1080bbe907dbSChin-Ting Kuo ctrl_val = flash->ce_ctrl_fread & (~0xf0ff40c7);
1081bbe907dbSChin-Ting Kuo ctrl_val |= op->io_mode;
1082bbe907dbSChin-Ting Kuo /* configure opcode */
1083bbe907dbSChin-Ting Kuo ctrl_val |= op->cmd.opcode << 16;
1084bbe907dbSChin-Ting Kuo
1085bbe907dbSChin-Ting Kuo /* configure operation address, address length and address mask */
1086bbe907dbSChin-Ting Kuo if (op->addr.nbytes != 0) {
1087bbe907dbSChin-Ting Kuo if (op->addr.nbytes == 3)
1088bbe907dbSChin-Ting Kuo addr_mode_reg &= ~(0x11 << cs);
1089bbe907dbSChin-Ting Kuo else
1090bbe907dbSChin-Ting Kuo addr_mode_reg |= (0x11 << cs);
1091bbe907dbSChin-Ting Kuo
1092bbe907dbSChin-Ting Kuo addr_data_mask &= 0x0f;
1093bbe907dbSChin-Ting Kuo op_addr = flash->ahb_base + op->addr.val;
1094bbe907dbSChin-Ting Kuo } else {
1095bbe907dbSChin-Ting Kuo addr_data_mask |= 0xf0;
1096bbe907dbSChin-Ting Kuo op_addr = flash->ahb_base;
1097bbe907dbSChin-Ting Kuo }
1098bbe907dbSChin-Ting Kuo
1099bbe907dbSChin-Ting Kuo if (op->dummy.nbytes != 0) {
1100bbe907dbSChin-Ting Kuo ctrl_val |= ((op->dummy.nbytes & 0x3) << 6 |
1101bbe907dbSChin-Ting Kuo ((op->dummy.nbytes & 0x4) >> 2) << 14);
1102bbe907dbSChin-Ting Kuo }
1103bbe907dbSChin-Ting Kuo
1104bbe907dbSChin-Ting Kuo /* configure data io mode and data mask */
1105bbe907dbSChin-Ting Kuo if (op->data.nbytes != 0) {
1106bbe907dbSChin-Ting Kuo addr_data_mask &= 0xF0;
1107bbe907dbSChin-Ting Kuo if (op->data.nbytes < 4)
1108bbe907dbSChin-Ting Kuo addr_data_mask |= ~((1 << op->data.nbytes) - 1);
1109bbe907dbSChin-Ting Kuo
1110bbe907dbSChin-Ting Kuo data_byte = op->data.nbytes;
1111bbe907dbSChin-Ting Kuo if (op->data.dir == ASPEED_SPI_DIR_OUT) {
1112bbe907dbSChin-Ting Kuo if (data_byte % 4 != 0) {
1113bbe907dbSChin-Ting Kuo memset(priv->tmp_buf, 0xff, ((data_byte / 4) + 1) * 4);
1114bbe907dbSChin-Ting Kuo memcpy(priv->tmp_buf, op->data.buf.out, data_byte);
1115bbe907dbSChin-Ting Kuo data_buf = priv->tmp_buf;
1116bbe907dbSChin-Ting Kuo data_byte = ((data_byte / 4) + 1) * 4;
1117bbe907dbSChin-Ting Kuo } else {
1118bbe907dbSChin-Ting Kuo data_buf = op->data.buf.out;
1119bbe907dbSChin-Ting Kuo }
1120bbe907dbSChin-Ting Kuo } else {
1121bbe907dbSChin-Ting Kuo data_buf = op->data.buf.in;
1122bbe907dbSChin-Ting Kuo }
1123bbe907dbSChin-Ting Kuo } else {
1124bbe907dbSChin-Ting Kuo addr_data_mask |= 0x0f;
1125bbe907dbSChin-Ting Kuo data_byte = 1;
1126bbe907dbSChin-Ting Kuo data_buf = &dummy_data;
1127bbe907dbSChin-Ting Kuo }
1128bbe907dbSChin-Ting Kuo
1129bbe907dbSChin-Ting Kuo /* configure command mode */
1130bbe907dbSChin-Ting Kuo if (op->data.dir == ASPEED_SPI_DIR_OUT)
1131bbe907dbSChin-Ting Kuo ctrl_val |= CE_CTRL_WRITEMODE;
1132bbe907dbSChin-Ting Kuo else
1133bbe907dbSChin-Ting Kuo ctrl_val |= CE_CTRL_FREADMODE;
1134bbe907dbSChin-Ting Kuo
1135bbe907dbSChin-Ting Kuo /* set controller registers */
1136bbe907dbSChin-Ting Kuo writel(ctrl_val, &priv->regs->ce_ctrl[cs]);
1137bbe907dbSChin-Ting Kuo writel(addr_mode_reg, &priv->regs->ctrl);
1138bbe907dbSChin-Ting Kuo writel(addr_data_mask, &priv->regs->cmd_ctrl);
1139bbe907dbSChin-Ting Kuo
1140bbe907dbSChin-Ting Kuo debug("ctrl: 0x%08x, addr_mode: 0x%x, mask: 0x%x, addr:0x%08x\n",
1141bbe907dbSChin-Ting Kuo ctrl_val, addr_mode_reg, addr_data_mask, (uint32_t)op_addr);
1142bbe907dbSChin-Ting Kuo
1143bbe907dbSChin-Ting Kuo /* trigger spi transmission or reception sequence */
1144bbe907dbSChin-Ting Kuo if (op->data.dir == ASPEED_SPI_DIR_OUT)
1145bbe907dbSChin-Ting Kuo memcpy_toio(op_addr, data_buf, data_byte);
1146bbe907dbSChin-Ting Kuo else
1147bbe907dbSChin-Ting Kuo memcpy_fromio((void *)data_buf, op_addr, data_byte);
1148bbe907dbSChin-Ting Kuo
1149bbe907dbSChin-Ting Kuo /* restore controller setting */
1150bbe907dbSChin-Ting Kuo writel(flash->ce_ctrl_fread, &priv->regs->ce_ctrl[cs]);
1151bbe907dbSChin-Ting Kuo writel(addr_mode_reg_backup, &priv->regs->ctrl);
1152bbe907dbSChin-Ting Kuo writel(0x0, &priv->regs->cmd_ctrl);
1153bbe907dbSChin-Ting Kuo
1154bbe907dbSChin-Ting Kuo return 0;
1155bbe907dbSChin-Ting Kuo }
1156bbe907dbSChin-Ting Kuo
aspeed_spi_xfer(struct udevice * dev,unsigned int bitlen,const void * dout,void * din,unsigned long flags)1157499853d6Sryan_chen static int aspeed_spi_xfer(struct udevice *dev, unsigned int bitlen,
1158499853d6Sryan_chen const void *dout, void *din, unsigned long flags)
1159499853d6Sryan_chen {
1160499853d6Sryan_chen struct udevice *bus = dev->parent;
1161499853d6Sryan_chen struct aspeed_spi_priv *priv = dev_get_priv(bus);
1162499853d6Sryan_chen struct aspeed_spi_flash *flash;
1163499853d6Sryan_chen u8 *cmd_buf = priv->cmd_buf;
1164499853d6Sryan_chen size_t data_bytes;
1165499853d6Sryan_chen int err = 0;
1166eaad4c09SChin-Ting Kuo u32 iomode;
1167499853d6Sryan_chen
1168499853d6Sryan_chen flash = aspeed_spi_get_flash(dev);
1169499853d6Sryan_chen if (!flash)
1170499853d6Sryan_chen return -ENXIO;
1171499853d6Sryan_chen
1172499853d6Sryan_chen if (flags & SPI_XFER_BEGIN) {
1173499853d6Sryan_chen /* save command in progress */
1174499853d6Sryan_chen priv->cmd_len = bitlen / 8;
1175499853d6Sryan_chen memcpy(cmd_buf, dout, priv->cmd_len);
1176499853d6Sryan_chen }
1177499853d6Sryan_chen
1178499853d6Sryan_chen if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) {
1179499853d6Sryan_chen /* if start and end bit are set, the data bytes is 0. */
1180499853d6Sryan_chen data_bytes = 0;
1181499853d6Sryan_chen } else {
1182499853d6Sryan_chen data_bytes = bitlen / 8;
1183499853d6Sryan_chen }
1184499853d6Sryan_chen
1185499853d6Sryan_chen debug("CS%u: %s cmd %zu bytes data %zu bytes\n", flash->cs,
1186499853d6Sryan_chen din ? "read" : "write", priv->cmd_len, data_bytes);
1187499853d6Sryan_chen
1188499853d6Sryan_chen if ((flags & SPI_XFER_END) || flags == 0) {
1189499853d6Sryan_chen if (priv->cmd_len == 0) {
1190499853d6Sryan_chen pr_err("No command is progress !\n");
1191499853d6Sryan_chen return -1;
1192499853d6Sryan_chen }
1193499853d6Sryan_chen
1194499853d6Sryan_chen if (din && data_bytes) {
11958fbbfa7dSChin-Ting Kuo if (priv->cmd_len == 1) {
1196499853d6Sryan_chen err = aspeed_spi_read_reg(priv, flash,
1197499853d6Sryan_chen cmd_buf[0],
1198499853d6Sryan_chen din, data_bytes);
11998fbbfa7dSChin-Ting Kuo } else if (cmd_buf[0] == SPINOR_OP_RDSFDP) {
12008fbbfa7dSChin-Ting Kuo err = aspeed_spi_read_sfdp(priv, flash,
12018fbbfa7dSChin-Ting Kuo priv->cmd_len,
12028fbbfa7dSChin-Ting Kuo cmd_buf, data_bytes,
12038fbbfa7dSChin-Ting Kuo din);
1204eaad4c09SChin-Ting Kuo } else if (cmd_buf[0] == SPINOR_OP_RDAR) {
1205eaad4c09SChin-Ting Kuo /* only for Cypress flash */
1206eaad4c09SChin-Ting Kuo iomode = flash->read_iomode;
1207eaad4c09SChin-Ting Kuo flash->read_iomode = 0;
1208eaad4c09SChin-Ting Kuo err = aspeed_spi_read_user(priv, flash,
1209eaad4c09SChin-Ting Kuo priv->cmd_len,
1210eaad4c09SChin-Ting Kuo cmd_buf, data_bytes,
1211eaad4c09SChin-Ting Kuo din);
1212eaad4c09SChin-Ting Kuo flash->read_iomode = iomode;
12138fbbfa7dSChin-Ting Kuo } else {
1214499853d6Sryan_chen err = aspeed_spi_read(priv, flash,
1215499853d6Sryan_chen priv->cmd_len,
1216499853d6Sryan_chen cmd_buf, data_bytes,
1217499853d6Sryan_chen din);
12188fbbfa7dSChin-Ting Kuo }
1219499853d6Sryan_chen } else if (dout) {
12208fbbfa7dSChin-Ting Kuo if (priv->cmd_len == 1) {
1221499853d6Sryan_chen err = aspeed_spi_write_reg(priv, flash,
1222499853d6Sryan_chen cmd_buf[0],
1223499853d6Sryan_chen dout, data_bytes);
12248fbbfa7dSChin-Ting Kuo } else {
1225499853d6Sryan_chen err = aspeed_spi_write_user(priv, flash,
1226499853d6Sryan_chen priv->cmd_len,
1227499853d6Sryan_chen cmd_buf, data_bytes,
1228499853d6Sryan_chen dout);
1229499853d6Sryan_chen }
12308fbbfa7dSChin-Ting Kuo }
1231499853d6Sryan_chen
1232499853d6Sryan_chen if (flags & SPI_XFER_END) {
1233499853d6Sryan_chen /* clear command */
1234499853d6Sryan_chen memset(cmd_buf, 0, sizeof(priv->cmd_buf));
1235499853d6Sryan_chen priv->cmd_len = 0;
1236499853d6Sryan_chen }
1237499853d6Sryan_chen }
1238499853d6Sryan_chen
1239499853d6Sryan_chen return err;
1240499853d6Sryan_chen }
1241499853d6Sryan_chen
12424c8f336cSChin-Ting Kuo #ifdef CONFIG_ASPEED_SPI_FLASH_WRITE_PROTECTION
aspeed_spi_fill_FQCD(struct aspeed_spi_priv * priv,u8 cmd)12434c8f336cSChin-Ting Kuo static void aspeed_spi_fill_FQCD(struct aspeed_spi_priv *priv, u8 cmd)
12444c8f336cSChin-Ting Kuo {
12454c8f336cSChin-Ting Kuo u32 reg_val;
12464c8f336cSChin-Ting Kuo u32 i;
12474c8f336cSChin-Ting Kuo
12484c8f336cSChin-Ting Kuo for (i = 0; i < 20; i++) {
12494c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->fully_qualified_cmd[i]);
12504c8f336cSChin-Ting Kuo if ((u8)(reg_val & 0xff) == cmd ||
12514c8f336cSChin-Ting Kuo (u8)((reg_val & 0xff00) >> 8) == cmd) {
12524c8f336cSChin-Ting Kuo if ((reg_val & 0x80000000) == 0x80000000) {
12534c8f336cSChin-Ting Kuo debug("cmd: %02x already exists in FQCD.\n", cmd);
12544c8f336cSChin-Ting Kuo return;
12554c8f336cSChin-Ting Kuo }
12564c8f336cSChin-Ting Kuo }
12574c8f336cSChin-Ting Kuo }
12584c8f336cSChin-Ting Kuo
12594c8f336cSChin-Ting Kuo for (i = 0; i < 20; i++) {
12604c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->fully_qualified_cmd[i]);
12614c8f336cSChin-Ting Kuo if ((reg_val & 0x80000000) == 0x80000000) {
12624c8f336cSChin-Ting Kuo if ((u8)(reg_val & 0xff) == 0x0) {
12634c8f336cSChin-Ting Kuo reg_val |= (u32)cmd;
12644c8f336cSChin-Ting Kuo debug("[%d]fill %02x cmd in FQCD%02d.\n", __LINE__, cmd, i);
12654c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->fully_qualified_cmd[i]);
12664c8f336cSChin-Ting Kuo return;
12674c8f336cSChin-Ting Kuo } else if ((u8)((reg_val & 0xff00) >> 8) == 0x0) {
12684c8f336cSChin-Ting Kuo reg_val |= ((u32)cmd) << 8;
12694c8f336cSChin-Ting Kuo debug("[%d]fill %02x cmd in FQCD%02d.\n", __LINE__, cmd, i);
12704c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->fully_qualified_cmd[i]);
12714c8f336cSChin-Ting Kuo return;
12724c8f336cSChin-Ting Kuo }
12734c8f336cSChin-Ting Kuo }
12744c8f336cSChin-Ting Kuo }
12754c8f336cSChin-Ting Kuo
12764c8f336cSChin-Ting Kuo for (i = 0; i < 20; i++) {
12774c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->fully_qualified_cmd[i]);
12784c8f336cSChin-Ting Kuo if (reg_val == 0) {
12794c8f336cSChin-Ting Kuo reg_val = 0x80000000 | (u32)cmd;
12804c8f336cSChin-Ting Kuo debug("[%d]fill %02x cmd in FQCD%02d.\n", __LINE__, cmd, i);
12814c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->fully_qualified_cmd[i]);
12824c8f336cSChin-Ting Kuo return;
12834c8f336cSChin-Ting Kuo }
12844c8f336cSChin-Ting Kuo }
12854c8f336cSChin-Ting Kuo }
12864c8f336cSChin-Ting Kuo
aspeed_spi_fill_AQCD(struct aspeed_spi_priv * priv,u8 cmd,u8 addr_width)12874c8f336cSChin-Ting Kuo static void aspeed_spi_fill_AQCD(struct aspeed_spi_priv *priv, u8 cmd, u8 addr_width)
12884c8f336cSChin-Ting Kuo {
12894c8f336cSChin-Ting Kuo u32 reg_val;
12904c8f336cSChin-Ting Kuo u32 i;
12914c8f336cSChin-Ting Kuo u32 bit_offset;
12924c8f336cSChin-Ting Kuo
12934c8f336cSChin-Ting Kuo if (addr_width != 3 && addr_width != 4) {
12944c8f336cSChin-Ting Kuo printf("wrong address width: %d.\n", addr_width);
12954c8f336cSChin-Ting Kuo return;
12964c8f336cSChin-Ting Kuo }
12974c8f336cSChin-Ting Kuo
12984c8f336cSChin-Ting Kuo bit_offset = (addr_width - 3) * 8;
12994c8f336cSChin-Ting Kuo
13004c8f336cSChin-Ting Kuo for (i = 0; i < 12; i++) {
13014c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->addr_qualified_cmd[i]);
13024c8f336cSChin-Ting Kuo if ((reg_val & 0x80000000) == 0x80000000) {
13034c8f336cSChin-Ting Kuo if ((u8)((reg_val & (0xff << bit_offset)) >> bit_offset) == cmd) {
13044c8f336cSChin-Ting Kuo debug("cmd: %02x already exists in AQCD.\n", cmd);
13054c8f336cSChin-Ting Kuo return;
13064c8f336cSChin-Ting Kuo }
13074c8f336cSChin-Ting Kuo }
13084c8f336cSChin-Ting Kuo }
13094c8f336cSChin-Ting Kuo
13104c8f336cSChin-Ting Kuo for (i = 0; i < 12; i++) {
13114c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->addr_qualified_cmd[i]);
13124c8f336cSChin-Ting Kuo if ((reg_val & 0x80000000) == 0x80000000) {
13134c8f336cSChin-Ting Kuo if ((u8)((reg_val & (0xff << bit_offset)) >> bit_offset) == 0x0) {
13144c8f336cSChin-Ting Kuo reg_val |= ((u32)cmd << bit_offset);
13154c8f336cSChin-Ting Kuo debug("fill %02x cmd in AQCD%02d.\n", cmd, i);
13164c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->addr_qualified_cmd[i]);
13174c8f336cSChin-Ting Kuo return;
13184c8f336cSChin-Ting Kuo }
13194c8f336cSChin-Ting Kuo }
13204c8f336cSChin-Ting Kuo
13214c8f336cSChin-Ting Kuo if (reg_val == 0) {
13224c8f336cSChin-Ting Kuo reg_val = 0x80000000 | ((u32)cmd << bit_offset);
13234c8f336cSChin-Ting Kuo debug("fill %02x cmd in AQCD%02d.\n", cmd, i);
13244c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->addr_qualified_cmd[i]);
13254c8f336cSChin-Ting Kuo return;
13264c8f336cSChin-Ting Kuo }
13274c8f336cSChin-Ting Kuo }
13284c8f336cSChin-Ting Kuo }
13294c8f336cSChin-Ting Kuo
aspeed_spi_cmd_filter_config(struct aspeed_spi_priv * priv,u32 cs,bool enable)13304c8f336cSChin-Ting Kuo static void aspeed_spi_cmd_filter_config(struct aspeed_spi_priv *priv,
13314c8f336cSChin-Ting Kuo u32 cs, bool enable)
13324c8f336cSChin-Ting Kuo {
13334c8f336cSChin-Ting Kuo u32 reg_val;
13344c8f336cSChin-Ting Kuo
13354c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->write_cmd_filter_ctrl);
13364c8f336cSChin-Ting Kuo
13374c8f336cSChin-Ting Kuo if (enable)
13384c8f336cSChin-Ting Kuo reg_val |= BIT(cs);
13394c8f336cSChin-Ting Kuo else
13404c8f336cSChin-Ting Kuo reg_val &= ~BIT(cs);
13414c8f336cSChin-Ting Kuo
13424c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->write_cmd_filter_ctrl);
13434c8f336cSChin-Ting Kuo }
13444c8f336cSChin-Ting Kuo
aspeed_spi_write_addr_ftr_sanity(struct aspeed_spi_priv * priv,u32 offset,size_t len)13454c8f336cSChin-Ting Kuo static int aspeed_spi_write_addr_ftr_sanity(struct aspeed_spi_priv *priv,
13464c8f336cSChin-Ting Kuo u32 offset, size_t len)
13474c8f336cSChin-Ting Kuo {
13484c8f336cSChin-Ting Kuo u32 addr_ftr_ctrl;
13494c8f336cSChin-Ting Kuo u32 reg_val;
13504c8f336cSChin-Ting Kuo u32 start;
13514c8f336cSChin-Ting Kuo u32 end;
13524c8f336cSChin-Ting Kuo u32 i;
13534c8f336cSChin-Ting Kuo
13544c8f336cSChin-Ting Kuo addr_ftr_ctrl = readl(&priv->regs->write_addr_filter_ctrl);
13554c8f336cSChin-Ting Kuo for (i = 0; i < 8; i++) {
13564c8f336cSChin-Ting Kuo if ((addr_ftr_ctrl & (0x3 << (i * 2))) == 0)
13574c8f336cSChin-Ting Kuo continue;
13584c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->write_addr_filter[i]);
13594c8f336cSChin-Ting Kuo start = (reg_val & 0xffff) << 12;
13604c8f336cSChin-Ting Kuo end = (((reg_val & 0xffff0000) >> 16) << 12) | 0xFFF;
13614c8f336cSChin-Ting Kuo
13624c8f336cSChin-Ting Kuo if (offset >= start && offset < end)
13634c8f336cSChin-Ting Kuo return -1;
13644c8f336cSChin-Ting Kuo else if ((offset + len) > start && (offset + len) < end)
13654c8f336cSChin-Ting Kuo return -1;
13664c8f336cSChin-Ting Kuo }
13674c8f336cSChin-Ting Kuo
13684c8f336cSChin-Ting Kuo return 0;
13694c8f336cSChin-Ting Kuo }
13704c8f336cSChin-Ting Kuo
aspeed_add_write_addr_ftr(struct aspeed_spi_priv * priv,u32 offset,size_t len)13714c8f336cSChin-Ting Kuo static int aspeed_add_write_addr_ftr(struct aspeed_spi_priv *priv,
13724c8f336cSChin-Ting Kuo u32 offset, size_t len)
13734c8f336cSChin-Ting Kuo {
13744c8f336cSChin-Ting Kuo u32 addr_ftr_ctrl;
13754c8f336cSChin-Ting Kuo u32 reg_val;
13764c8f336cSChin-Ting Kuo u32 start;
13774c8f336cSChin-Ting Kuo u32 end;
13784c8f336cSChin-Ting Kuo u32 i;
13794c8f336cSChin-Ting Kuo
13804c8f336cSChin-Ting Kuo if ((offset & 0xfff) != 0) {
13814c8f336cSChin-Ting Kuo offset &= 0xfffff000;
13824c8f336cSChin-Ting Kuo printf("protected start address will be entend to 0x%08x.\n",
13834c8f336cSChin-Ting Kuo offset);
13844c8f336cSChin-Ting Kuo }
13854c8f336cSChin-Ting Kuo
13864c8f336cSChin-Ting Kuo if ((len & 0xfff) != 0) {
13874c8f336cSChin-Ting Kuo len &= 0xfff;
13884c8f336cSChin-Ting Kuo printf("protected len will be trimed to 0x%x.\n", len);
13894c8f336cSChin-Ting Kuo }
13904c8f336cSChin-Ting Kuo
13914c8f336cSChin-Ting Kuo if (len == 0) {
13924c8f336cSChin-Ting Kuo printf("invalid protect len: 0x%x.\n", len);
13934c8f336cSChin-Ting Kuo return -1;
13944c8f336cSChin-Ting Kuo }
13954c8f336cSChin-Ting Kuo
13964c8f336cSChin-Ting Kuo addr_ftr_ctrl = readl(&priv->regs->write_addr_filter_ctrl);
13974c8f336cSChin-Ting Kuo for (i = 0; i < 8; i++) {
13984c8f336cSChin-Ting Kuo if ((addr_ftr_ctrl & (0x3 << (i * 2))) == 0) {
13994c8f336cSChin-Ting Kuo start = offset;
14004c8f336cSChin-Ting Kuo end = offset + len - 1;
14014c8f336cSChin-Ting Kuo
14024c8f336cSChin-Ting Kuo reg_val = (start >> 12) | ((end >> 12) << 16);
14034c8f336cSChin-Ting Kuo
14044c8f336cSChin-Ting Kuo debug("start: 0x%08x, end: 0x%08x, val: 0x%08x.\n",
14054c8f336cSChin-Ting Kuo start, end, reg_val);
14064c8f336cSChin-Ting Kuo
14074c8f336cSChin-Ting Kuo writel(reg_val, &priv->regs->write_addr_filter[i]);
14084c8f336cSChin-Ting Kuo addr_ftr_ctrl |= 0x3 << (i * 2);
14094c8f336cSChin-Ting Kuo writel(addr_ftr_ctrl, &priv->regs->write_addr_filter_ctrl);
14104c8f336cSChin-Ting Kuo
14114c8f336cSChin-Ting Kuo printf("apply write lock from offset, 0x%08x, with len, 0x%08x.\n",
14124c8f336cSChin-Ting Kuo offset, (u32)len);
14134c8f336cSChin-Ting Kuo
14144c8f336cSChin-Ting Kuo break;
14154c8f336cSChin-Ting Kuo }
14164c8f336cSChin-Ting Kuo }
14174c8f336cSChin-Ting Kuo
14184c8f336cSChin-Ting Kuo if (i == 8) {
14194c8f336cSChin-Ting Kuo printf("insufficient write address filter register.\n");
14204c8f336cSChin-Ting Kuo return -1;
14214c8f336cSChin-Ting Kuo }
14224c8f336cSChin-Ting Kuo
14234c8f336cSChin-Ting Kuo return 0;
14244c8f336cSChin-Ting Kuo }
14254c8f336cSChin-Ting Kuo
aspeed_remove_write_addr_ftr(struct aspeed_spi_priv * priv,u32 offset,size_t len)14264c8f336cSChin-Ting Kuo static int aspeed_remove_write_addr_ftr(struct aspeed_spi_priv *priv,
14274c8f336cSChin-Ting Kuo u32 offset, size_t len)
14284c8f336cSChin-Ting Kuo {
14294c8f336cSChin-Ting Kuo u32 addr_ftr_ctrl;
14304c8f336cSChin-Ting Kuo u32 reg_val;
14314c8f336cSChin-Ting Kuo u32 bit_mask;
14324c8f336cSChin-Ting Kuo u32 start;
14334c8f336cSChin-Ting Kuo u32 end;
14344c8f336cSChin-Ting Kuo u32 i;
14354c8f336cSChin-Ting Kuo
14364c8f336cSChin-Ting Kuo if ((offset & 0xfff) != 0) {
14374c8f336cSChin-Ting Kuo printf("start address should be aligned to 0x1000.\n");
14384c8f336cSChin-Ting Kuo return -1;
14394c8f336cSChin-Ting Kuo }
14404c8f336cSChin-Ting Kuo
14414c8f336cSChin-Ting Kuo if ((len & 0xfff) != 0) {
14424c8f336cSChin-Ting Kuo printf("removed length should be aligned to 0x1000.\n");
14434c8f336cSChin-Ting Kuo return -1;
14444c8f336cSChin-Ting Kuo }
14454c8f336cSChin-Ting Kuo
14464c8f336cSChin-Ting Kuo if (len == 0) {
14474c8f336cSChin-Ting Kuo printf("invalid removed length!\n");
14484c8f336cSChin-Ting Kuo return -1;
14494c8f336cSChin-Ting Kuo }
14504c8f336cSChin-Ting Kuo
14514c8f336cSChin-Ting Kuo addr_ftr_ctrl = readl(&priv->regs->write_addr_filter_ctrl);
14524c8f336cSChin-Ting Kuo for (i = 0; i < 8; i++) {
14534c8f336cSChin-Ting Kuo bit_mask = 0x3 << (i * 2);
14544c8f336cSChin-Ting Kuo if ((addr_ftr_ctrl & bit_mask) != bit_mask)
14554c8f336cSChin-Ting Kuo continue;
14564c8f336cSChin-Ting Kuo
14574c8f336cSChin-Ting Kuo reg_val = readl(&priv->regs->write_addr_filter[i]);
14584c8f336cSChin-Ting Kuo start = (reg_val & 0xffff) << 12;
14594c8f336cSChin-Ting Kuo end = (((reg_val & 0xffff0000) >> 16) << 12) + 0x1000;
14604c8f336cSChin-Ting Kuo
14614c8f336cSChin-Ting Kuo if (offset != start || offset + len != end)
14624c8f336cSChin-Ting Kuo continue;
14634c8f336cSChin-Ting Kuo
14644c8f336cSChin-Ting Kuo addr_ftr_ctrl &= ~(0x3 << (i * 2));
14654c8f336cSChin-Ting Kuo writel(addr_ftr_ctrl, &priv->regs->write_addr_filter_ctrl);
14664c8f336cSChin-Ting Kuo writel(0x0, &priv->regs->write_addr_filter[i]);
14674c8f336cSChin-Ting Kuo printf("remove write lock from offset, 0x%08x, with len, 0x%08x.\n",
14684c8f336cSChin-Ting Kuo offset, (u32)len);
14694c8f336cSChin-Ting Kuo break;
14704c8f336cSChin-Ting Kuo }
14714c8f336cSChin-Ting Kuo
14724c8f336cSChin-Ting Kuo if (i == 8) {
14734c8f336cSChin-Ting Kuo printf("cannot find expected removed region.\n");
14744c8f336cSChin-Ting Kuo return -1;
14754c8f336cSChin-Ting Kuo }
14764c8f336cSChin-Ting Kuo
14774c8f336cSChin-Ting Kuo return 0;
14784c8f336cSChin-Ting Kuo }
14794c8f336cSChin-Ting Kuo
aspeed_spi_mem_wlock(struct udevice * dev,u32 offset,size_t len)14804c8f336cSChin-Ting Kuo static int aspeed_spi_mem_wlock(struct udevice *dev, u32 offset, size_t len)
14814c8f336cSChin-Ting Kuo {
14824c8f336cSChin-Ting Kuo struct udevice *bus = dev->parent;
14834c8f336cSChin-Ting Kuo struct aspeed_spi_priv *priv = dev_get_priv(bus);
14844c8f336cSChin-Ting Kuo struct aspeed_spi_flash *flash;
14854c8f336cSChin-Ting Kuo struct spi_nor *nor;
14864c8f336cSChin-Ting Kuo int ret;
14874c8f336cSChin-Ting Kuo
14884c8f336cSChin-Ting Kuo debug("%s offset: 0x%08x, len: 0x%08x.\n", __func__, offset, (u32)len);
14894c8f336cSChin-Ting Kuo
14904c8f336cSChin-Ting Kuo flash = aspeed_spi_get_flash(dev);
14914c8f336cSChin-Ting Kuo if (!flash)
14924c8f336cSChin-Ting Kuo return -ENXIO;
14934c8f336cSChin-Ting Kuo
14944c8f336cSChin-Ting Kuo nor = flash->spi;
14954c8f336cSChin-Ting Kuo
14964c8f336cSChin-Ting Kuo debug("name: %s, read cmd: %02x, erase cmd: %02x, write cmd: %02x.\n",
14974c8f336cSChin-Ting Kuo nor->name, nor->read_opcode, nor->erase_opcode, nor->program_opcode);
14984c8f336cSChin-Ting Kuo
14994c8f336cSChin-Ting Kuo /* enable address filter */
15004c8f336cSChin-Ting Kuo aspeed_spi_fill_FQCD(priv, nor->read_opcode);
15014c8f336cSChin-Ting Kuo aspeed_spi_fill_AQCD(priv, nor->erase_opcode, nor->addr_width);
15024c8f336cSChin-Ting Kuo aspeed_spi_fill_AQCD(priv, nor->program_opcode, nor->addr_width);
15034c8f336cSChin-Ting Kuo aspeed_spi_cmd_filter_config(priv, flash->cs, true);
15044c8f336cSChin-Ting Kuo
15054c8f336cSChin-Ting Kuo ret = aspeed_spi_write_addr_ftr_sanity(priv, offset, len);
15064c8f336cSChin-Ting Kuo if (ret < 0) {
15074c8f336cSChin-Ting Kuo printf("The expected protect region overlays with the existed regions!\n");
15084c8f336cSChin-Ting Kuo return ret;
15094c8f336cSChin-Ting Kuo }
15104c8f336cSChin-Ting Kuo
15114c8f336cSChin-Ting Kuo ret = aspeed_add_write_addr_ftr(priv, offset, len);
15124c8f336cSChin-Ting Kuo if (ret < 0)
15134c8f336cSChin-Ting Kuo return -1;
15144c8f336cSChin-Ting Kuo
15154c8f336cSChin-Ting Kuo return 0;
15164c8f336cSChin-Ting Kuo }
15174c8f336cSChin-Ting Kuo
aspeed_spi_mem_wunlock(struct udevice * dev,u32 offset,size_t len)15184c8f336cSChin-Ting Kuo static int aspeed_spi_mem_wunlock(struct udevice *dev, u32 offset, size_t len)
15194c8f336cSChin-Ting Kuo {
15204c8f336cSChin-Ting Kuo struct udevice *bus = dev->parent;
15214c8f336cSChin-Ting Kuo struct aspeed_spi_priv *priv = dev_get_priv(bus);
15224c8f336cSChin-Ting Kuo int ret;
15234c8f336cSChin-Ting Kuo
15244c8f336cSChin-Ting Kuo ret = aspeed_remove_write_addr_ftr(priv, offset, len);
15254c8f336cSChin-Ting Kuo if (ret < 0)
15264c8f336cSChin-Ting Kuo return -1;
15274c8f336cSChin-Ting Kuo
15284c8f336cSChin-Ting Kuo return 0;
15294c8f336cSChin-Ting Kuo }
15304c8f336cSChin-Ting Kuo #endif
15314c8f336cSChin-Ting Kuo
aspeed_spi_child_pre_probe(struct udevice * dev)1532499853d6Sryan_chen static int aspeed_spi_child_pre_probe(struct udevice *dev)
1533499853d6Sryan_chen {
1534499853d6Sryan_chen struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
1535499853d6Sryan_chen
1536499853d6Sryan_chen debug("pre_probe slave device on CS%u, max_hz %u, mode 0x%x.\n",
1537499853d6Sryan_chen slave_plat->cs, slave_plat->max_hz, slave_plat->mode);
1538499853d6Sryan_chen
1539499853d6Sryan_chen if (!aspeed_spi_get_flash(dev))
1540499853d6Sryan_chen return -ENXIO;
1541499853d6Sryan_chen
1542499853d6Sryan_chen return 0;
1543499853d6Sryan_chen }
1544499853d6Sryan_chen
1545499853d6Sryan_chen /*
15468fbbfa7dSChin-Ting Kuo * AST2600 SPI memory controllers support multiple chip selects.
15478fbbfa7dSChin-Ting Kuo * The start address of a decode range should be multiple
15488fbbfa7dSChin-Ting Kuo * of its related flash size. Namely, the total decoded size
15498fbbfa7dSChin-Ting Kuo * from flash 0 to flash N should be multiple of (N + 1) flash size.
15508fbbfa7dSChin-Ting Kuo */
aspeed_g6_adjust_decode_sz(u32 decode_sz_arr[],int len)15518fbbfa7dSChin-Ting Kuo void aspeed_g6_adjust_decode_sz(u32 decode_sz_arr[], int len)
15528fbbfa7dSChin-Ting Kuo {
15538fbbfa7dSChin-Ting Kuo int cs, j;
15548fbbfa7dSChin-Ting Kuo u32 sz;
15558fbbfa7dSChin-Ting Kuo
15568fbbfa7dSChin-Ting Kuo for (cs = len - 1; cs >= 0; cs--) {
15578fbbfa7dSChin-Ting Kuo sz = 0;
15588fbbfa7dSChin-Ting Kuo for (j = 0; j < cs; j++)
15598fbbfa7dSChin-Ting Kuo sz += decode_sz_arr[j];
15608fbbfa7dSChin-Ting Kuo
15618fbbfa7dSChin-Ting Kuo if (sz % decode_sz_arr[cs] != 0)
15628fbbfa7dSChin-Ting Kuo decode_sz_arr[0] += (sz % decode_sz_arr[cs]);
15638fbbfa7dSChin-Ting Kuo }
15648fbbfa7dSChin-Ting Kuo }
15658fbbfa7dSChin-Ting Kuo
15668fbbfa7dSChin-Ting Kuo /*
1567499853d6Sryan_chen * It is possible to automatically define a contiguous address space
1568499853d6Sryan_chen * on top of all CEs in the AHB window of the controller but it would
1569499853d6Sryan_chen * require much more work. Let's start with a simple mapping scheme
1570499853d6Sryan_chen * which should work fine for a single flash device.
1571499853d6Sryan_chen *
1572499853d6Sryan_chen * More complex schemes should probably be defined with the device
1573499853d6Sryan_chen * tree.
1574499853d6Sryan_chen */
aspeed_spi_flash_set_segment(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash)1575499853d6Sryan_chen static int aspeed_spi_flash_set_segment(struct aspeed_spi_priv *priv,
1576499853d6Sryan_chen struct aspeed_spi_flash *flash)
1577499853d6Sryan_chen {
1578499853d6Sryan_chen u32 seg_addr;
15798fbbfa7dSChin-Ting Kuo u32 decode_sz_arr[ASPEED_SPI_MAX_CS];
15808fbbfa7dSChin-Ting Kuo u32 reg_val;
15818fbbfa7dSChin-Ting Kuo u32 cs;
15828fbbfa7dSChin-Ting Kuo u32 total_decode_sz = 0;
15838fbbfa7dSChin-Ting Kuo u32 cur_offset = 0;
1584499853d6Sryan_chen
1585499853d6Sryan_chen /* could be configured through the device tree */
1586499853d6Sryan_chen flash->ahb_size = flash->spi->size;
1587499853d6Sryan_chen
1588da83dd7eSryan_chen if (priv->new_ver) {
15898fbbfa7dSChin-Ting Kuo for (cs = 0; cs < ASPEED_SPI_MAX_CS; cs++) {
15908fbbfa7dSChin-Ting Kuo reg_val = readl(&priv->regs->segment_addr[cs]);
159171df69c6SChin-Ting Kuo if (reg_val != 0 &&
159271df69c6SChin-Ting Kuo G6_SEGMENT_ADDR_END(reg_val) > G6_SEGMENT_ADDR_START(reg_val)) {
15938fbbfa7dSChin-Ting Kuo decode_sz_arr[cs] =
15948fbbfa7dSChin-Ting Kuo G6_SEGMENT_ADDR_END(reg_val) - G6_SEGMENT_ADDR_START(reg_val);
15958fbbfa7dSChin-Ting Kuo } else {
15968fbbfa7dSChin-Ting Kuo decode_sz_arr[cs] = 0;
15978fbbfa7dSChin-Ting Kuo }
15988fbbfa7dSChin-Ting Kuo }
15998fbbfa7dSChin-Ting Kuo
16008fbbfa7dSChin-Ting Kuo decode_sz_arr[flash->cs] = flash->ahb_size;
16018fbbfa7dSChin-Ting Kuo aspeed_g6_adjust_decode_sz(decode_sz_arr, flash->cs + 1);
16028fbbfa7dSChin-Ting Kuo
16038fbbfa7dSChin-Ting Kuo for (cs = 0; cs < ASPEED_SPI_MAX_CS; cs++)
16048fbbfa7dSChin-Ting Kuo total_decode_sz += decode_sz_arr[cs];
16058fbbfa7dSChin-Ting Kuo
16068fbbfa7dSChin-Ting Kuo if (total_decode_sz > priv->ahb_size) {
1607edaef9d2SChin-Ting Kuo printf("err: Total decoded size, 0x%x, is too large.\n", total_decode_sz);
16088fbbfa7dSChin-Ting Kuo return -ENOMEM;
16098fbbfa7dSChin-Ting Kuo }
16108fbbfa7dSChin-Ting Kuo
16118fbbfa7dSChin-Ting Kuo for (cs = 0; cs < ASPEED_SPI_MAX_CS; cs++) {
16128fbbfa7dSChin-Ting Kuo struct aspeed_spi_flash *flash = &priv->flashes[cs];
16138fbbfa7dSChin-Ting Kuo
16148fbbfa7dSChin-Ting Kuo flash->ahb_base = (void __iomem *)((u32)priv->ahb_base + cur_offset);
16158fbbfa7dSChin-Ting Kuo
16168fbbfa7dSChin-Ting Kuo if (decode_sz_arr[cs] != 0) {
1617da83dd7eSryan_chen seg_addr = G6_SEGMENT_ADDR_VALUE((u32)flash->ahb_base,
16188fbbfa7dSChin-Ting Kuo (u32)flash->ahb_base + decode_sz_arr[cs]);
16198fbbfa7dSChin-Ting Kuo } else {
16208fbbfa7dSChin-Ting Kuo seg_addr = 0;
16218fbbfa7dSChin-Ting Kuo }
16228fbbfa7dSChin-Ting Kuo
16238fbbfa7dSChin-Ting Kuo writel(seg_addr, &priv->regs->segment_addr[cs]);
16248fbbfa7dSChin-Ting Kuo flash->ahb_size = decode_sz_arr[cs];
16258fbbfa7dSChin-Ting Kuo cur_offset += decode_sz_arr[cs];
16268fbbfa7dSChin-Ting Kuo }
1627da83dd7eSryan_chen } else {
1628499853d6Sryan_chen seg_addr = SEGMENT_ADDR_VALUE((u32)flash->ahb_base,
1629499853d6Sryan_chen (u32)flash->ahb_base + flash->ahb_size);
1630499853d6Sryan_chen writel(seg_addr, &priv->regs->segment_addr[flash->cs]);
16318fbbfa7dSChin-Ting Kuo }
1632499853d6Sryan_chen
1633499853d6Sryan_chen return 0;
1634499853d6Sryan_chen }
1635499853d6Sryan_chen
aspeed_spi_flash_init(struct aspeed_spi_priv * priv,struct aspeed_spi_flash * flash,struct udevice * dev)1636499853d6Sryan_chen static int aspeed_spi_flash_init(struct aspeed_spi_priv *priv,
1637499853d6Sryan_chen struct aspeed_spi_flash *flash,
1638499853d6Sryan_chen struct udevice *dev)
1639499853d6Sryan_chen {
1640beec505fSChin-Ting Kuo int ret;
1641499853d6Sryan_chen struct spi_flash *spi_flash = dev_get_uclass_priv(dev);
1642499853d6Sryan_chen struct spi_slave *slave = dev_get_parent_priv(dev);
164351b227c8SChin-Ting Kuo struct udevice *bus = dev->parent;
1644499853d6Sryan_chen u32 read_hclk;
1645499853d6Sryan_chen
1646a4e44632SChin-Ting Kuo flash->spi = spi_flash;
1647a4e44632SChin-Ting Kuo
1648499853d6Sryan_chen /*
1649499853d6Sryan_chen * The flash device has not been probed yet. Initial transfers
1650499853d6Sryan_chen * to read the JEDEC of the device will use the initial
1651499853d6Sryan_chen * default settings of the registers.
1652499853d6Sryan_chen */
1653499853d6Sryan_chen if (!spi_flash->name)
1654499853d6Sryan_chen return 0;
1655499853d6Sryan_chen
1656543bff32SChin-Ting Kuo /*
1657543bff32SChin-Ting Kuo * The SPI flash device slave should not change, so initialize
1658543bff32SChin-Ting Kuo * it only once.
1659543bff32SChin-Ting Kuo */
1660543bff32SChin-Ting Kuo if (flash->init)
1661543bff32SChin-Ting Kuo return 0;
1662543bff32SChin-Ting Kuo
1663edaef9d2SChin-Ting Kuo debug("CS%u: init %s flags:%x size:%d page:%d sector:%d erase:%d",
1664499853d6Sryan_chen flash->cs,
1665499853d6Sryan_chen spi_flash->name, spi_flash->flags, spi_flash->size,
1666499853d6Sryan_chen spi_flash->page_size, spi_flash->sector_size,
1667edaef9d2SChin-Ting Kuo spi_flash->erase_size);
1668edaef9d2SChin-Ting Kuo debug(" cmds [ erase:%x read=%x write:%x ] dummy:%d, speed:%d\n",
1669edaef9d2SChin-Ting Kuo spi_flash->erase_opcode,
1670499853d6Sryan_chen spi_flash->read_opcode, spi_flash->program_opcode,
1671edaef9d2SChin-Ting Kuo spi_flash->read_dummy, slave->speed);
1672499853d6Sryan_chen
16737d182336Sryan_chen flash->ce_ctrl_user = CE_CTRL_USERMODE;
1674edaef9d2SChin-Ting Kuo flash->max_freq = slave->speed;
1675499853d6Sryan_chen
1676ac86fa8bSryan_chen if(priv->new_ver)
1677ac86fa8bSryan_chen read_hclk = aspeed_g6_spi_hclk_divisor(priv, slave->speed);
1678ac86fa8bSryan_chen else
16797d182336Sryan_chen read_hclk = aspeed_spi_hclk_divisor(priv, slave->speed);
1680499853d6Sryan_chen
1681528cd552Sryan_chen switch(flash->spi->read_opcode) {
1682bbe907dbSChin-Ting Kuo case SPINOR_OP_READ:
1683bbe907dbSChin-Ting Kuo case SPINOR_OP_READ_4B:
1684bbe907dbSChin-Ting Kuo flash->read_iomode = CE_CTRL_IO_SINGLE;
1685bbe907dbSChin-Ting Kuo break;
1686528cd552Sryan_chen case SPINOR_OP_READ_1_1_2:
1687528cd552Sryan_chen case SPINOR_OP_READ_1_1_2_4B:
1688cd800046SChin-Ting Kuo flash->read_iomode = CE_CTRL_IO_DUAL_DATA;
1689528cd552Sryan_chen break;
1690528cd552Sryan_chen case SPINOR_OP_READ_1_1_4:
1691528cd552Sryan_chen case SPINOR_OP_READ_1_1_4_4B:
1692cd800046SChin-Ting Kuo flash->read_iomode = CE_CTRL_IO_QUAD_DATA;
1693528cd552Sryan_chen break;
1694528cd552Sryan_chen case SPINOR_OP_READ_1_4_4:
1695528cd552Sryan_chen case SPINOR_OP_READ_1_4_4_4B:
1696cd800046SChin-Ting Kuo flash->read_iomode = CE_CTRL_IO_QUAD_ADDR_DATA;
1697734f8860SChin-Ting Kuo printf("need modify dummy for 3 bytes\n");
1698528cd552Sryan_chen break;
1699499853d6Sryan_chen }
1700499853d6Sryan_chen
1701cd800046SChin-Ting Kuo switch(flash->spi->program_opcode) {
1702cd800046SChin-Ting Kuo case SPINOR_OP_PP:
1703cd800046SChin-Ting Kuo case SPINOR_OP_PP_4B:
1704cd800046SChin-Ting Kuo flash->write_iomode = CE_CTRL_IO_SINGLE;
1705cd800046SChin-Ting Kuo break;
1706cd800046SChin-Ting Kuo case SPINOR_OP_PP_1_1_4:
1707cd800046SChin-Ting Kuo case SPINOR_OP_PP_1_1_4_4B:
1708cd800046SChin-Ting Kuo flash->write_iomode = CE_CTRL_IO_QUAD_DATA;
1709cd800046SChin-Ting Kuo break;
1710cd800046SChin-Ting Kuo case SPINOR_OP_PP_1_4_4:
1711cd800046SChin-Ting Kuo case SPINOR_OP_PP_1_4_4_4B:
1712cd800046SChin-Ting Kuo flash->write_iomode = CE_CTRL_IO_QUAD_ADDR_DATA;
1713cd800046SChin-Ting Kuo printf("need modify dummy for 3 bytes");
1714cd800046SChin-Ting Kuo break;
1715cd800046SChin-Ting Kuo }
1716cd800046SChin-Ting Kuo
1717d32338fdSryan_chen if(priv->new_ver) {
17187d182336Sryan_chen flash->ce_ctrl_fread = CE_G6_CTRL_CLOCK_FREQ(read_hclk) |
1719cd800046SChin-Ting Kuo flash->read_iomode |
17207d182336Sryan_chen CE_CTRL_CMD(flash->spi->read_opcode) |
17217d182336Sryan_chen CE_CTRL_DUMMY((flash->spi->read_dummy/8)) |
17227d182336Sryan_chen CE_CTRL_FREADMODE;
1723b679b8adSChin-Ting Kuo flash->ce_ctrl_user |= CE_G6_CTRL_CLOCK_FREQ(read_hclk);
1724d32338fdSryan_chen } else {
1725499853d6Sryan_chen flash->ce_ctrl_fread = CE_CTRL_CLOCK_FREQ(read_hclk) |
1726cd800046SChin-Ting Kuo flash->read_iomode |
1727499853d6Sryan_chen CE_CTRL_CMD(flash->spi->read_opcode) |
1728499853d6Sryan_chen CE_CTRL_DUMMY((flash->spi->read_dummy/8)) |
1729499853d6Sryan_chen CE_CTRL_FREADMODE;
1730d32338fdSryan_chen }
1731499853d6Sryan_chen
17329405f2a1SChin-Ting Kuo if (flash->spi->addr_width == 4)
17339405f2a1SChin-Ting Kuo writel(readl(&priv->regs->ctrl) | 0x11 << flash->cs, &priv->regs->ctrl);
17349405f2a1SChin-Ting Kuo
1735499853d6Sryan_chen debug("CS%u: USER mode 0x%08x FREAD mode 0x%08x\n", flash->cs,
1736499853d6Sryan_chen flash->ce_ctrl_user, flash->ce_ctrl_fread);
1737499853d6Sryan_chen
1738499853d6Sryan_chen /* Set the CE Control Register default (FAST READ) */
1739499853d6Sryan_chen writel(flash->ce_ctrl_fread, &priv->regs->ce_ctrl[flash->cs]);
1740499853d6Sryan_chen
1741499853d6Sryan_chen /* Set Address Segment Register for direct AHB accesses */
17428fbbfa7dSChin-Ting Kuo ret = aspeed_spi_flash_set_segment(priv, flash);
17438fbbfa7dSChin-Ting Kuo if (ret != 0)
17448fbbfa7dSChin-Ting Kuo return ret;
1745499853d6Sryan_chen
1746beec505fSChin-Ting Kuo /*
1747beec505fSChin-Ting Kuo * Set the Read Timing Compensation Register. This setting
1748beec505fSChin-Ting Kuo * applies to all devices.
1749beec505fSChin-Ting Kuo */
175051b227c8SChin-Ting Kuo if (!dev_read_bool(bus, "timing-calibration-disabled")) {
1751edaef9d2SChin-Ting Kuo ret = aspeed_spi_timing_calibration(priv, flash);
1752beec505fSChin-Ting Kuo if (ret != 0)
1753beec505fSChin-Ting Kuo return ret;
175451b227c8SChin-Ting Kuo }
1755beec505fSChin-Ting Kuo
1756499853d6Sryan_chen /* All done */
1757499853d6Sryan_chen flash->init = true;
1758499853d6Sryan_chen
1759499853d6Sryan_chen return 0;
1760499853d6Sryan_chen }
1761499853d6Sryan_chen
aspeed_spi_claim_bus(struct udevice * dev)1762499853d6Sryan_chen static int aspeed_spi_claim_bus(struct udevice *dev)
1763499853d6Sryan_chen {
1764499853d6Sryan_chen struct udevice *bus = dev->parent;
1765499853d6Sryan_chen struct aspeed_spi_priv *priv = dev_get_priv(bus);
1766499853d6Sryan_chen struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
1767499853d6Sryan_chen struct aspeed_spi_flash *flash;
1768*eba3f26aSChin-Ting Kuo struct spi_slave *slave = dev_get_parent_priv(dev);
1769*eba3f26aSChin-Ting Kuo u32 read_hclk;
1770499853d6Sryan_chen
1771499853d6Sryan_chen debug("%s: claim bus CS%u\n", bus->name, slave_plat->cs);
1772499853d6Sryan_chen
1773499853d6Sryan_chen flash = aspeed_spi_get_flash(dev);
1774499853d6Sryan_chen if (!flash)
1775499853d6Sryan_chen return -ENODEV;
1776499853d6Sryan_chen
1777*eba3f26aSChin-Ting Kuo if (priv->new_ver) {
1778*eba3f26aSChin-Ting Kuo if (dev_read_bool(bus, "timing-calibration-disabled")) {
1779*eba3f26aSChin-Ting Kuo read_hclk = aspeed_g6_spi_hclk_divisor(priv, slave->speed);
1780*eba3f26aSChin-Ting Kuo flash->ce_ctrl_user &= CE_CTRL_FREQ_MASK;
1781*eba3f26aSChin-Ting Kuo flash->ce_ctrl_user |= CE_G6_CTRL_CLOCK_FREQ(read_hclk);
1782*eba3f26aSChin-Ting Kuo flash->ce_ctrl_fread &= CE_CTRL_FREQ_MASK;
1783*eba3f26aSChin-Ting Kuo flash->ce_ctrl_fread |= CE_G6_CTRL_CLOCK_FREQ(read_hclk);
1784*eba3f26aSChin-Ting Kuo }
1785*eba3f26aSChin-Ting Kuo }
1786*eba3f26aSChin-Ting Kuo
1787499853d6Sryan_chen return aspeed_spi_flash_init(priv, flash, dev);
1788499853d6Sryan_chen }
1789499853d6Sryan_chen
aspeed_spi_release_bus(struct udevice * dev)1790499853d6Sryan_chen static int aspeed_spi_release_bus(struct udevice *dev)
1791499853d6Sryan_chen {
1792499853d6Sryan_chen struct udevice *bus = dev->parent;
1793499853d6Sryan_chen struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
1794499853d6Sryan_chen
1795499853d6Sryan_chen debug("%s: release bus CS%u\n", bus->name, slave_plat->cs);
1796499853d6Sryan_chen
1797499853d6Sryan_chen if (!aspeed_spi_get_flash(dev))
1798499853d6Sryan_chen return -ENODEV;
1799499853d6Sryan_chen
1800499853d6Sryan_chen return 0;
1801499853d6Sryan_chen }
1802499853d6Sryan_chen
aspeed_spi_set_mode(struct udevice * bus,uint mode)1803499853d6Sryan_chen static int aspeed_spi_set_mode(struct udevice *bus, uint mode)
1804499853d6Sryan_chen {
1805499853d6Sryan_chen debug("%s: setting mode to %x\n", bus->name, mode);
1806499853d6Sryan_chen
1807499853d6Sryan_chen if (mode & (SPI_RX_QUAD | SPI_TX_QUAD)) {
180876e3c7a9Sryan_chen #ifndef CONFIG_ASPEED_AST2600
1809499853d6Sryan_chen pr_err("%s invalid QUAD IO mode\n", bus->name);
1810499853d6Sryan_chen return -EINVAL;
181176e3c7a9Sryan_chen #endif
1812499853d6Sryan_chen }
1813499853d6Sryan_chen
1814499853d6Sryan_chen /* The CE Control Register is set in claim_bus() */
1815499853d6Sryan_chen return 0;
1816499853d6Sryan_chen }
1817499853d6Sryan_chen
aspeed_spi_set_speed(struct udevice * bus,uint hz)1818499853d6Sryan_chen static int aspeed_spi_set_speed(struct udevice *bus, uint hz)
1819499853d6Sryan_chen {
1820499853d6Sryan_chen debug("%s: setting speed to %u\n", bus->name, hz);
1821499853d6Sryan_chen
1822499853d6Sryan_chen /* The CE Control Register is set in claim_bus() */
1823499853d6Sryan_chen return 0;
1824499853d6Sryan_chen }
1825499853d6Sryan_chen
aspeed_spi_count_flash_devices(struct udevice * bus)1826499853d6Sryan_chen static int aspeed_spi_count_flash_devices(struct udevice *bus)
1827499853d6Sryan_chen {
1828499853d6Sryan_chen ofnode node;
1829499853d6Sryan_chen int count = 0;
1830499853d6Sryan_chen
1831499853d6Sryan_chen dev_for_each_subnode(node, bus) {
1832499853d6Sryan_chen if (ofnode_is_available(node) &&
18339544b743SCédric Le Goater (ofnode_device_is_compatible(node, "spi-flash") ||
18349544b743SCédric Le Goater ofnode_device_is_compatible(node, "jedec,spi-nor")))
1835499853d6Sryan_chen count++;
1836499853d6Sryan_chen }
1837499853d6Sryan_chen
1838499853d6Sryan_chen return count;
1839499853d6Sryan_chen }
1840499853d6Sryan_chen
aspeed_spi_bind(struct udevice * bus)1841499853d6Sryan_chen static int aspeed_spi_bind(struct udevice *bus)
1842499853d6Sryan_chen {
1843499853d6Sryan_chen debug("%s assigned req_seq=%d seq=%d\n", bus->name, bus->req_seq,
1844499853d6Sryan_chen bus->seq);
1845499853d6Sryan_chen
1846499853d6Sryan_chen return 0;
1847499853d6Sryan_chen }
1848499853d6Sryan_chen
aspeed_spi_probe(struct udevice * bus)1849499853d6Sryan_chen static int aspeed_spi_probe(struct udevice *bus)
1850499853d6Sryan_chen {
1851499853d6Sryan_chen struct resource res_regs, res_ahb;
1852499853d6Sryan_chen struct aspeed_spi_priv *priv = dev_get_priv(bus);
1853499853d6Sryan_chen struct clk hclk;
1854499853d6Sryan_chen int ret;
1855499853d6Sryan_chen
1856499853d6Sryan_chen ret = dev_read_resource(bus, 0, &res_regs);
1857499853d6Sryan_chen if (ret < 0)
1858499853d6Sryan_chen return ret;
1859499853d6Sryan_chen
1860499853d6Sryan_chen priv->regs = (void __iomem *)res_regs.start;
1861499853d6Sryan_chen
1862499853d6Sryan_chen ret = dev_read_resource(bus, 1, &res_ahb);
1863499853d6Sryan_chen if (ret < 0)
1864499853d6Sryan_chen return ret;
1865499853d6Sryan_chen
1866499853d6Sryan_chen priv->ahb_base = (void __iomem *)res_ahb.start;
1867742f43eeSChin-Ting Kuo priv->ahb_size = res_ahb.end - res_ahb.start + 1;
1868499853d6Sryan_chen
1869499853d6Sryan_chen ret = clk_get_by_index(bus, 0, &hclk);
1870499853d6Sryan_chen if (ret < 0) {
1871499853d6Sryan_chen pr_err("%s could not get clock: %d\n", bus->name, ret);
1872499853d6Sryan_chen return ret;
1873499853d6Sryan_chen }
1874499853d6Sryan_chen
1875499853d6Sryan_chen priv->hclk_rate = clk_get_rate(&hclk);
1876499853d6Sryan_chen clk_free(&hclk);
1877499853d6Sryan_chen
1878499853d6Sryan_chen priv->num_cs = dev_read_u32_default(bus, "num-cs", ASPEED_SPI_MAX_CS);
1879499853d6Sryan_chen
1880499853d6Sryan_chen priv->flash_count = aspeed_spi_count_flash_devices(bus);
1881499853d6Sryan_chen if (priv->flash_count > priv->num_cs) {
1882499853d6Sryan_chen pr_err("%s has too many flash devices: %d\n", bus->name,
1883499853d6Sryan_chen priv->flash_count);
1884499853d6Sryan_chen return -EINVAL;
1885499853d6Sryan_chen }
1886499853d6Sryan_chen
1887499853d6Sryan_chen if (!priv->flash_count) {
1888499853d6Sryan_chen pr_err("%s has no flash devices ?!\n", bus->name);
1889499853d6Sryan_chen return -ENODEV;
1890499853d6Sryan_chen }
1891499853d6Sryan_chen
18927d182336Sryan_chen if (device_is_compatible(bus, "aspeed,ast2600-fmc") ||
1893f87fadc3Sryan_chen device_is_compatible(bus, "aspeed,ast2600-spi")) {
1894499853d6Sryan_chen priv->new_ver = 1;
1895499853d6Sryan_chen }
1896499853d6Sryan_chen
1897bbe907dbSChin-Ting Kuo if (dev_read_bool(bus, "aspeed-spi-command-mode")) {
1898bbe907dbSChin-Ting Kuo debug("adopt command mode\n");
1899bbe907dbSChin-Ting Kuo priv->tmp_buf = memalign(4, 512);
1900bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd = aspeed_spi_exec_op_cmd_mode;
1901bbe907dbSChin-Ting Kuo } else {
1902bbe907dbSChin-Ting Kuo priv->spi_exec_op_cmd = NULL;
1903bbe907dbSChin-Ting Kuo }
1904bbe907dbSChin-Ting Kuo
1905499853d6Sryan_chen /*
1906499853d6Sryan_chen * There are some slight differences between the FMC and the
1907499853d6Sryan_chen * SPI controllers
1908499853d6Sryan_chen */
1909499853d6Sryan_chen priv->is_fmc = dev_get_driver_data(bus);
1910499853d6Sryan_chen
1911499853d6Sryan_chen ret = aspeed_spi_controller_init(priv);
1912499853d6Sryan_chen if (ret)
1913499853d6Sryan_chen return ret;
1914499853d6Sryan_chen
1915edaef9d2SChin-Ting Kuo debug("%s probed regs=%p ahb_base=%p cs_num=%d seq=%d\n",
1916edaef9d2SChin-Ting Kuo bus->name, priv->regs, priv->ahb_base, priv->flash_count, bus->seq);
1917499853d6Sryan_chen
1918499853d6Sryan_chen return 0;
1919499853d6Sryan_chen }
1920499853d6Sryan_chen
1921499853d6Sryan_chen static const struct dm_spi_ops aspeed_spi_ops = {
1922499853d6Sryan_chen .claim_bus = aspeed_spi_claim_bus,
1923499853d6Sryan_chen .release_bus = aspeed_spi_release_bus,
1924499853d6Sryan_chen .set_mode = aspeed_spi_set_mode,
1925499853d6Sryan_chen .set_speed = aspeed_spi_set_speed,
1926499853d6Sryan_chen .xfer = aspeed_spi_xfer,
1927591e1cf0SChin-Ting Kuo #ifdef CONFIG_ASPEED_SPI_FLASH_WRITE_PROTECTION
1928591e1cf0SChin-Ting Kuo .mem_ctrl_wlock = aspeed_spi_mem_wlock,
1929591e1cf0SChin-Ting Kuo .mem_ctrl_wunlock = aspeed_spi_mem_wunlock,
1930591e1cf0SChin-Ting Kuo #endif
1931499853d6Sryan_chen };
1932499853d6Sryan_chen
1933499853d6Sryan_chen static const struct udevice_id aspeed_spi_ids[] = {
1934499853d6Sryan_chen { .compatible = "aspeed,ast2600-fmc", .data = 1 },
1935499853d6Sryan_chen { .compatible = "aspeed,ast2600-spi", .data = 0 },
1936499853d6Sryan_chen { .compatible = "aspeed,ast2500-fmc", .data = 1 },
1937499853d6Sryan_chen { .compatible = "aspeed,ast2500-spi", .data = 0 },
1938c4475966Sryan_chen { .compatible = "aspeed,ast2400-fmc", .data = 1 },
1939499853d6Sryan_chen { }
1940499853d6Sryan_chen };
1941499853d6Sryan_chen
1942499853d6Sryan_chen U_BOOT_DRIVER(aspeed_spi) = {
1943499853d6Sryan_chen .name = "aspeed_spi",
1944499853d6Sryan_chen .id = UCLASS_SPI,
1945499853d6Sryan_chen .of_match = aspeed_spi_ids,
1946499853d6Sryan_chen .ops = &aspeed_spi_ops,
1947499853d6Sryan_chen .priv_auto_alloc_size = sizeof(struct aspeed_spi_priv),
1948499853d6Sryan_chen .child_pre_probe = aspeed_spi_child_pre_probe,
1949499853d6Sryan_chen .bind = aspeed_spi_bind,
1950499853d6Sryan_chen .probe = aspeed_spi_probe,
1951499853d6Sryan_chen };
1952