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