On Wed, Dec 11, 2013 at 09:22:11AM +0100, Tomasz Nowicki wrote:
On 10.12.2013 20:30, Al Stone wrote:
On 12/06/2013 05:48 AM, Tomasz Nowicki wrote:
On 04.12.2013 15:31, Tomasz Nowicki wrote:
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 | 437 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 451 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 1359eda..4afc430 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -36,6 +36,10 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { { "LNRO0007" }, /* armv8 pmu */ { "LNRO0009" }, /* vexpress-sysreg */ { "LNRO000A" }, /* uart-pl011 */
- { "LNRO000B" }, /* flash cfi_probe */
- { "LNRO000C" }, /* flash jedec_probe */
- { "LNRO000D" }, /* flash map_ram */
- { "LNRO000E" }, /* flash direct-mapped */
Looking at the registry page on the wiki, these will need to be changed. Please see the page at:
https://wiki.linaro.org/LEG/Engineering/Kernel/ACPI/ACPILinaroRegistry
I have sent it before new AMBA ID came in to acpi_platform_device_ids, but surely now it should be updated as you said. Thanks.
Reserve your IDs first, thats the point of the registry!
G
Also, if you could make sure this page is up-to-date:
https://wiki.linaro.org/LEG/Engineering/Kernel/ACPI/DriverWork
I'd greatly appreciate it.
Done.
{ }
}; 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..0973e00 --- /dev/null +++ b/drivers/mtd/maps/physmap_acpi.c @@ -0,0 +1,437 @@ +/*
- 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
- */
+#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];
+};
+/* UUID: ce511ba0-52bf-11e3-8f96-0800200c9a66 (randomly generated) */ +static const char device_label_dsm_uuid[] = {
- 0xA0, 0x1B, 0x51, 0xCE,
- 0xBF, 0x52,
- 0xE3, 0x11,
- 0x8F, 0x96,
- 0x08, 0x00, 0x20, 0xC, 0x9A, 0x66
+};
+static inline void acpi_flash_assign_params(union acpi_object params[4]) +{
- params[0].type = ACPI_TYPE_BUFFER; /* UUID */
- params[0].buffer.length = sizeof(device_label_dsm_uuid);
- params[0].buffer.pointer = (char *)device_label_dsm_uuid;
- params[1].type = ACPI_TYPE_INTEGER; /* Revision */
- params[1].integer.value = 1;
- params[2].type = ACPI_TYPE_INTEGER; /* Function # */
- params[2].integer.value = 1;
- params[3].type = ACPI_TYPE_PACKAGE; /* Arguments */
- params[3].package.count = 0;
- params[3].package.elements = NULL;
+}
+static int acpi_flash_call_dsm(acpi_handle handler, const char *key,
const void *value,
acpi_object_type return_type)
+{
- union acpi_object *obj, *element;
- struct acpi_object_list input;
- union acpi_object params[4];
- acpi_status status;
- char **char_ptr;
- char *string;
- u64 *int_ptr;
- int len, i;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- input.count = 4;
- acpi_flash_assign_params(params);
- input.pointer = params;
- status = acpi_evaluate_object_typed(handler, "_DSM", &input,
&output,
ACPI_TYPE_PACKAGE);
- if (ACPI_FAILURE(status)) {
pr_err("Failed to get _DSM package for this device\n");
return -ENXIO;
- }
- obj = (union acpi_object *)output.pointer;
- /* Parse 2 objects per entry */
- for (i = 0; i < obj->package.count; i += 2) {
element = &obj->package.elements[i];
/* Key must be a string */
len = element->string.length;
if ((element->type != ACPI_TYPE_STRING) ||
(element->string.length <= 0))
continue;
/* Check to see if this is requested key */
if (strncmp(key, element->string.pointer,
len) != 0)
continue;
/* If value type is correct, this is the one */
element++;
if (element->type != return_type)
continue;
switch (return_type) {
case (ACPI_TYPE_INTEGER):
int_ptr = (u64 *)value;
*int_ptr = element->integer.value;
goto found;
case (ACPI_TYPE_STRING):
len = element->string.length;
if (len <= 0)
continue;
string = kmalloc(len + 1, GFP_KERNEL);
strncpy(string, element->string.pointer, len);
string[len] = '\0';
char_ptr = (char **)value;
*char_ptr = string;
goto found;
}
- }
- status = -ENXIO;
+found:
- kfree(output.pointer);
- return status;
+}
+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)
+{
- acpi_handle handler;
- const char *acpi_probe;
- struct mtd_info *mtd;
- int i, err;
- handler = ACPI_HANDLE(&dev->dev);
- dev_warn(&dev->dev, "ACPI uses obsolete "direct-mapped" flash "
"binding\n");
err = acpi_flash_call_dsm(handler, "probe-type", &acpi_probe,
ACPI_TYPE_STRING);
- if (err || acpi_probe == 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;
- } else 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(acpi_handle handler) +{
- char *cp = NULL;
- unsigned int count = 0, i;
- const char **res;
- int cplen, err;
- /* Get space separated strings */
- err = acpi_flash_call_dsm(handler, "linux,part-probe", &cp,
ACPI_TYPE_STRING);
- if (err || cp == NULL)
return part_probe_types_def;
- 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, i;
- const u64 bank_width = 0, map_indirect = 0;
These should not be constant variables.
- struct mtd_info **mtd_list = NULL;
- const char *mtd_name = NULL;
And this as well.
- int count = 0;
- 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;
- acpi_flash_call_dsm(handler, "linux,mtd-name", &mtd_name,
ACPI_TYPE_STRING);
- acpi_flash_call_dsm(handler, "no-unaligned-direct-access",
&map_indirect, ACPI_TYPE_INTEGER);
- 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_flash_call_dsm(handler, "bank-width", &bank_width,
ACPI_TYPE_INTEGER);
if (err || !bank_width) {
dev_err(&dev->dev, "Can't get bank width from DSDT\n");
goto err_out;
}
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(handler);
- 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[] = {
- { "LNRO000B", (unsigned long)"cfi_probe"},
- { "LNRO000C", (unsigned long)"jedec_probe"},
- { "LNRO000D", (unsigned long)"map_ram"},
- { "LNRO000E", (unsigned long)"direct-mapped"},
- {},
+}; +MODULE_DEVICE_TABLE(acpi, acpi_flash_match);
Please see the note above about "LNRO*" values to be used.
+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");
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi