brintos

brintos / linux-shallow public Read only

0
0
Text · 16.4 KiB · 96eb2e7 Raw
692 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Normal mappings of chips in physical memory4 *5 * Copyright (C) 2003 MontaVista Software Inc.6 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net7 *8 * 031022 - [jsun] add run-time configure and partition setup9 *10 * Device tree support:11 *    Copyright (C) 2006 MontaVista Software Inc.12 *    Author: Vitaly Wool <vwool@ru.mvista.com>13 *14 *    Revised to handle newer style flash binding by:15 *    Copyright (C) 2007 David Gibson, IBM Corporation.16 *17 * GPIO address extension:18 *    Handle the case where a flash device is mostly addressed using physical19 *    line and supplemented by GPIOs.  This way you can hook up say a 8MiB flash20 *    to a 2MiB memory range and use the GPIOs to select a particular range.21 *22 *    Copyright © 2000 Nicolas Pitre <nico@cam.org>23 *    Copyright © 2005-2009 Analog Devices Inc.24 */25 26#include <linux/module.h>27#include <linux/types.h>28#include <linux/kernel.h>29#include <linux/init.h>30#include <linux/slab.h>31#include <linux/device.h>32#include <linux/platform_device.h>33#include <linux/property.h>34#include <linux/mtd/mtd.h>35#include <linux/mtd/map.h>36#include <linux/mtd/partitions.h>37#include <linux/mtd/physmap.h>38#include <linux/mtd/concat.h>39#include <linux/mtd/cfi_endian.h>40#include <linux/io.h>41#include <linux/of.h>42#include <linux/pm_runtime.h>43#include <linux/gpio/consumer.h>44 45#include "physmap-bt1-rom.h"46#include "physmap-gemini.h"47#include "physmap-ixp4xx.h"48#include "physmap-versatile.h"49 50struct physmap_flash_info {51	unsigned int		nmaps;52	struct mtd_info		**mtds;53	struct mtd_info		*cmtd;54	struct map_info		*maps;55	spinlock_t		vpp_lock;56	int			vpp_refcnt;57	const char		*probe_type;58	const char * const	*part_types;59	unsigned int		nparts;60	const struct mtd_partition *parts;61	struct gpio_descs	*gpios;62	unsigned int		gpio_values;63	unsigned int		win_order;64};65 66static void physmap_flash_remove(struct platform_device *dev)67{68	struct physmap_flash_info *info;69	struct physmap_flash_data *physmap_data;70	int i;71 72	info = platform_get_drvdata(dev);73 74	if (info->cmtd) {75		WARN_ON(mtd_device_unregister(info->cmtd));76 77		if (info->cmtd != info->mtds[0])78			mtd_concat_destroy(info->cmtd);79	}80 81	for (i = 0; i < info->nmaps; i++) {82		if (info->mtds[i])83			map_destroy(info->mtds[i]);84	}85 86	physmap_data = dev_get_platdata(&dev->dev);87	if (physmap_data && physmap_data->exit)88		physmap_data->exit(dev);89 90	pm_runtime_put(&dev->dev);91	pm_runtime_disable(&dev->dev);92}93 94static void physmap_set_vpp(struct map_info *map, int state)95{96	struct platform_device *pdev;97	struct physmap_flash_data *physmap_data;98	struct physmap_flash_info *info;99	unsigned long flags;100 101	pdev = (struct platform_device *)map->map_priv_1;102	physmap_data = dev_get_platdata(&pdev->dev);103 104	if (!physmap_data->set_vpp)105		return;106 107	info = platform_get_drvdata(pdev);108 109	spin_lock_irqsave(&info->vpp_lock, flags);110	if (state) {111		if (++info->vpp_refcnt == 1)    /* first nested 'on' */112			physmap_data->set_vpp(pdev, 1);113	} else {114		if (--info->vpp_refcnt == 0)    /* last nested 'off' */115			physmap_data->set_vpp(pdev, 0);116	}117	spin_unlock_irqrestore(&info->vpp_lock, flags);118}119 120#if IS_ENABLED(CONFIG_MTD_PHYSMAP_GPIO_ADDR)121static void physmap_set_addr_gpios(struct physmap_flash_info *info,122				   unsigned long ofs)123{124	unsigned int i;125 126	ofs >>= info->win_order;127	if (info->gpio_values == ofs)128		return;129 130	for (i = 0; i < info->gpios->ndescs; i++) {131		if ((BIT(i) & ofs) == (BIT(i) & info->gpio_values))132			continue;133 134		gpiod_set_value(info->gpios->desc[i], !!(BIT(i) & ofs));135	}136 137	info->gpio_values = ofs;138}139 140#define win_mask(order)		(BIT(order) - 1)141 142static map_word physmap_addr_gpios_read(struct map_info *map,143					unsigned long ofs)144{145	struct platform_device *pdev;146	struct physmap_flash_info *info;147	map_word mw;148	u16 word;149 150	pdev = (struct platform_device *)map->map_priv_1;151	info = platform_get_drvdata(pdev);152	physmap_set_addr_gpios(info, ofs);153 154	word = readw(map->virt + (ofs & win_mask(info->win_order)));155	mw.x[0] = word;156	return mw;157}158 159static void physmap_addr_gpios_copy_from(struct map_info *map, void *buf,160					 unsigned long ofs, ssize_t len)161{162	struct platform_device *pdev;163	struct physmap_flash_info *info;164 165	pdev = (struct platform_device *)map->map_priv_1;166	info = platform_get_drvdata(pdev);167 168	while (len) {169		unsigned int winofs = ofs & win_mask(info->win_order);170		unsigned int chunklen = min_t(unsigned int, len,171					      BIT(info->win_order) - winofs);172 173		physmap_set_addr_gpios(info, ofs);174		memcpy_fromio(buf, map->virt + winofs, chunklen);175		len -= chunklen;176		buf += chunklen;177		ofs += chunklen;178	}179}180 181static void physmap_addr_gpios_write(struct map_info *map, map_word mw,182				     unsigned long ofs)183{184	struct platform_device *pdev;185	struct physmap_flash_info *info;186	u16 word;187 188	pdev = (struct platform_device *)map->map_priv_1;189	info = platform_get_drvdata(pdev);190	physmap_set_addr_gpios(info, ofs);191 192	word = mw.x[0];193	writew(word, map->virt + (ofs & win_mask(info->win_order)));194}195 196static void physmap_addr_gpios_copy_to(struct map_info *map, unsigned long ofs,197				       const void *buf, ssize_t len)198{199	struct platform_device *pdev;200	struct physmap_flash_info *info;201 202	pdev = (struct platform_device *)map->map_priv_1;203	info = platform_get_drvdata(pdev);204 205	while (len) {206		unsigned int winofs = ofs & win_mask(info->win_order);207		unsigned int chunklen = min_t(unsigned int, len,208					      BIT(info->win_order) - winofs);209 210		physmap_set_addr_gpios(info, ofs);211		memcpy_toio(map->virt + winofs, buf, chunklen);212		len -= chunklen;213		buf += chunklen;214		ofs += chunklen;215	}216}217 218static int physmap_addr_gpios_map_init(struct map_info *map)219{220	map->phys = NO_XIP;221	map->read = physmap_addr_gpios_read;222	map->copy_from = physmap_addr_gpios_copy_from;223	map->write = physmap_addr_gpios_write;224	map->copy_to = physmap_addr_gpios_copy_to;225 226	return 0;227}228#else229static int physmap_addr_gpios_map_init(struct map_info *map)230{231	return -ENOTSUPP;232}233#endif234 235#if IS_ENABLED(CONFIG_MTD_PHYSMAP_OF)236static const struct of_device_id of_flash_match[] = {237	{238		.compatible = "cfi-flash",239		.data = "cfi_probe",240	},241	{242		/*243		 * FIXME: JEDEC chips can't be safely and reliably244		 * probed, although the mtd code gets it right in245		 * practice most of the time.  We should use the246		 * vendor and device ids specified by the binding to247		 * bypass the heuristic probe code, but the mtd layer248		 * provides, at present, no interface for doing so249		 * :(.250		 */251		.compatible = "jedec-flash",252		.data = "jedec_probe",253	},254	{255		.compatible = "mtd-ram",256		.data = "map_ram",257	},258	{259		.compatible = "mtd-rom",260		.data = "map_rom",261	},262	{263		.type = "rom",264		.compatible = "direct-mapped"265	},266	{ /* sentinel */ },267};268MODULE_DEVICE_TABLE(of, of_flash_match);269 270static const char * const of_default_part_probes[] = {271	"cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL272};273 274static const char * const *of_get_part_probes(struct platform_device *dev)275{276	struct device_node *dp = dev->dev.of_node;277	const char **res;278	int count;279 280	count = of_property_count_strings(dp, "linux,part-probe");281	if (count < 0)282		return of_default_part_probes;283 284	res = devm_kcalloc(&dev->dev, count + 1, sizeof(*res), GFP_KERNEL);285	if (!res)286		return NULL;287 288	count = of_property_read_string_array(dp, "linux,part-probe", res,289					      count);290	if (count < 0)291		return NULL;292 293	return res;294}295 296static const char *of_select_probe_type(struct platform_device *dev)297{298	struct device_node *dp = dev->dev.of_node;299	const char *probe_type;300 301	probe_type = device_get_match_data(&dev->dev);302	if (probe_type)303		return probe_type;304 305	dev_warn(&dev->dev,306		 "Device tree uses obsolete \"direct-mapped\" flash binding\n");307 308	of_property_read_string(dp, "probe-type", &probe_type);309	if (!probe_type)310		return NULL;311 312	if (!strcmp(probe_type, "CFI")) {313		probe_type = "cfi_probe";314	} else if (!strcmp(probe_type, "JEDEC")) {315		probe_type = "jedec_probe";316	} else if (!strcmp(probe_type, "ROM")) {317		probe_type = "map_rom";318	} else {319		dev_warn(&dev->dev,320			 "obsolete_probe: don't know probe type '%s', mapping as rom\n",321			 probe_type);322		probe_type = "map_rom";323	}324 325	return probe_type;326}327 328static int physmap_flash_of_init(struct platform_device *dev)329{330	struct physmap_flash_info *info = platform_get_drvdata(dev);331	struct device_node *dp = dev->dev.of_node;332	const char *mtd_name = NULL;333	int err, swap = 0;334	bool map_indirect;335	unsigned int i;336	u32 bankwidth;337 338	if (!dp)339		return -EINVAL;340 341	info->probe_type = of_select_probe_type(dev);342 343	info->part_types = of_get_part_probes(dev);344	if (!info->part_types)345		return -ENOMEM;346 347	of_property_read_string(dp, "linux,mtd-name", &mtd_name);348 349	map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");350 351	err = of_property_read_u32(dp, "bank-width", &bankwidth);352	if (err) {353		dev_err(&dev->dev, "Can't get bank width from device tree\n");354		return err;355	}356 357	if (of_property_read_bool(dp, "big-endian"))358		swap = CFI_BIG_ENDIAN;359	else if (of_property_read_bool(dp, "little-endian"))360		swap = CFI_LITTLE_ENDIAN;361 362	for (i = 0; i < info->nmaps; i++) {363		info->maps[i].name = mtd_name;364		info->maps[i].swap = swap;365		info->maps[i].bankwidth = bankwidth;366		info->maps[i].device_node = dp;367 368		err = of_flash_probe_bt1_rom(dev, dp, &info->maps[i]);369		if (err)370			return err;371 372		err = of_flash_probe_gemini(dev, dp, &info->maps[i]);373		if (err)374			return err;375 376		err = of_flash_probe_ixp4xx(dev, dp, &info->maps[i]);377		if (err)378			return err;379 380		err = of_flash_probe_versatile(dev, dp, &info->maps[i]);381		if (err)382			return err;383 384		/*385		 * On some platforms (e.g. MPC5200) a direct 1:1 mapping386		 * may cause problems with JFFS2 usage, as the local bus (LPB)387		 * doesn't support unaligned accesses as implemented in the388		 * JFFS2 code via memcpy(). By setting NO_XIP, the389		 * flash will not be exposed directly to the MTD users390		 * (e.g. JFFS2) any more.391		 */392		if (map_indirect)393			info->maps[i].phys = NO_XIP;394	}395 396	return 0;397}398#else /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */399#define of_flash_match NULL400 401static int physmap_flash_of_init(struct platform_device *dev)402{403	return -ENOTSUPP;404}405#endif /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */406 407static const char * const rom_probe_types[] = {408	"cfi_probe", "jedec_probe", "qinfo_probe", "map_rom",409};410 411static const char * const part_probe_types[] = {412	"cmdlinepart", "RedBoot", "afs", NULL413};414 415static int physmap_flash_pdata_init(struct platform_device *dev)416{417	struct physmap_flash_info *info = platform_get_drvdata(dev);418	struct physmap_flash_data *physmap_data;419	unsigned int i;420	int err;421 422	physmap_data = dev_get_platdata(&dev->dev);423	if (!physmap_data)424		return -EINVAL;425 426	info->probe_type = physmap_data->probe_type;427	info->part_types = physmap_data->part_probe_types ? : part_probe_types;428	info->parts = physmap_data->parts;429	info->nparts = physmap_data->nr_parts;430 431	if (physmap_data->init) {432		err = physmap_data->init(dev);433		if (err)434			return err;435	}436 437	for (i = 0; i < info->nmaps; i++) {438		info->maps[i].bankwidth = physmap_data->width;439		info->maps[i].pfow_base = physmap_data->pfow_base;440		info->maps[i].set_vpp = physmap_set_vpp;441	}442 443	return 0;444}445 446static int physmap_flash_probe(struct platform_device *dev)447{448	struct physmap_flash_info *info;449	int err = 0;450	int i;451 452	if (!dev->dev.of_node && !dev_get_platdata(&dev->dev))453		return -EINVAL;454 455	info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);456	if (!info)457		return -ENOMEM;458 459	while (platform_get_resource(dev, IORESOURCE_MEM, info->nmaps))460		info->nmaps++;461 462	if (!info->nmaps)463		return -ENODEV;464 465	info->maps = devm_kzalloc(&dev->dev,466				  sizeof(*info->maps) * info->nmaps,467				  GFP_KERNEL);468	if (!info->maps)469		return -ENOMEM;470 471	info->mtds = devm_kzalloc(&dev->dev,472				  sizeof(*info->mtds) * info->nmaps,473				  GFP_KERNEL);474	if (!info->mtds)475		return -ENOMEM;476 477	platform_set_drvdata(dev, info);478 479	info->gpios = devm_gpiod_get_array_optional(&dev->dev, "addr",480						    GPIOD_OUT_LOW);481	if (IS_ERR(info->gpios))482		return PTR_ERR(info->gpios);483 484	if (info->gpios && info->nmaps > 1) {485		dev_err(&dev->dev, "addr-gpios only supported for nmaps == 1\n");486		return -EINVAL;487	}488 489	pm_runtime_enable(&dev->dev);490	pm_runtime_get_sync(&dev->dev);491 492	if (dev->dev.of_node)493		err = physmap_flash_of_init(dev);494	else495		err = physmap_flash_pdata_init(dev);496 497	if (err) {498		pm_runtime_put(&dev->dev);499		pm_runtime_disable(&dev->dev);500		return err;501	}502 503	for (i = 0; i < info->nmaps; i++) {504		struct resource *res;505 506		info->maps[i].virt = devm_platform_get_and_ioremap_resource(dev, i, &res);507		if (IS_ERR(info->maps[i].virt)) {508			err = PTR_ERR(info->maps[i].virt);509			goto err_out;510		}511 512		dev_notice(&dev->dev, "physmap platform flash device: %pR\n",513			   res);514 515		if (!info->maps[i].name)516			info->maps[i].name = dev_name(&dev->dev);517 518		if (!info->maps[i].phys)519			info->maps[i].phys = res->start;520 521		info->win_order = fls64(resource_size(res)) - 1;522		info->maps[i].size = BIT(info->win_order +523					 (info->gpios ?524					  info->gpios->ndescs : 0));525 526		info->maps[i].map_priv_1 = (unsigned long)dev;527 528		if (info->gpios) {529			err = physmap_addr_gpios_map_init(&info->maps[i]);530			if (err)531				goto err_out;532		}533 534#ifdef CONFIG_MTD_COMPLEX_MAPPINGS535		/*536		 * Only use the simple_map implementation if map hooks are not537		 * implemented. Since map->read() is mandatory checking for its538		 * presence is enough.539		 */540		if (!info->maps[i].read)541			simple_map_init(&info->maps[i]);542#else543		simple_map_init(&info->maps[i]);544#endif545 546		if (info->probe_type) {547			info->mtds[i] = do_map_probe(info->probe_type,548						     &info->maps[i]);549 550			/* Fall back to mapping region as ROM */551			if (!info->mtds[i] && IS_ENABLED(CONFIG_MTD_ROM) &&552			    strcmp(info->probe_type, "map_rom")) {553				dev_warn(&dev->dev,554					 "map_probe() failed for type %s\n",555					 info->probe_type);556 557				info->mtds[i] = do_map_probe("map_rom",558							     &info->maps[i]);559			}560		} else {561			int j;562 563			for (j = 0; j < ARRAY_SIZE(rom_probe_types); j++) {564				info->mtds[i] = do_map_probe(rom_probe_types[j],565							     &info->maps[i]);566				if (info->mtds[i])567					break;568			}569		}570 571		if (!info->mtds[i]) {572			dev_err(&dev->dev, "map_probe failed\n");573			err = -ENXIO;574			goto err_out;575		}576		info->mtds[i]->dev.parent = &dev->dev;577	}578 579	if (info->nmaps == 1) {580		info->cmtd = info->mtds[0];581	} else {582		/*583		 * We detected multiple devices. Concatenate them together.584		 */585		info->cmtd = mtd_concat_create(info->mtds, info->nmaps,586					       dev_name(&dev->dev));587		if (!info->cmtd)588			err = -ENXIO;589	}590	if (err)591		goto err_out;592 593	spin_lock_init(&info->vpp_lock);594 595	mtd_set_of_node(info->cmtd, dev->dev.of_node);596	err = mtd_device_parse_register(info->cmtd, info->part_types, NULL,597					info->parts, info->nparts);598	if (err)599		goto err_out;600 601	return 0;602 603err_out:604	physmap_flash_remove(dev);605	return err;606}607 608#ifdef CONFIG_PM609static void physmap_flash_shutdown(struct platform_device *dev)610{611	struct physmap_flash_info *info = platform_get_drvdata(dev);612	int i;613 614	for (i = 0; i < info->nmaps && info->mtds[i]; i++)615		if (mtd_suspend(info->mtds[i]) == 0)616			mtd_resume(info->mtds[i]);617}618#else619#define physmap_flash_shutdown NULL620#endif621 622static struct platform_driver physmap_flash_driver = {623	.probe		= physmap_flash_probe,624	.remove_new	= physmap_flash_remove,625	.shutdown	= physmap_flash_shutdown,626	.driver		= {627		.name	= "physmap-flash",628		.of_match_table = of_flash_match,629	},630};631 632#ifdef CONFIG_MTD_PHYSMAP_COMPAT633static struct physmap_flash_data physmap_flash_data = {634	.width		= CONFIG_MTD_PHYSMAP_BANKWIDTH,635};636 637static struct resource physmap_flash_resource = {638	.start		= CONFIG_MTD_PHYSMAP_START,639	.end		= CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,640	.flags		= IORESOURCE_MEM,641};642 643static struct platform_device physmap_flash = {644	.name		= "physmap-flash",645	.id		= 0,646	.dev		= {647		.platform_data	= &physmap_flash_data,648	},649	.num_resources	= 1,650	.resource	= &physmap_flash_resource,651};652#endif653 654static int __init physmap_init(void)655{656	int err;657 658	err = platform_driver_register(&physmap_flash_driver);659#ifdef CONFIG_MTD_PHYSMAP_COMPAT660	if (err == 0) {661		err = platform_device_register(&physmap_flash);662		if (err)663			platform_driver_unregister(&physmap_flash_driver);664	}665#endif666 667	return err;668}669 670static void __exit physmap_exit(void)671{672#ifdef CONFIG_MTD_PHYSMAP_COMPAT673	platform_device_unregister(&physmap_flash);674#endif675	platform_driver_unregister(&physmap_flash_driver);676}677 678module_init(physmap_init);679module_exit(physmap_exit);680 681MODULE_LICENSE("GPL");682MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");683MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");684MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");685MODULE_DESCRIPTION("Generic configurable MTD map driver");686 687/* legacy platform drivers can't hotplug or coldplg */688#ifndef CONFIG_MTD_PHYSMAP_COMPAT689/* work with hotplug and coldplug */690MODULE_ALIAS("platform:physmap-flash");691#endif692