cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sa1100-flash.c (6645B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Flash memory access on SA11x0 based devices
      4 *
      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 since
    114	 * 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, size, 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	size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
    171
    172	/*
    173	 * Allocate the map_info structs in one go.
    174	 */
    175	info = kzalloc(size, GFP_KERNEL);
    176	if (!info) {
    177		ret = -ENOMEM;
    178		goto out;
    179	}
    180
    181	if (plat->init) {
    182		ret = plat->init();
    183		if (ret)
    184			goto err;
    185	}
    186
    187	/*
    188	 * Claim and then map the memory regions.
    189	 */
    190	for (i = 0; i < nr; i++) {
    191		struct sa_subdev_info *subdev = &info->subdev[i];
    192		struct resource *res;
    193
    194		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
    195		if (!res)
    196			break;
    197
    198		subdev->map.name = subdev->name;
    199		sprintf(subdev->name, "%s-%d", plat->name, i);
    200		subdev->plat = plat;
    201
    202		ret = sa1100_probe_subdev(subdev, res);
    203		if (ret)
    204			break;
    205	}
    206
    207	info->num_subdev = i;
    208
    209	/*
    210	 * ENXIO is special.  It means we didn't find a chip when we probed.
    211	 */
    212	if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
    213		goto err;
    214
    215	/*
    216	 * If we found one device, don't bother with concat support.  If
    217	 * we found multiple devices, use concat if we have it available,
    218	 * otherwise fail.  Either way, it'll be called "sa1100".
    219	 */
    220	if (info->num_subdev == 1) {
    221		strcpy(info->subdev[0].name, plat->name);
    222		info->mtd = info->subdev[0].mtd;
    223		ret = 0;
    224	} else if (info->num_subdev > 1) {
    225		struct mtd_info **cdev;
    226
    227		cdev = kmalloc_array(nr, sizeof(*cdev), GFP_KERNEL);
    228		if (!cdev) {
    229			ret = -ENOMEM;
    230			goto err;
    231		}
    232
    233		/*
    234		 * We detected multiple devices.  Concatenate them together.
    235		 */
    236		for (i = 0; i < info->num_subdev; i++)
    237			cdev[i] = info->subdev[i].mtd;
    238
    239		info->mtd = mtd_concat_create(cdev, info->num_subdev,
    240					      plat->name);
    241		kfree(cdev);
    242		if (info->mtd == NULL) {
    243			ret = -ENXIO;
    244			goto err;
    245		}
    246	}
    247	info->mtd->dev.parent = &pdev->dev;
    248
    249	if (ret == 0)
    250		return info;
    251
    252 err:
    253	sa1100_destroy(info, plat);
    254 out:
    255	return ERR_PTR(ret);
    256}
    257
    258static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
    259
    260static int sa1100_mtd_probe(struct platform_device *pdev)
    261{
    262	struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
    263	struct sa_info *info;
    264	int err;
    265
    266	if (!plat)
    267		return -ENODEV;
    268
    269	info = sa1100_setup_mtd(pdev, plat);
    270	if (IS_ERR(info)) {
    271		err = PTR_ERR(info);
    272		goto out;
    273	}
    274
    275	/*
    276	 * Partition selection stuff.
    277	 */
    278	mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
    279				  plat->nr_parts);
    280
    281	platform_set_drvdata(pdev, info);
    282	err = 0;
    283
    284 out:
    285	return err;
    286}
    287
    288static int sa1100_mtd_remove(struct platform_device *pdev)
    289{
    290	struct sa_info *info = platform_get_drvdata(pdev);
    291	struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
    292
    293	sa1100_destroy(info, plat);
    294
    295	return 0;
    296}
    297
    298static struct platform_driver sa1100_mtd_driver = {
    299	.probe		= sa1100_mtd_probe,
    300	.remove		= sa1100_mtd_remove,
    301	.driver		= {
    302		.name	= "sa1100-mtd",
    303	},
    304};
    305
    306module_platform_driver(sa1100_mtd_driver);
    307
    308MODULE_AUTHOR("Nicolas Pitre");
    309MODULE_DESCRIPTION("SA1100 CFI map driver");
    310MODULE_LICENSE("GPL");
    311MODULE_ALIAS("platform:sa1100-mtd");