1/*
2 * Flash mapping for BCM947XX boards
3 *
4 * Copyright 2006, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 *
12 * $Id: bcm947xx-flash.c,v 1.1.1.1 2008/10/15 03:26:35 james26_jang Exp $
13 */
14
15#include <linux/module.h>
16#include <linux/types.h>
17#include <linux/kernel.h>
18#include <asm/io.h>
19#include <linux/mtd/mtd.h>
20#include <linux/mtd/map.h>
21#include <linux/mtd/partitions.h>
22#include <linux/config.h>
23
24#include <typedefs.h>
25#include <bcmnvram.h>
26#include <bcmutils.h>
27#include <sbconfig.h>
28#include <sbchipc.h>
29#include <sbutils.h>
30#include <trxhdr.h>
31
32/* Global SB handle */
33extern void *bcm947xx_sbh;
34extern spinlock_t bcm947xx_sbh_lock;
35
36/* Convenience */
37#define sbh bcm947xx_sbh
38#define sbh_lock bcm947xx_sbh_lock
39
40#ifdef CONFIG_MTD_PARTITIONS
41extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
42#endif
43
44#define WINDOW_ADDR 0x1fc00000
45#define WINDOW_SIZE 0x400000
46#define BUSWIDTH 2
47
48/* e.g., flash=2M or flash=4M */
49static int flash = 0;
50MODULE_PARM(flash, "i");
51static int __init
52bcm947xx_setup(char *str)
53{
54	flash = memparse(str, &str);
55	return 1;
56}
57__setup("flash=", bcm947xx_setup);
58
59static struct mtd_info *bcm947xx_mtd;
60
61__u8 bcm947xx_map_read8(struct map_info *map, unsigned long ofs)
62{
63	if (map->map_priv_2 == 1)
64		return __raw_readb(map->map_priv_1 + ofs);
65
66	u16 val = __raw_readw(map->map_priv_1 + (ofs & ~1));
67	if (ofs & 1)
68		return ((val >> 8) & 0xff);
69	else
70		return (val & 0xff);
71}
72
73__u16 bcm947xx_map_read16(struct map_info *map, unsigned long ofs)
74{
75	return __raw_readw(map->map_priv_1 + ofs);
76}
77
78__u32 bcm947xx_map_read32(struct map_info *map, unsigned long ofs)
79{
80	return __raw_readl(map->map_priv_1 + ofs);
81}
82
83void bcm947xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
84{
85	memcpy_fromio(to, map->map_priv_1 + from, len);
86}
87
88void bcm947xx_map_write8(struct map_info *map, __u8 d, unsigned long adr)
89{
90	__raw_writeb(d, map->map_priv_1 + adr);
91	mb();
92}
93
94void bcm947xx_map_write16(struct map_info *map, __u16 d, unsigned long adr)
95{
96	__raw_writew(d, map->map_priv_1 + adr);
97	mb();
98}
99
100void bcm947xx_map_write32(struct map_info *map, __u32 d, unsigned long adr)
101{
102	__raw_writel(d, map->map_priv_1 + adr);
103	mb();
104}
105
106void bcm947xx_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
107{
108	memcpy_toio(map->map_priv_1 + to, from, len);
109}
110
111struct map_info bcm947xx_map = {
112	name: "Physically mapped flash",
113	size: WINDOW_SIZE,
114	buswidth: BUSWIDTH,
115	read8: bcm947xx_map_read8,
116	read16: bcm947xx_map_read16,
117	read32: bcm947xx_map_read32,
118	copy_from: bcm947xx_map_copy_from,
119	write8: bcm947xx_map_write8,
120	write16: bcm947xx_map_write16,
121	write32: bcm947xx_map_write32,
122	copy_to: bcm947xx_map_copy_to
123};
124
125#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
126#define init_bcm947xx_map init_module
127#define cleanup_bcm947xx_map cleanup_module
128#endif
129
130mod_init_t init_bcm947xx_map(void)
131{
132	ulong flags;
133 	uint coreidx;
134	chipcregs_t *cc;
135	uint32 fltype;
136	uint window_addr = 0, window_size = 0;
137	size_t size;
138	int ret = 0;
139#ifdef CONFIG_MTD_PARTITIONS
140	struct mtd_partition *parts;
141	int i;
142#endif
143
144	spin_lock_irqsave(&sbh_lock, flags);
145	coreidx = sb_coreidx(sbh);
146
147	/* Check strapping option if chipcommon exists */
148	if ((cc = sb_setcore(sbh, SB_CC, 0))) {
149		fltype = readl(&cc->capabilities) & CC_CAP_FLASH_MASK;
150		if (fltype == PFLASH) {
151			bcm947xx_map.map_priv_2 = 1;
152			window_addr = 0x1c000000;
153			bcm947xx_map.size = window_size = 32 * 1024 * 1024;
154			if ((readl(&cc->flash_config) & CC_CFG_DS) == 0)
155				bcm947xx_map.buswidth = 1;
156		}
157	} else {
158		fltype = PFLASH;
159		bcm947xx_map.map_priv_2 = 0;
160		window_addr = WINDOW_ADDR;
161		window_size = WINDOW_SIZE;
162	}
163
164	sb_setcoreidx(sbh, coreidx);
165	spin_unlock_irqrestore(&sbh_lock, flags);
166
167	if (fltype != PFLASH) {
168		printk(KERN_ERR "pflash: found no supported devices\n");
169		ret = -ENODEV;
170		goto fail;
171	}
172
173	bcm947xx_map.map_priv_1 = (unsigned long) ioremap(window_addr, window_size);
174	if (!bcm947xx_map.map_priv_1) {
175		printk(KERN_ERR "pflash: ioremap failed\n");
176		ret = -EIO;
177		goto fail;
178	}
179
180	if (!(bcm947xx_mtd = do_map_probe("cfi_probe", &bcm947xx_map))) {
181		printk(KERN_ERR "pflash: cfi_probe failed\n");
182		ret = -ENXIO;
183		goto fail;
184	}
185
186	bcm947xx_mtd->module = THIS_MODULE;
187
188	/* Allow size override for testing */
189	size = flash ? : bcm947xx_mtd->size;
190
191	printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, window_addr);
192
193#ifdef CONFIG_MTD_PARTITIONS
194	parts = init_mtd_partitions(bcm947xx_mtd, size);
195	for (i = 0; parts[i].name; i++);
196	ret = add_mtd_partitions(bcm947xx_mtd, parts, i);
197	if (ret) {
198		printk(KERN_ERR "pflash: add_mtd_partitions failed\n");
199		goto fail;
200	}
201#endif
202
203	return 0;
204
205 fail:
206	if (bcm947xx_mtd)
207		map_destroy(bcm947xx_mtd);
208	if (bcm947xx_map.map_priv_1)
209		iounmap((void *) bcm947xx_map.map_priv_1);
210	bcm947xx_map.map_priv_1 = 0;
211	return ret;
212}
213
214mod_exit_t cleanup_bcm947xx_map(void)
215{
216#ifdef CONFIG_MTD_PARTITIONS
217	del_mtd_partitions(bcm947xx_mtd);
218#endif
219	map_destroy(bcm947xx_mtd);
220	iounmap((void *) bcm947xx_map.map_priv_1);
221	bcm947xx_map.map_priv_1 = 0;
222}
223
224module_init(init_bcm947xx_map);
225module_exit(cleanup_bcm947xx_map);
226