brintos

brintos / linux-shallow public Read only

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