kernel: update bcma and ssb to master-2012-10-18 from wireless-testing
[openwrt.git] / target / linux / brcm47xx / patches-3.3 / 071-bcma-add-functions-to-write-to-nand-flash.patch
1 --- a/drivers/bcma/driver_chipcommon_nflash.c
2 +++ b/drivers/bcma/driver_chipcommon_nflash.c
3 @@ -2,16 +2,23 @@
4   * Broadcom specific AMBA
5   * ChipCommon NAND flash interface
6   *
7 + * Copyright 2011, Tathagata Das <tathagata@alumnux.com>
8 + * Copyright 2010, Broadcom Corporation
9 + *
10   * Licensed under the GNU/GPL. See COPYING for details.
11   */
12  
13 +#include <linux/delay.h>
14 +#include <linux/mtd/bcm47xx_nand.h>
15 +#include <linux/mtd/nand.h>
16  #include <linux/platform_device.h>
17  #include <linux/bcma/bcma.h>
18 +#include <linux/bcma/bcma_driver_chipcommon.h>
19  
20  #include "bcma_private.h"
21  
22  struct platform_device bcma_nflash_dev = {
23 -       .name           = "bcma_nflash",
24 +       .name           = "bcm47xx-nflash",
25         .num_resources  = 0,
26  };
27  
28 @@ -31,6 +38,11 @@ int bcma_nflash_init(struct bcma_drv_cc
29                 return -ENODEV;
30         }
31  
32 +       if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
33 +               bcma_err(bus, "NAND flash support for BCM4706 not implemented\n");
34 +               return -ENOTSUPP;
35 +       }
36 +
37         cc->nflash.present = true;
38         if (cc->core->id.rev == 38 &&
39             (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT))
40 @@ -42,3 +54,141 @@ int bcma_nflash_init(struct bcma_drv_cc
41  
42         return 0;
43  }
44 +
45 +/* Issue a nand flash command */
46 +static inline void bcma_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode)
47 +{
48 +       bcma_cc_write32(cc, NAND_CMD_START, opcode);
49 +       bcma_cc_read32(cc,  NAND_CMD_START);
50 +}
51 +
52 +/* Check offset and length */
53 +static int bcma_nflash_offset_is_valid(struct bcma_drv_cc *cc, u32 offset, u32 len, u32 mask)
54 +{
55 +       if ((offset & mask) != 0 || (len & mask) != 0) {
56 +               pr_err("%s(): Address is not aligned. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask);
57 +               return 1;
58 +       }
59 +
60 +       if ((((offset + len) >> 20) >= cc->nflash.size) &&
61 +               (((offset + len) & ((1 << 20) - 1)) != 0)) {
62 +               pr_err("%s(): Address is outside Flash memory region. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask);
63 +               return 1;
64 +       }
65 +
66 +       return 0;
67 +}
68 +
69 +#define NF_RETRIES   1000000
70 +
71 +/* Poll for command completion. Returns zero when complete. */
72 +int bcma_nflash_poll(struct bcma_drv_cc *cc)
73 +{
74 +       u32 retries = NF_RETRIES;
75 +       u32 pollmask = NIST_CTRL_READY|NIST_FLASH_READY;
76 +       u32 mask;
77 +
78 +       while (retries--) {
79 +               mask = bcma_cc_read32(cc, NAND_INTFC_STATUS) & pollmask;
80 +               if (mask == pollmask)
81 +                       return 0;
82 +               cpu_relax();
83 +       }
84 +
85 +       if (!retries) {
86 +               pr_err("bcma_nflash_poll: not ready\n");
87 +               return -1;
88 +       }
89 +
90 +       return 0;
91 +}
92 +
93 +/* Read len bytes starting at offset into buf. Returns number of bytes read. */
94 +int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf)
95 +{
96 +       u32 mask;
97 +       int i;
98 +       u32 *to, val, res;
99 +
100 +       mask = NFL_SECTOR_SIZE - 1;
101 +       if (bcma_nflash_offset_is_valid(cc, offset, len, mask))
102 +               return 0;
103 +
104 +       to = (u32 *)buf;
105 +       res = len;
106 +       while (res > 0) {
107 +               bcma_cc_write32(cc, NAND_CMD_ADDR, offset);
108 +               bcma_nflash_cmd(cc, NCMD_PAGE_RD);
109 +               if (bcma_nflash_poll(cc) < 0)
110 +                       break;
111 +               val = bcma_cc_read32(cc, NAND_INTFC_STATUS);
112 +               if ((val & NIST_CACHE_VALID) == 0)
113 +                       break;
114 +               bcma_cc_write32(cc, NAND_CACHE_ADDR, 0);
115 +               for (i = 0; i < NFL_SECTOR_SIZE; i += 4, to++) {
116 +                       *to = bcma_cc_read32(cc, NAND_CACHE_DATA);
117 +               }
118 +               res -= NFL_SECTOR_SIZE;
119 +               offset += NFL_SECTOR_SIZE;
120 +       }
121 +       return (len - res);
122 +}
123 +
124 +/* Write len bytes starting at offset into buf. Returns success (0) or failure (!0).
125 + * Should poll for completion.
126 + */
127 +int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len,
128 +                           const u8 *buf)
129 +{
130 +       u32 mask;
131 +       int i;
132 +       u32 *from, res, reg;
133 +
134 +       mask = cc->nflash.pagesize - 1;
135 +       if (bcma_nflash_offset_is_valid(cc, offset, len, mask))
136 +               return 1;
137 +
138 +       /* disable partial page enable */
139 +       reg = bcma_cc_read32(cc, NAND_ACC_CONTROL);
140 +       reg &= ~NAC_PARTIAL_PAGE_EN;
141 +       bcma_cc_write32(cc, NAND_ACC_CONTROL, reg);
142 +
143 +       from = (u32 *)buf;
144 +       res = len;
145 +       while (res > 0) {
146 +               bcma_cc_write32(cc, NAND_CACHE_ADDR, 0);
147 +               for (i = 0; i < cc->nflash.pagesize; i += 4, from++) {
148 +                       if (i % 512 == 0)
149 +                               bcma_cc_write32(cc, NAND_CMD_ADDR, i);
150 +                       bcma_cc_write32(cc, NAND_CACHE_DATA, *from);
151 +               }
152 +               bcma_cc_write32(cc, NAND_CMD_ADDR, offset + cc->nflash.pagesize - 512);
153 +               bcma_nflash_cmd(cc, NCMD_PAGE_PROG);
154 +               if (bcma_nflash_poll(cc) < 0)
155 +                       break;
156 +               res -= cc->nflash.pagesize;
157 +               offset += cc->nflash.pagesize;
158 +       }
159 +
160 +       if (res <= 0)
161 +               return 0;
162 +       else
163 +               return (len - res);
164 +}
165 +
166 +/* Erase a region. Returns success (0) or failure (-1).
167 + * Poll for completion.
168 + */
169 +int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset)
170 +{
171 +       if ((offset >> 20) >= cc->nflash.size)
172 +               return -1;
173 +       if ((offset & (cc->nflash.blocksize - 1)) != 0)
174 +               return -1;
175 +
176 +       bcma_cc_write32(cc, NAND_CMD_ADDR, offset);
177 +       bcma_nflash_cmd(cc, NCMD_BLOCK_ERASE);
178 +       if (bcma_nflash_poll(cc) < 0)
179 +               return -1;
180 +       return 0;
181 +}
182 --- a/include/linux/bcma/bcma_driver_chipcommon.h
183 +++ b/include/linux/bcma/bcma_driver_chipcommon.h
184 @@ -2,6 +2,7 @@
185  #define LINUX_BCMA_DRIVER_CC_H_
186  
187  #include <linux/mtd/bcm47xx_sflash.h>
188 +#include <linux/mtd/bcm47xx_nand.h>
189  
190  /** ChipCommon core registers. **/
191  #define BCMA_CC_ID                     0x0000
192 @@ -519,17 +520,6 @@ struct bcma_pflash {
193  };
194  
195  
196 -#ifdef CONFIG_BCMA_NFLASH
197 -struct mtd_info;
198 -
199 -struct bcma_nflash {
200 -       bool present;
201 -       bool boot;              /* This is the flash the SoC boots from */
202 -
203 -       struct mtd_info *mtd;
204 -};
205 -#endif
206 -
207  struct bcma_serial_port {
208         void *regs;
209         unsigned long clockspeed;
210 @@ -555,7 +545,7 @@ struct bcma_drv_cc {
211         struct bcm47xx_sflash sflash;
212  #endif
213  #ifdef CONFIG_BCMA_NFLASH
214 -       struct bcma_nflash nflash;
215 +       struct bcm47xx_nflash nflash;
216  #endif
217  
218         int nr_serial_ports;
219 @@ -613,4 +603,13 @@ extern void bcma_chipco_regctl_maskset(s
220                                        u32 offset, u32 mask, u32 set);
221  extern void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid);
222  
223 +#ifdef CONFIG_BCMA_NFLASH
224 +/* Chipcommon nflash support. */
225 +int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf);
226 +int bcma_nflash_poll(struct bcma_drv_cc *cc);
227 +int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf);
228 +int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset);
229 +int bcma_nflash_commit(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf);
230 +#endif
231 +
232  #endif /* LINUX_BCMA_DRIVER_CC_H_ */