• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/mtd/bcm947xx/devices/
1/*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id$
19 */
20
21#include <linux/version.h>
22
23#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
24#include <linux/config.h>
25#endif
26#include <linux/module.h>
27#include <linux/slab.h>
28#include <linux/ioport.h>
29#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
30#include <linux/mtd/compatmac.h>
31#endif
32#include <linux/mtd/mtd.h>
33#include <linux/mtd/partitions.h>
34#include <linux/errno.h>
35#include <linux/pci.h>
36#include <linux/delay.h>
37#include <asm/io.h>
38
39#include <typedefs.h>
40#include <osl.h>
41#include <bcmutils.h>
42#include <bcmdevs.h>
43#include <bcmnvram.h>
44#include <siutils.h>
45#include <hndpci.h>
46#include <pcicfg.h>
47#include <hndsoc.h>
48#include <hndsflash.h>
49
50
51#ifdef CONFIG_MTD_PARTITIONS
52extern struct mtd_partition *
53init_mtd_partitions(hndsflash_t *sfl, struct mtd_info *mtd, size_t size);
54#endif
55
56extern void *partitions_lock_init(void);
57#define	BCMSFLASH_LOCK(lock)		if (lock) spin_lock(lock)
58#define	BCMSFLASH_UNLOCK(lock)	if (lock) spin_unlock(lock)
59
60struct bcmsflash_mtd {
61	si_t *sih;
62	hndsflash_t *sfl;
63	struct mtd_info mtd;
64	struct mtd_erase_region_info region;
65};
66
67/* Private global state */
68static struct bcmsflash_mtd bcmsflash;
69
70static int
71bcmsflash_mtd_poll(hndsflash_t *sfl, unsigned int offset, int timeout)
72{
73	int now = jiffies;
74	int ret = 0;
75
76	for (;;) {
77		if (!hndsflash_poll(sfl, offset))
78			break;
79
80		if (time_after((unsigned long)jiffies, (unsigned long)now + timeout)) {
81			if (!hndsflash_poll(sfl, offset))
82				break;
83
84			printk(KERN_ERR "sflash: timeout\n");
85			ret = -ETIMEDOUT;
86			break;
87		}
88		udelay(1);
89	}
90
91	return ret;
92}
93
94static int
95bcmsflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
96{
97	hndsflash_t *sfl = ((struct bcmsflash_mtd *)mtd->priv)->sfl;
98	int bytes, ret = 0;
99
100	/* Check address range */
101	if (!len)
102		return 0;
103	if ((from + len) > mtd->size)
104		return -EINVAL;
105
106	BCMSFLASH_LOCK(mtd->mlock);
107
108	*retlen = 0;
109	while (len) {
110		if ((bytes = hndsflash_read(sfl, (uint)from, len, buf))
111			< 0) {
112			ret = bytes;
113			break;
114		}
115		from += (loff_t) bytes;
116		len -= bytes;
117		buf += bytes;
118		*retlen += bytes;
119	}
120
121	BCMSFLASH_UNLOCK(mtd->mlock);
122	return ret;
123}
124
125static int
126bcmsflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
127{
128	hndsflash_t *sfl = ((struct bcmsflash_mtd *)mtd->priv)->sfl;
129	int bytes, ret = 0;
130
131	/* Check address range */
132	if (!len)
133		return 0;
134	if ((to + len) > mtd->size)
135		return -EINVAL;
136
137	BCMSFLASH_LOCK(mtd->mlock);
138	*retlen = 0;
139	while (len) {
140		if ((bytes = hndsflash_write(sfl, (uint)to, len, (u_char *)buf))
141			< 0) {
142			ret = bytes;
143			break;
144		}
145		if ((ret = bcmsflash_mtd_poll(sfl, (unsigned int)to, HZ)))
146			break;
147		to += (loff_t) bytes;
148		len -= bytes;
149		buf += bytes;
150		*retlen += bytes;
151	}
152
153	BCMSFLASH_UNLOCK(mtd->mlock);
154	return ret;
155}
156
157static int
158bcmsflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
159{
160	hndsflash_t *sfl = ((struct bcmsflash_mtd *)mtd->priv)->sfl;
161	int i, j, ret = 0;
162	unsigned int addr, len;
163
164	/* Check address range */
165	if (!erase->len)
166		return 0;
167	if ((erase->addr + erase->len) > mtd->size)
168		return -EINVAL;
169
170	BCMSFLASH_LOCK(mtd->mlock);
171	addr = erase->addr;
172	len = erase->len;
173
174	/* Ensure that requested region is aligned */
175	for (i = 0; i < mtd->numeraseregions; i++) {
176		for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
177			if (addr == mtd->eraseregions[i].offset +
178			    mtd->eraseregions[i].erasesize * j &&
179			    len >= mtd->eraseregions[i].erasesize) {
180				if ((ret = hndsflash_erase(sfl, addr)) < 0)
181					break;
182				if ((ret = bcmsflash_mtd_poll(sfl, addr, 10 * HZ)))
183					break;
184				addr += mtd->eraseregions[i].erasesize;
185				len -= mtd->eraseregions[i].erasesize;
186			}
187		}
188		if (ret)
189			break;
190	}
191
192	/* Set erase status */
193	if (ret < 0)
194		erase->state = MTD_ERASE_FAILED;
195	else {
196		erase->state = MTD_ERASE_DONE;
197		ret = 0;
198	}
199
200	BCMSFLASH_UNLOCK(mtd->mlock);
201
202	/* Call erase callback */
203	if (erase->callback)
204		erase->callback(erase);
205
206	return ret;
207}
208
209static int __init
210bcmsflash_mtd_init(void)
211{
212	int ret = 0;
213	hndsflash_t *info;
214#ifdef CONFIG_MTD_PARTITIONS
215	struct mtd_partition *parts;
216	int i;
217#endif
218
219	memset(&bcmsflash, 0, sizeof(struct bcmsflash_mtd));
220
221	/* attach to the backplane */
222	if (!(bcmsflash.sih = si_kattach(SI_OSH))) {
223		printk(KERN_ERR "bcmsflash: error attaching to backplane\n");
224		ret = -EIO;
225		goto fail;
226	}
227
228	/* Initialize serial flash access */
229	if (!(info = hndsflash_init(bcmsflash.sih))) {
230		printk(KERN_ERR "bcmsflash: found no supported devices\n");
231		ret = -ENODEV;
232		goto fail;
233	}
234	bcmsflash.sfl = info;
235
236	/* Setup region info */
237	bcmsflash.region.offset = 0;
238	bcmsflash.region.erasesize = info->blocksize;
239	bcmsflash.region.numblocks = info->numblocks;
240	if (bcmsflash.region.erasesize > bcmsflash.mtd.erasesize)
241		bcmsflash.mtd.erasesize = bcmsflash.region.erasesize;
242	bcmsflash.mtd.size = info->size;
243	bcmsflash.mtd.numeraseregions = 1;
244
245	/* Register with MTD */
246	bcmsflash.mtd.name = "bcmsflash";
247	bcmsflash.mtd.type = MTD_NORFLASH;
248	bcmsflash.mtd.flags = MTD_CAP_NORFLASH;
249	bcmsflash.mtd.eraseregions = &bcmsflash.region;
250	bcmsflash.mtd.erase = bcmsflash_mtd_erase;
251	bcmsflash.mtd.read = bcmsflash_mtd_read;
252	bcmsflash.mtd.write = bcmsflash_mtd_write;
253	bcmsflash.mtd.writesize = 1;
254	bcmsflash.mtd.priv = &bcmsflash;
255	bcmsflash.mtd.owner = THIS_MODULE;
256	bcmsflash.mtd.mlock = partitions_lock_init();
257	if (!bcmsflash.mtd.mlock)
258		return -ENOMEM;
259
260#ifdef CONFIG_MTD_PARTITIONS
261	parts = init_mtd_partitions(info, &bcmsflash.mtd, bcmsflash.mtd.size);
262	if (parts) {
263		for (i = 0; parts[i].name; i++);
264		ret = add_mtd_partitions(&bcmsflash.mtd, parts, i);
265		if (ret) {
266			printk(KERN_ERR "bcmsflash: add_mtd failed\n");
267			goto fail;
268		}
269	}
270#endif
271
272	return 0;
273
274fail:
275	return ret;
276}
277
278static void __exit
279bcmsflash_mtd_exit(void)
280{
281#ifdef CONFIG_MTD_PARTITIONS
282	del_mtd_partitions(&bcmsflash.mtd);
283#else
284	del_mtd_device(&bcmsflash.mtd);
285#endif
286}
287
288module_init(bcmsflash_mtd_init);
289module_exit(bcmsflash_mtd_exit);
290