1/*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright (C) 2010, 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: sflash.c,v 1.6 2011-02-10 10:55:57 Exp $
19 */
20
21#include <linux/config.h>
22#include <linux/module.h>
23#include <linux/slab.h>
24#include <linux/ioport.h>
25#include <linux/mtd/compatmac.h>
26#include <linux/mtd/mtd.h>
27#include <linux/mtd/partitions.h>
28#include <linux/errno.h>
29#include <linux/pci.h>
30#include <linux/delay.h>
31#include <asm/io.h>
32
33#include <typedefs.h>
34#include <osl.h>
35#include <bcmutils.h>
36#include <bcmdevs.h>
37#include <bcmnvram.h>
38#include <siutils.h>
39#include <hndpci.h>
40#include <pcicfg.h>
41#include <hndsoc.h>
42#include <sbchipc.h>
43#include <sflash.h>
44
45#ifdef CONFIG_MTD_PARTITIONS
46extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
47#endif
48
49
50struct sflash_mtd {
51	si_t *sih;
52	chipcregs_t *cc;
53	struct mtd_info mtd;
54	struct mtd_erase_region_info region;
55};
56
57/* Private global state */
58static struct sflash_mtd sflash;
59
60static int
61sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)
62{
63	int now = jiffies;
64	int ret = 0;
65
66	for (;;) {
67		if (!sflash_poll(sflash->sih, sflash->cc, offset))
68			break;
69
70		if (time_after((unsigned long)jiffies, (unsigned long)now + timeout)) {
71			if (!sflash_poll(sflash->sih, sflash->cc, offset))
72				break;
73
74			printk(KERN_ERR "sflash: timeout\n");
75			ret = -ETIMEDOUT;
76			break;
77		}
78		udelay(1);
79	}
80
81	return ret;
82}
83
84static int
85sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
86{
87	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
88	int bytes, ret = 0;
89
90	/* Check address range */
91	if (!len)
92		return 0;
93	if ((from + len) > mtd->size)
94		return -EINVAL;
95
96	mutex_lock(mtd->mutex);
97
98	*retlen = 0;
99	while (len) {
100		if ((bytes = sflash_read(sflash->sih, sflash->cc, (uint) from, len, buf)) < 0) {
101			ret = bytes;
102			break;
103		}
104		from += (loff_t) bytes;
105		len -= bytes;
106		buf += bytes;
107		*retlen += bytes;
108	}
109
110	mutex_unlock(mtd->mutex);
111	return ret;
112}
113
114static int
115sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
116{
117	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
118	int bytes, ret = 0;
119
120	/* Check address range */
121	if (!len)
122		return 0;
123	if ((to + len) > mtd->size)
124		return -EINVAL;
125
126	mutex_lock(mtd->mutex);
127	*retlen = 0;
128	while (len) {
129		if ((bytes = sflash_write(sflash->sih, sflash->cc, (uint) to, len, buf)) < 0) {
130			ret = bytes;
131			break;
132		}
133		if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ)))
134			break;
135		to += (loff_t) bytes;
136		len -= bytes;
137		buf += bytes;
138		*retlen += bytes;
139	}
140
141	mutex_unlock(mtd->mutex);
142	return ret;
143}
144
145static int
146sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
147{
148	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
149	int i, j, ret = 0;
150	unsigned int addr, len;
151
152	/* Check address range */
153	if (!erase->len)
154		return 0;
155	if ((erase->addr + erase->len) > mtd->size)
156		return -EINVAL;
157
158	mutex_lock(mtd->mutex);
159	addr = erase->addr;
160	len = erase->len;
161
162	/* Ensure that requested region is aligned */
163	for (i = 0; i < mtd->numeraseregions; i++) {
164		for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
165			if (addr == mtd->eraseregions[i].offset +
166			    mtd->eraseregions[i].erasesize * j &&
167			    len >= mtd->eraseregions[i].erasesize) {
168				if ((ret = sflash_erase(sflash->sih, sflash->cc, addr)) < 0)
169					break;
170				if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ)))
171					break;
172				addr += mtd->eraseregions[i].erasesize;
173				len -= mtd->eraseregions[i].erasesize;
174			}
175		}
176		if (ret)
177			break;
178	}
179
180	/* Set erase status */
181	if (ret)
182		erase->state = MTD_ERASE_FAILED;
183	else
184		erase->state = MTD_ERASE_DONE;
185
186	mutex_unlock(mtd->mutex);
187
188	/* Call erase callback */
189	if (erase->callback)
190		erase->callback(erase);
191
192	return ret;
193}
194
195#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
196#define sflash_mtd_init init_module
197#define sflash_mtd_exit cleanup_module
198#endif
199
200static int __init
201sflash_mtd_init(void)
202{
203	int ret = 0;
204	struct sflash *info;
205	struct pci_dev *dev = NULL;
206#ifdef CONFIG_MTD_PARTITIONS
207	struct mtd_partition *parts;
208	int i;
209#endif
210
211	list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) {
212		if ((dev != NULL) && (dev->device == CC_CORE_ID))
213			break;
214	}
215
216	if (dev == NULL) {
217		printk(KERN_ERR "sflash: chipcommon not found\n");
218		return -ENODEV;
219	}
220
221	memset(&sflash, 0, sizeof(struct sflash_mtd));
222
223	/* attach to the backplane */
224	if (!(sflash.sih = si_kattach(SI_OSH))) {
225		printk(KERN_ERR "sflash: error attaching to backplane\n");
226		ret = -EIO;
227		goto fail;
228	}
229
230	/* Map registers and flash base */
231	if (!(sflash.cc = ioremap_nocache(
232		pci_resource_start(dev, 0),
233		pci_resource_len(dev, 0)))) {
234		printk(KERN_ERR "sflash: error mapping registers\n");
235		ret = -EIO;
236		goto fail;
237	}
238
239	/* Initialize serial flash access */
240	if (!(info = sflash_init(sflash.sih, sflash.cc))) {
241		printk(KERN_ERR "sflash: found no supported devices\n");
242		ret = -ENODEV;
243		goto fail;
244	}
245
246	/* Setup region info */
247	sflash.region.offset = 0;
248	sflash.region.erasesize = info->blocksize;
249	sflash.region.numblocks = info->numblocks;
250	if (sflash.region.erasesize > sflash.mtd.erasesize)
251		sflash.mtd.erasesize = sflash.region.erasesize;
252	sflash.mtd.size = info->size;
253	sflash.mtd.numeraseregions = 1;
254
255	/* Register with MTD */
256	sflash.mtd.name = "sflash";
257	sflash.mtd.type = MTD_NORFLASH;
258	sflash.mtd.flags = MTD_CAP_NORFLASH;
259	sflash.mtd.eraseregions = &sflash.region;
260	sflash.mtd.erase = sflash_mtd_erase;
261	sflash.mtd.read = sflash_mtd_read;
262	sflash.mtd.write = sflash_mtd_write;
263	sflash.mtd.writesize = 1;
264	sflash.mtd.priv = &sflash;
265	sflash.mtd.owner = THIS_MODULE;
266	sflash.mtd.mutex = partitions_mutex_init();
267	if (!sflash.mtd.mutex)
268		return -ENOMEM;
269
270#ifdef CONFIG_MTD_PARTITIONS
271	parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size);
272	for (i = 0; parts[i].name; i++);
273	ret = add_mtd_partitions(&sflash.mtd, parts, i);
274	if (ret) {
275		printk(KERN_ERR "sflash: add_mtd failed\n");
276		goto fail;
277	}
278#endif
279
280	return 0;
281
282fail:
283	if (sflash.cc)
284		iounmap((void *) sflash.cc);
285	if (sflash.sih)
286		si_detach(sflash.sih);
287	return ret;
288}
289
290static void __exit
291sflash_mtd_exit(void)
292{
293#ifdef CONFIG_MTD_PARTITIONS
294	del_mtd_partitions(&sflash.mtd);
295#else
296	del_mtd_device(&sflash.mtd);
297#endif
298	iounmap((void *) sflash.cc);
299	si_detach(sflash.sih);
300}
301
302module_init(sflash_mtd_init);
303module_exit(sflash_mtd_exit);
304