Changes for v2: - fix ID driver - use generic utility acpi_keyvalue to retrieve properties from _DSM
Tomasz Nowicki (1): acpi, mtd: Add support for flash device in physical memory map based on ACPI description
drivers/acpi/acpi_platform.c | 4 + drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/physmap_acpi.c | 372 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 drivers/mtd/maps/physmap_acpi.c
This provides a 'mapping' driver which allows the NOR Flash and ROM driver code to communicate with chips which are mapped physically into the CPU's memory. The mapping description here is taken from DSDT ACPI table.
This code was "inspired" by physmap_of.c
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- drivers/acpi/acpi_platform.c | 4 + drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/physmap_acpi.c | 372 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 drivers/mtd/maps/physmap_acpi.c
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 36467a6..c8aff25 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -40,6 +40,10 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { { "LNRO000F" }, /* kmi0,kmi1 */ { "LNRO0011" }, /* wdt */ { "LNRO0013" }, /* rtc */ + { "LNRO0016" }, /* flash jedec_probe */ + { "LNRO0015" }, /* flash cfi_probe */ + { "LNRO0017" }, /* flash map_ram */ + { "LNRO0018" }, /* flash direct-mapped */ { "LNRO0019" }, /* fixed regulator */
{ "AMBA0000" }, diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 310dc7c..07d26e0 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -74,6 +74,15 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree.
+config MTD_PHYSMAP_ACPI + tristate "Flash device in physical memory map based on ACPI description" + depends on ACPI && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) + help + This provides a 'mapping' driver which allows the NOR Flash and + ROM driver code to communicate with chips which are mapped + physically into the CPU's memory. The mapping description here is + taken from DSDT ACPI table. + config MTD_PMC_MSP_EVM tristate "CFI Flash device mapped on PMC-Sierra MSP" depends on PMC_MSP && MTD_CFI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 141c91a..379cfff 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o +obj-$(CONFIG_MTD_PHYSMAP_ACPI) += physmap_acpi.o obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o diff --git a/drivers/mtd/maps/physmap_acpi.c b/drivers/mtd/maps/physmap_acpi.c new file mode 100644 index 0000000..f425dfd --- /dev/null +++ b/drivers/mtd/maps/physmap_acpi.c @@ -0,0 +1,372 @@ +/* + * Flash mappings described by the ACPI + * + * Copyright (C) 2006 MontaVista Software Inc. + * Author: Vitaly Wool vwool@ru.mvista.com + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * Revised to handle ACPI style flash binding by: + * Copyright (C) 2013 Tomasz Nowicki tomasz.nowicki@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> +#include <linux/acpi.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +struct acpi_flash_list { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; +}; + +struct acpi_flash { + struct mtd_info *cmtd; + int list_size; /* number of elements in acpi_flash_list */ + struct acpi_flash_list list[0]; +}; + +static const char * const rom_probe_types[] = { + "cfi_probe", "jedec_probe", "map_rom" }; + +/* Helper function to handle probing of the obsolete "direct-mapped" + * compatible binding, which has an extra "probe-type" property + * describing the type of flash probe necessary. */ +static struct mtd_info *obsolete_probe(struct platform_device *dev, + struct map_info *map) +{ + struct acpi_dsm_entry entry; + struct mtd_info *mtd; + acpi_handle handler; + char *acpi_probe; + int i, err, len; + + handler = ACPI_HANDLE(&dev->dev); + dev_warn(&dev->dev, "ACPI uses obsolete "direct-mapped" flash " + "binding\n"); + + err = acpi_dsm_lookup_value(handler, "probe-type", 0, &entry); + if (err || entry.value == NULL) { + for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) { + mtd = do_map_probe(rom_probe_types[i], map); + if (mtd) + return mtd; + } + return NULL; + } + + len = strlen(entry.value) + 1; + acpi_probe = devm_kzalloc(&dev->dev, len, GFP_KERNEL); + strncpy(acpi_probe, entry.value, len); + kfree(entry.key); + kfree(entry.value); + + if (strcmp(acpi_probe, "CFI") == 0) { + return do_map_probe("cfi_probe", map); + } else if (strcmp(acpi_probe, "JEDEC") == 0) { + return do_map_probe("jedec_probe", map); + } else { + if (strcmp(acpi_probe, "ROM") != 0) + dev_warn(&dev->dev, "obsolete_probe: don't know probe " + "type '%s', mapping as rom\n", acpi_probe); + return do_map_probe("mtd_rom", map); + } +} + +/* When partitions are set we look for a linux,part-probe property which + specifies the list of partition probers to use. If none is given then the + default is use. These take precedence over other device tree + information. */ +static const char * const part_probe_types_def[] = { + "cmdlinepart", "RedBoot", NULL }; + +static const char * const *acpi_get_part_probes(struct device *dev) +{ + struct acpi_dsm_entry entry; + const char **res; + int cplen, err, len; + unsigned int count = 0, i; + char *cp = NULL; + + acpi_handle handler = ACPI_HANDLE(dev); + + /* Get space separated strings */ + err = acpi_dsm_lookup_value(handler, "linux,part-probe", 0, &entry); + if (err || entry.value == NULL) + return part_probe_types_def; + + len = strlen(entry.value) + 1; + cp = devm_kzalloc(dev, len, GFP_KERNEL); + strncpy(cp, entry.value, len); + kfree(entry.key); + kfree(entry.value); + + cplen = strlen(cp); + for (i = 0; i != cplen; i++) + if (cp[i] == ' ') + count++; + + /* Create the table with references to strings */ + res = kzalloc((count + 1) * sizeof(char *), GFP_KERNEL); + for (i = 0; i < count + 1; i++) { + res[i] = cp; + cp = strnchr(cp, cplen, ' '); + if (cp == NULL) + break; + + *cp++ = '\0'; + cplen = strlen(cp); + } + return res; +} + +static void acpi_free_probes(const char * const *probes) +{ + if (probes != part_probe_types_def) + kfree(probes); +} + +static const struct acpi_device_id acpi_flash_match[]; +static int acpi_flash_remove(struct platform_device *dev); + +static int acpi_flash_probe(struct platform_device *dev) +{ + const char * const *part_probe_types; + const struct acpi_device_id *id; + resource_size_t res_size; + struct acpi_flash *info; + const char *probe_type; + struct resource *res; + acpi_handle handler; + int err = 0, i; + int bank_width = 0, map_indirect = 0; + struct mtd_info **mtd_list = NULL; + char *mtd_name = NULL; + int len, count = 0; + struct acpi_dsm_entry entry; + + handler = ACPI_HANDLE(&dev->dev); + + id = acpi_match_device(acpi_flash_match, &dev->dev); + if (!id) + return -ENODEV; + + probe_type = (const char *)id->driver_data; + + err = acpi_dsm_lookup_value(handler, "linux,mtd-name", 0, &entry); + if (err == 0 && entry.value) { + len = strlen(entry.value) + 1; + mtd_name = devm_kzalloc(&dev->dev, len, GFP_KERNEL); + strncpy(mtd_name, entry.value, len); + kfree(entry.key); + kfree(entry.value); + } + + err = acpi_dsm_lookup_value(handler, "no-unaligned-direct-access", + 0, &entry); + if (err == 0 && kstrtoint(entry.value, 0, &map_indirect) == 0) { + kfree(entry.key); + kfree(entry.value); + } + + while (platform_get_resource(dev, IORESOURCE_MEM, count)) + count++; + + if (!count) { + dev_err(&dev->dev, "No resources found for %s device\n", + dev_name(&dev->dev)); + err = -ENXIO; + goto err_flash_remove; + } + + info = devm_kzalloc(&dev->dev, + sizeof(struct acpi_flash) + + sizeof(struct acpi_flash_list) * count, GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto err_flash_remove; + } + + dev_set_drvdata(&dev->dev, info); + + mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL); + if (!mtd_list) + goto err_flash_remove; + + for (i = 0; i < count; i++) { + res = platform_get_resource(dev, IORESOURCE_MEM, i); + dev_dbg(&dev->dev, "resource[%d]: address 0x%lx size 0x%lx\n", + i, (long)res->start, (long)resource_size(res)); + + res_size = resource_size(res); + info->list[i].res = request_mem_region(res->start, res_size, + dev_name(&dev->dev)); + if (!info->list[i].res) { + err = -EBUSY; + goto err_out; + } + + /* Mandatory property */ + err = acpi_dsm_lookup_value(handler, "bank-width", 0, &entry); + if (err || kstrtoint(entry.value, 0, &bank_width) != 0) { + dev_err(&dev->dev, + "Can't get bank width from DSDT\n"); + goto err_out; + } + kfree(entry.key); + kfree(entry.value); + + info->list[i].map.name = mtd_name ?: dev_name(&dev->dev); + info->list[i].map.phys = res->start; + info->list[i].map.size = res_size; + info->list[i].map.bankwidth = bank_width; + info->list[i].map.virt = ioremap(info->list[i].map.phys, + info->list[i].map.size); + if (!info->list[i].map.virt) { + dev_err(&dev->dev, "Failed to ioremap() flash region\n"); + err = -ENOMEM; + goto err_out; + } + + simple_map_init(&info->list[i].map); + + /* + * On some platforms (e.g. MPC5200) a direct 1:1 mapping + * may cause problems with JFFS2 usage, as the local bus (LPB) + * doesn't support unaligned accesses as implemented in the + * JFFS2 code via memcpy(). By setting NO_XIP, the + * flash will not be exposed directly to the MTD users + * (e.g. JFFS2) any more. + */ + if (map_indirect) + info->list[i].map.phys = NO_XIP; + + if (probe_type) { + info->list[i].mtd = do_map_probe(probe_type, + &info->list[i].map); + } else { + info->list[i].mtd = obsolete_probe(dev, + &info->list[i].map); + } + + if (!info->list[i].mtd) { + dev_err(&dev->dev, "do_map_probe() failed\n"); + err = -ENXIO; + goto err_out; + } else + info->list_size++; + + info->list[i].mtd->owner = THIS_MODULE; + info->list[i].mtd->dev.parent = &dev->dev; + mtd_list[i] = info->list[i].mtd; + } + + info->cmtd = NULL; + if (info->list_size == 1) { + info->cmtd = info->list[0].mtd; + } else if (info->list_size > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ + info->cmtd = mtd_concat_create(mtd_list, info->list_size, + dev_name(&dev->dev)); + } + if (info->cmtd == NULL) { + err = -ENXIO; + goto err_out; + } + + part_probe_types = acpi_get_part_probes(&dev->dev); + mtd_device_parse_register(info->cmtd, part_probe_types, NULL, + NULL, 0); + acpi_free_probes(part_probe_types); + + kfree(mtd_list); + + return 0; + +err_out: + kfree(mtd_list); +err_flash_remove: + acpi_flash_remove(dev); + + return err; +} + +static int acpi_flash_remove(struct platform_device *dev) +{ + struct acpi_flash *info; + int i; + + info = dev_get_drvdata(&dev->dev); + if (!info) + return 0; + dev_set_drvdata(&dev->dev, NULL); + + if (info->cmtd != info->list[0].mtd) { + mtd_device_unregister(info->cmtd); + mtd_concat_destroy(info->cmtd); + } + + if (info->cmtd) + mtd_device_unregister(info->cmtd); + + for (i = 0; i < info->list_size; i++) { + if (info->list[i].mtd) + map_destroy(info->list[i].mtd); + + if (info->list[i].map.virt) + iounmap(info->list[i].map.virt); + + if (info->list[i].res) { + release_resource(info->list[i].res); + kfree(info->list[i].res); + } + } + + return 0; +} + +static const struct acpi_device_id acpi_flash_match[] = { + { "LNRO0015", (unsigned long)"cfi_probe"}, + { "LNRO0016", (unsigned long)"jedec_probe"}, + { "LNRO0017", (unsigned long)"map_ram"}, + { "LNRO0018", (unsigned long)"direct-mapped"}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_flash_match); + +static struct platform_driver acpi_flash_driver = { + .driver = { + .name = "acpi-flash", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(acpi_flash_match), + }, + .probe = acpi_flash_probe, + .remove = acpi_flash_remove, +}; + +module_platform_driver(acpi_flash_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Nowicki tomasz.nowicki@linaro.org"); +MODULE_DESCRIPTION("ACPI based MTD map driver");
Acked and pushed!
G
On Fri, Jan 10, 2014 at 01:24:19PM +0100, Tomasz Nowicki wrote:
Changes for v2: - fix ID driver - use generic utility acpi_keyvalue to retrieve properties from _DSM
Tomasz Nowicki (1): acpi, mtd: Add support for flash device in physical memory map based on ACPI description
drivers/acpi/acpi_platform.c | 4 + drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/physmap_acpi.c | 372 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 drivers/mtd/maps/physmap_acpi.c
-- 1.7.9.5
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi