]> git.sur5r.net Git - u-boot/blob - drivers/mtd/altera_qspi.c
pci: correct a function description
[u-boot] / drivers / mtd / altera_qspi.c
1 /*
2  * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 #include <common.h>
8 #include <console.h>
9 #include <dm.h>
10 #include <errno.h>
11 #include <fdt_support.h>
12 #include <flash.h>
13 #include <mtd.h>
14 #include <asm/io.h>
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 /* The STATUS register */
19 #define QUADSPI_SR_BP0                          BIT(2)
20 #define QUADSPI_SR_BP1                          BIT(3)
21 #define QUADSPI_SR_BP2                          BIT(4)
22 #define QUADSPI_SR_BP2_0                        GENMASK(4, 2)
23 #define QUADSPI_SR_BP3                          BIT(6)
24 #define QUADSPI_SR_TB                           BIT(5)
25
26 /*
27  * The QUADSPI_MEM_OP register is used to do memory protect and erase operations
28  */
29 #define QUADSPI_MEM_OP_BULK_ERASE               0x00000001
30 #define QUADSPI_MEM_OP_SECTOR_ERASE             0x00000002
31 #define QUADSPI_MEM_OP_SECTOR_PROTECT           0x00000003
32
33 /*
34  * The QUADSPI_ISR register is used to determine whether an invalid write or
35  * erase operation trigerred an interrupt
36  */
37 #define QUADSPI_ISR_ILLEGAL_ERASE               BIT(0)
38 #define QUADSPI_ISR_ILLEGAL_WRITE               BIT(1)
39
40 struct altera_qspi_regs {
41         u32     rd_status;
42         u32     rd_sid;
43         u32     rd_rdid;
44         u32     mem_op;
45         u32     isr;
46         u32     imr;
47         u32     chip_select;
48 };
49
50 struct altera_qspi_platdata {
51         struct altera_qspi_regs *regs;
52         void *base;
53         unsigned long size;
54 };
55
56 static uint flash_verbose;
57 flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];    /* FLASH chips info */
58
59 static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs,
60                                          uint64_t *len);
61
62 void flash_print_info(flash_info_t *info)
63 {
64         struct mtd_info *mtd = info->mtd;
65         loff_t ofs;
66         u64 len;
67
68         printf("Altera QSPI flash  Size: %ld MB in %d Sectors\n",
69                info->size >> 20, info->sector_count);
70         altera_qspi_get_locked_range(mtd, &ofs, &len);
71         printf("  %08lX +%lX", info->start[0], info->size);
72         if (len) {
73                 printf(", protected %08llX +%llX",
74                        info->start[0] + ofs, len);
75         }
76         putc('\n');
77 }
78
79 void flash_set_verbose(uint v)
80 {
81         flash_verbose = v;
82 }
83
84 int flash_erase(flash_info_t *info, int s_first, int s_last)
85 {
86         struct mtd_info *mtd = info->mtd;
87         struct erase_info instr;
88         int ret;
89
90         memset(&instr, 0, sizeof(instr));
91         instr.mtd = mtd;
92         instr.addr = mtd->erasesize * s_first;
93         instr.len = mtd->erasesize * (s_last + 1 - s_first);
94         flash_set_verbose(1);
95         ret = mtd_erase(mtd, &instr);
96         flash_set_verbose(0);
97         if (ret)
98                 return ERR_PROTECTED;
99
100         puts(" done\n");
101         return 0;
102 }
103
104 int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
105 {
106         struct mtd_info *mtd = info->mtd;
107         struct udevice *dev = mtd->dev;
108         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
109         ulong base = (ulong)pdata->base;
110         loff_t to = addr - base;
111         size_t retlen;
112         int ret;
113
114         ret = mtd_write(mtd, to, cnt, &retlen, src);
115         if (ret)
116                 return ERR_PROTECTED;
117
118         return 0;
119 }
120
121 unsigned long flash_init(void)
122 {
123         struct udevice *dev;
124
125         /* probe every MTD device */
126         for (uclass_first_device(UCLASS_MTD, &dev);
127              dev;
128              uclass_next_device(&dev)) {
129         }
130
131         return flash_info[0].size;
132 }
133
134 static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr)
135 {
136         struct udevice *dev = mtd->dev;
137         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
138         struct altera_qspi_regs *regs = pdata->regs;
139         size_t addr = instr->addr;
140         size_t len = instr->len;
141         size_t end = addr + len;
142         u32 sect;
143         u32 stat;
144         u32 *flash, *last;
145
146         instr->state = MTD_ERASING;
147         addr &= ~(mtd->erasesize - 1); /* get lower aligned address */
148         while (addr < end) {
149                 if (ctrlc()) {
150                         if (flash_verbose)
151                                 putc('\n');
152                         instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
153                         instr->state = MTD_ERASE_FAILED;
154                         mtd_erase_callback(instr);
155                         return -EIO;
156                 }
157                 flash = pdata->base + addr;
158                 last = pdata->base + addr + mtd->erasesize;
159                 /* skip erase if sector is blank */
160                 while (flash < last) {
161                         if (readl(flash) != 0xffffffff)
162                                 break;
163                         flash++;
164                 }
165                 if (flash < last) {
166                         sect = addr / mtd->erasesize;
167                         sect <<= 8;
168                         sect |= QUADSPI_MEM_OP_SECTOR_ERASE;
169                         debug("erase %08x\n", sect);
170                         writel(sect, &regs->mem_op);
171                         stat = readl(&regs->isr);
172                         if (stat & QUADSPI_ISR_ILLEGAL_ERASE) {
173                                 /* erase failed, sector might be protected */
174                                 debug("erase %08x fail %x\n", sect, stat);
175                                 writel(stat, &regs->isr); /* clear isr */
176                                 instr->fail_addr = addr;
177                                 instr->state = MTD_ERASE_FAILED;
178                                 mtd_erase_callback(instr);
179                                 return -EIO;
180                         }
181                         if (flash_verbose)
182                                 putc('.');
183                 } else {
184                         if (flash_verbose)
185                                 putc(',');
186                 }
187                 addr += mtd->erasesize;
188         }
189         instr->state = MTD_ERASE_DONE;
190         mtd_erase_callback(instr);
191
192         return 0;
193 }
194
195 static int altera_qspi_read(struct mtd_info *mtd, loff_t from, size_t len,
196                             size_t *retlen, u_char *buf)
197 {
198         struct udevice *dev = mtd->dev;
199         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
200
201         memcpy_fromio(buf, pdata->base + from, len);
202         *retlen = len;
203
204         return 0;
205 }
206
207 static int altera_qspi_write(struct mtd_info *mtd, loff_t to, size_t len,
208                              size_t *retlen, const u_char *buf)
209 {
210         struct udevice *dev = mtd->dev;
211         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
212         struct altera_qspi_regs *regs = pdata->regs;
213         u32 stat;
214
215         memcpy_toio(pdata->base + to, buf, len);
216         /* check whether write triggered a illegal write interrupt */
217         stat = readl(&regs->isr);
218         if (stat & QUADSPI_ISR_ILLEGAL_WRITE) {
219                 /* write failed, sector might be protected */
220                 debug("write fail %x\n", stat);
221                 writel(stat, &regs->isr); /* clear isr */
222                 return -EIO;
223         }
224         *retlen = len;
225
226         return 0;
227 }
228
229 static void altera_qspi_sync(struct mtd_info *mtd)
230 {
231 }
232
233 static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs,
234                                          uint64_t *len)
235 {
236         struct udevice *dev = mtd->dev;
237         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
238         struct altera_qspi_regs *regs = pdata->regs;
239         int shift0 = ffs(QUADSPI_SR_BP2_0) - 1;
240         int shift3 = ffs(QUADSPI_SR_BP3) - 1 - 3;
241         u32 stat = readl(&regs->rd_status);
242         unsigned pow = ((stat & QUADSPI_SR_BP2_0) >> shift0) |
243                 ((stat & QUADSPI_SR_BP3) >> shift3);
244
245         *ofs = 0;
246         *len = 0;
247         if (pow) {
248                 *len = mtd->erasesize << (pow - 1);
249                 if (*len > mtd->size)
250                         *len = mtd->size;
251                 if (!(stat & QUADSPI_SR_TB))
252                         *ofs = mtd->size - *len;
253         }
254 }
255
256 static int altera_qspi_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
257 {
258         struct udevice *dev = mtd->dev;
259         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
260         struct altera_qspi_regs *regs = pdata->regs;
261         u32 sector_start, sector_end;
262         u32 num_sectors;
263         u32 mem_op;
264         u32 sr_bp;
265         u32 sr_tb;
266
267         num_sectors = mtd->size / mtd->erasesize;
268         sector_start = ofs / mtd->erasesize;
269         sector_end = (ofs + len) / mtd->erasesize;
270
271         if (sector_start >= num_sectors / 2) {
272                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
273                 sr_tb = 0;
274         } else if (sector_end < num_sectors / 2) {
275                 sr_bp = fls(sector_end) + 1;
276                 sr_tb = 1;
277         } else {
278                 sr_bp = 15;
279                 sr_tb = 0;
280         }
281
282         mem_op = (sr_tb << 12) | (sr_bp << 8);
283         mem_op |= QUADSPI_MEM_OP_SECTOR_PROTECT;
284         debug("lock %08x\n", mem_op);
285         writel(mem_op, &regs->mem_op);
286
287         return 0;
288 }
289
290 static int altera_qspi_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
291 {
292         struct udevice *dev = mtd->dev;
293         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
294         struct altera_qspi_regs *regs = pdata->regs;
295         u32 mem_op;
296
297         mem_op = QUADSPI_MEM_OP_SECTOR_PROTECT;
298         debug("unlock %08x\n", mem_op);
299         writel(mem_op, &regs->mem_op);
300
301         return 0;
302 }
303
304 static int altera_qspi_probe(struct udevice *dev)
305 {
306         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
307         struct altera_qspi_regs *regs = pdata->regs;
308         unsigned long base = (unsigned long)pdata->base;
309         struct mtd_info *mtd;
310         flash_info_t *flash = &flash_info[0];
311         u32 rdid;
312         int i;
313
314         rdid = readl(&regs->rd_rdid);
315         debug("rdid %x\n", rdid);
316
317         mtd = dev_get_uclass_priv(dev);
318         mtd->dev = dev;
319         mtd->name               = "nor0";
320         mtd->type               = MTD_NORFLASH;
321         mtd->flags              = MTD_CAP_NORFLASH;
322         mtd->size               = 1 << ((rdid & 0xff) - 6);
323         mtd->writesize          = 1;
324         mtd->writebufsize       = mtd->writesize;
325         mtd->_erase             = altera_qspi_erase;
326         mtd->_read              = altera_qspi_read;
327         mtd->_write             = altera_qspi_write;
328         mtd->_sync              = altera_qspi_sync;
329         mtd->_lock              = altera_qspi_lock;
330         mtd->_unlock            = altera_qspi_unlock;
331         mtd->numeraseregions = 0;
332         mtd->erasesize = 0x10000;
333         if (add_mtd_device(mtd))
334                 return -ENOMEM;
335
336         flash->mtd = mtd;
337         flash->size = mtd->size;
338         flash->sector_count = mtd->size / mtd->erasesize;
339         flash->flash_id = rdid;
340         flash->start[0] = base;
341         for (i = 1; i < flash->sector_count; i++)
342                 flash->start[i] = flash->start[i - 1] + mtd->erasesize;
343         gd->bd->bi_flashstart = base;
344
345         return 0;
346 }
347
348 static int altera_qspi_ofdata_to_platdata(struct udevice *dev)
349 {
350         struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
351         void *blob = (void *)gd->fdt_blob;
352         int node = dev_of_offset(dev);
353         const char *list, *end;
354         const fdt32_t *cell;
355         void *base;
356         unsigned long addr, size;
357         int parent, addrc, sizec;
358         int len, idx;
359
360         /*
361          * decode regs. there are multiple reg tuples, and they need to
362          * match with reg-names.
363          */
364         parent = fdt_parent_offset(blob, node);
365         of_bus_default_count_cells(blob, parent, &addrc, &sizec);
366         list = fdt_getprop(blob, node, "reg-names", &len);
367         if (!list)
368                 return -ENOENT;
369         end = list + len;
370         cell = fdt_getprop(blob, node, "reg", &len);
371         if (!cell)
372                 return -ENOENT;
373         idx = 0;
374         while (list < end) {
375                 addr = fdt_translate_address((void *)blob,
376                                              node, cell + idx);
377                 size = fdt_addr_to_cpu(cell[idx + addrc]);
378                 base = map_physmem(addr, size, MAP_NOCACHE);
379                 len = strlen(list);
380                 if (strcmp(list, "avl_csr") == 0) {
381                         pdata->regs = base;
382                 } else if (strcmp(list, "avl_mem") == 0) {
383                         pdata->base = base;
384                         pdata->size = size;
385                 }
386                 idx += addrc + sizec;
387                 list += (len + 1);
388         }
389
390         return 0;
391 }
392
393 static const struct udevice_id altera_qspi_ids[] = {
394         { .compatible = "altr,quadspi-1.0" },
395         {}
396 };
397
398 U_BOOT_DRIVER(altera_qspi) = {
399         .name   = "altera_qspi",
400         .id     = UCLASS_MTD,
401         .of_match = altera_qspi_ids,
402         .ofdata_to_platdata = altera_qspi_ofdata_to_platdata,
403         .platdata_auto_alloc_size = sizeof(struct altera_qspi_platdata),
404         .probe  = altera_qspi_probe,
405 };