xref: /OK3568_Linux_fs/kernel/drivers/mtd/maps/sa1100-flash.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Flash memory access on SA11x0 based devices
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/types.h>
9*4882a593Smuzhiyun #include <linux/ioport.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/errno.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/err.h>
16*4882a593Smuzhiyun #include <linux/io.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/mtd/mtd.h>
19*4882a593Smuzhiyun #include <linux/mtd/map.h>
20*4882a593Smuzhiyun #include <linux/mtd/partitions.h>
21*4882a593Smuzhiyun #include <linux/mtd/concat.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include <mach/hardware.h>
24*4882a593Smuzhiyun #include <linux/sizes.h>
25*4882a593Smuzhiyun #include <asm/mach/flash.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun struct sa_subdev_info {
28*4882a593Smuzhiyun 	char name[16];
29*4882a593Smuzhiyun 	struct map_info map;
30*4882a593Smuzhiyun 	struct mtd_info *mtd;
31*4882a593Smuzhiyun 	struct flash_platform_data *plat;
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun struct sa_info {
35*4882a593Smuzhiyun 	struct mtd_info		*mtd;
36*4882a593Smuzhiyun 	int			num_subdev;
37*4882a593Smuzhiyun 	struct sa_subdev_info	subdev[];
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun static DEFINE_SPINLOCK(sa1100_vpp_lock);
41*4882a593Smuzhiyun static int sa1100_vpp_refcnt;
sa1100_set_vpp(struct map_info * map,int on)42*4882a593Smuzhiyun static void sa1100_set_vpp(struct map_info *map, int on)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
45*4882a593Smuzhiyun 	unsigned long flags;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	spin_lock_irqsave(&sa1100_vpp_lock, flags);
48*4882a593Smuzhiyun 	if (on) {
49*4882a593Smuzhiyun 		if (++sa1100_vpp_refcnt == 1)   /* first nested 'on' */
50*4882a593Smuzhiyun 			subdev->plat->set_vpp(1);
51*4882a593Smuzhiyun 	} else {
52*4882a593Smuzhiyun 		if (--sa1100_vpp_refcnt == 0)   /* last nested 'off' */
53*4882a593Smuzhiyun 			subdev->plat->set_vpp(0);
54*4882a593Smuzhiyun 	}
55*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sa1100_vpp_lock, flags);
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
sa1100_destroy_subdev(struct sa_subdev_info * subdev)58*4882a593Smuzhiyun static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	if (subdev->mtd)
61*4882a593Smuzhiyun 		map_destroy(subdev->mtd);
62*4882a593Smuzhiyun 	if (subdev->map.virt)
63*4882a593Smuzhiyun 		iounmap(subdev->map.virt);
64*4882a593Smuzhiyun 	release_mem_region(subdev->map.phys, subdev->map.size);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
sa1100_probe_subdev(struct sa_subdev_info * subdev,struct resource * res)67*4882a593Smuzhiyun static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	unsigned long phys;
70*4882a593Smuzhiyun 	unsigned int size;
71*4882a593Smuzhiyun 	int ret;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	phys = res->start;
74*4882a593Smuzhiyun 	size = res->end - phys + 1;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	/*
77*4882a593Smuzhiyun 	 * Retrieve the bankwidth from the MSC registers.
78*4882a593Smuzhiyun 	 * We currently only implement CS0 and CS1 here.
79*4882a593Smuzhiyun 	 */
80*4882a593Smuzhiyun 	switch (phys) {
81*4882a593Smuzhiyun 	default:
82*4882a593Smuzhiyun 		printk(KERN_WARNING "SA1100 flash: unknown base address "
83*4882a593Smuzhiyun 		       "0x%08lx, assuming CS0\n", phys);
84*4882a593Smuzhiyun 		fallthrough;
85*4882a593Smuzhiyun 	case SA1100_CS0_PHYS:
86*4882a593Smuzhiyun 		subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
87*4882a593Smuzhiyun 		break;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	case SA1100_CS1_PHYS:
90*4882a593Smuzhiyun 		subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
91*4882a593Smuzhiyun 		break;
92*4882a593Smuzhiyun 	}
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	if (!request_mem_region(phys, size, subdev->name)) {
95*4882a593Smuzhiyun 		ret = -EBUSY;
96*4882a593Smuzhiyun 		goto out;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (subdev->plat->set_vpp)
100*4882a593Smuzhiyun 		subdev->map.set_vpp = sa1100_set_vpp;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	subdev->map.phys = phys;
103*4882a593Smuzhiyun 	subdev->map.size = size;
104*4882a593Smuzhiyun 	subdev->map.virt = ioremap(phys, size);
105*4882a593Smuzhiyun 	if (!subdev->map.virt) {
106*4882a593Smuzhiyun 		ret = -ENOMEM;
107*4882a593Smuzhiyun 		goto err;
108*4882a593Smuzhiyun 	}
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	simple_map_init(&subdev->map);
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	/*
113*4882a593Smuzhiyun 	 * Now let's probe for the actual flash.  Do it here since
114*4882a593Smuzhiyun 	 * specific machine settings might have been set above.
115*4882a593Smuzhiyun 	 */
116*4882a593Smuzhiyun 	subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
117*4882a593Smuzhiyun 	if (subdev->mtd == NULL) {
118*4882a593Smuzhiyun 		ret = -ENXIO;
119*4882a593Smuzhiyun 		goto err;
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
123*4882a593Smuzhiyun 		phys, (unsigned)(subdev->mtd->size >> 20),
124*4882a593Smuzhiyun 		subdev->map.bankwidth * 8);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	return 0;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun  err:
129*4882a593Smuzhiyun 	sa1100_destroy_subdev(subdev);
130*4882a593Smuzhiyun  out:
131*4882a593Smuzhiyun 	return ret;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
sa1100_destroy(struct sa_info * info,struct flash_platform_data * plat)134*4882a593Smuzhiyun static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	int i;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	if (info->mtd) {
139*4882a593Smuzhiyun 		mtd_device_unregister(info->mtd);
140*4882a593Smuzhiyun 		if (info->mtd != info->subdev[0].mtd)
141*4882a593Smuzhiyun 			mtd_concat_destroy(info->mtd);
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	for (i = info->num_subdev - 1; i >= 0; i--)
145*4882a593Smuzhiyun 		sa1100_destroy_subdev(&info->subdev[i]);
146*4882a593Smuzhiyun 	kfree(info);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	if (plat->exit)
149*4882a593Smuzhiyun 		plat->exit();
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
sa1100_setup_mtd(struct platform_device * pdev,struct flash_platform_data * plat)152*4882a593Smuzhiyun static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
153*4882a593Smuzhiyun 					struct flash_platform_data *plat)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	struct sa_info *info;
156*4882a593Smuzhiyun 	int nr, size, i, ret = 0;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	/*
159*4882a593Smuzhiyun 	 * Count number of devices.
160*4882a593Smuzhiyun 	 */
161*4882a593Smuzhiyun 	for (nr = 0; ; nr++)
162*4882a593Smuzhiyun 		if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
163*4882a593Smuzhiyun 			break;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	if (nr == 0) {
166*4882a593Smuzhiyun 		ret = -ENODEV;
167*4882a593Smuzhiyun 		goto out;
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	/*
173*4882a593Smuzhiyun 	 * Allocate the map_info structs in one go.
174*4882a593Smuzhiyun 	 */
175*4882a593Smuzhiyun 	info = kzalloc(size, GFP_KERNEL);
176*4882a593Smuzhiyun 	if (!info) {
177*4882a593Smuzhiyun 		ret = -ENOMEM;
178*4882a593Smuzhiyun 		goto out;
179*4882a593Smuzhiyun 	}
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (plat->init) {
182*4882a593Smuzhiyun 		ret = plat->init();
183*4882a593Smuzhiyun 		if (ret)
184*4882a593Smuzhiyun 			goto err;
185*4882a593Smuzhiyun 	}
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	/*
188*4882a593Smuzhiyun 	 * Claim and then map the memory regions.
189*4882a593Smuzhiyun 	 */
190*4882a593Smuzhiyun 	for (i = 0; i < nr; i++) {
191*4882a593Smuzhiyun 		struct sa_subdev_info *subdev = &info->subdev[i];
192*4882a593Smuzhiyun 		struct resource *res;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
195*4882a593Smuzhiyun 		if (!res)
196*4882a593Smuzhiyun 			break;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 		subdev->map.name = subdev->name;
199*4882a593Smuzhiyun 		sprintf(subdev->name, "%s-%d", plat->name, i);
200*4882a593Smuzhiyun 		subdev->plat = plat;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 		ret = sa1100_probe_subdev(subdev, res);
203*4882a593Smuzhiyun 		if (ret)
204*4882a593Smuzhiyun 			break;
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	info->num_subdev = i;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/*
210*4882a593Smuzhiyun 	 * ENXIO is special.  It means we didn't find a chip when we probed.
211*4882a593Smuzhiyun 	 */
212*4882a593Smuzhiyun 	if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
213*4882a593Smuzhiyun 		goto err;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	/*
216*4882a593Smuzhiyun 	 * If we found one device, don't bother with concat support.  If
217*4882a593Smuzhiyun 	 * we found multiple devices, use concat if we have it available,
218*4882a593Smuzhiyun 	 * otherwise fail.  Either way, it'll be called "sa1100".
219*4882a593Smuzhiyun 	 */
220*4882a593Smuzhiyun 	if (info->num_subdev == 1) {
221*4882a593Smuzhiyun 		strcpy(info->subdev[0].name, plat->name);
222*4882a593Smuzhiyun 		info->mtd = info->subdev[0].mtd;
223*4882a593Smuzhiyun 		ret = 0;
224*4882a593Smuzhiyun 	} else if (info->num_subdev > 1) {
225*4882a593Smuzhiyun 		struct mtd_info **cdev;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 		cdev = kmalloc_array(nr, sizeof(*cdev), GFP_KERNEL);
228*4882a593Smuzhiyun 		if (!cdev) {
229*4882a593Smuzhiyun 			ret = -ENOMEM;
230*4882a593Smuzhiyun 			goto err;
231*4882a593Smuzhiyun 		}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 		/*
234*4882a593Smuzhiyun 		 * We detected multiple devices.  Concatenate them together.
235*4882a593Smuzhiyun 		 */
236*4882a593Smuzhiyun 		for (i = 0; i < info->num_subdev; i++)
237*4882a593Smuzhiyun 			cdev[i] = info->subdev[i].mtd;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		info->mtd = mtd_concat_create(cdev, info->num_subdev,
240*4882a593Smuzhiyun 					      plat->name);
241*4882a593Smuzhiyun 		kfree(cdev);
242*4882a593Smuzhiyun 		if (info->mtd == NULL) {
243*4882a593Smuzhiyun 			ret = -ENXIO;
244*4882a593Smuzhiyun 			goto err;
245*4882a593Smuzhiyun 		}
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 	info->mtd->dev.parent = &pdev->dev;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	if (ret == 0)
250*4882a593Smuzhiyun 		return info;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun  err:
253*4882a593Smuzhiyun 	sa1100_destroy(info, plat);
254*4882a593Smuzhiyun  out:
255*4882a593Smuzhiyun 	return ERR_PTR(ret);
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
259*4882a593Smuzhiyun 
sa1100_mtd_probe(struct platform_device * pdev)260*4882a593Smuzhiyun static int sa1100_mtd_probe(struct platform_device *pdev)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun 	struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
263*4882a593Smuzhiyun 	struct sa_info *info;
264*4882a593Smuzhiyun 	int err;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (!plat)
267*4882a593Smuzhiyun 		return -ENODEV;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	info = sa1100_setup_mtd(pdev, plat);
270*4882a593Smuzhiyun 	if (IS_ERR(info)) {
271*4882a593Smuzhiyun 		err = PTR_ERR(info);
272*4882a593Smuzhiyun 		goto out;
273*4882a593Smuzhiyun 	}
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	/*
276*4882a593Smuzhiyun 	 * Partition selection stuff.
277*4882a593Smuzhiyun 	 */
278*4882a593Smuzhiyun 	mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
279*4882a593Smuzhiyun 				  plat->nr_parts);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	platform_set_drvdata(pdev, info);
282*4882a593Smuzhiyun 	err = 0;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun  out:
285*4882a593Smuzhiyun 	return err;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
sa1100_mtd_remove(struct platform_device * pdev)288*4882a593Smuzhiyun static int sa1100_mtd_remove(struct platform_device *pdev)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	struct sa_info *info = platform_get_drvdata(pdev);
291*4882a593Smuzhiyun 	struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	sa1100_destroy(info, plat);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	return 0;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun static struct platform_driver sa1100_mtd_driver = {
299*4882a593Smuzhiyun 	.probe		= sa1100_mtd_probe,
300*4882a593Smuzhiyun 	.remove		= sa1100_mtd_remove,
301*4882a593Smuzhiyun 	.driver		= {
302*4882a593Smuzhiyun 		.name	= "sa1100-mtd",
303*4882a593Smuzhiyun 	},
304*4882a593Smuzhiyun };
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun module_platform_driver(sa1100_mtd_driver);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun MODULE_AUTHOR("Nicolas Pitre");
309*4882a593Smuzhiyun MODULE_DESCRIPTION("SA1100 CFI map driver");
310*4882a593Smuzhiyun MODULE_LICENSE("GPL");
311*4882a593Smuzhiyun MODULE_ALIAS("platform:sa1100-mtd");
312