diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2011-11-19 10:02:48 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2012-01-09 13:07:18 -0500 |
commit | ae9d4934b2d76a9fba21f5ad3692378d0e7fc24b (patch) | |
tree | 32dc01f47b5dc4ed0ff8e0d4b05f662e826d2745 /drivers/mtd/devices | |
parent | 32a50b3a457fda9606fd0946eb77ba28a520cd7f (diff) |
mtd: docg3: add multiple floor support
Add support for multiple floors, ie. cascaded docg3
chips. There might be 4 docg3 chips cascaded, sharing the
same address space, and providing up to 4 times the storage
capacity of a unique chip.
Each floor will be seen as an independant mtd device.
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Reviewed-by: Ivan Djelic <ivan.djelic@parrot.com>
Reviewed-by: Mike Dunn <mikedunn@newsguy.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | drivers/mtd/devices/docg3.c | 181 | ||||
-rw-r--r-- | drivers/mtd/devices/docg3.h | 1 |
2 files changed, 126 insertions, 56 deletions
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 1c2f54d23c7e..6eca7f6c072c 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c | |||
@@ -948,7 +948,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) | |||
948 | 948 | ||
949 | switch (chip_id) { | 949 | switch (chip_id) { |
950 | case DOC_CHIPID_G3: | 950 | case DOC_CHIPID_G3: |
951 | mtd->name = "DiskOnChip G3"; | 951 | mtd->name = kasprintf(GFP_KERNEL, "DiskOnChip G3 floor %d", |
952 | docg3->device_id); | ||
952 | docg3->max_block = 2047; | 953 | docg3->max_block = 2047; |
953 | break; | 954 | break; |
954 | } | 955 | } |
@@ -975,22 +976,24 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) | |||
975 | } | 976 | } |
976 | 977 | ||
977 | /** | 978 | /** |
978 | * doc_probe - Probe the IO space for a DiskOnChip G3 chip | 979 | * doc_probe_device - Check if a device is available |
979 | * @pdev: platform device | 980 | * @base: the io space where the device is probed |
981 | * @floor: the floor of the probed device | ||
982 | * @dev: the device | ||
980 | * | 983 | * |
981 | * Probes for a G3 chip at the specified IO space in the platform data | 984 | * Checks whether a device at the specified IO range, and floor is available. |
982 | * ressources. | ||
983 | * | 985 | * |
984 | * Returns 0 on success, -ENOMEM, -ENXIO on error | 986 | * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM |
987 | * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is | ||
988 | * launched. | ||
985 | */ | 989 | */ |
986 | static int __init docg3_probe(struct platform_device *pdev) | 990 | static struct mtd_info *doc_probe_device(void __iomem *base, int floor, |
991 | struct device *dev) | ||
987 | { | 992 | { |
988 | struct device *dev = &pdev->dev; | ||
989 | struct docg3 *docg3; | ||
990 | struct mtd_info *mtd; | ||
991 | struct resource *ress; | ||
992 | int ret, bbt_nbpages; | 993 | int ret, bbt_nbpages; |
993 | u16 chip_id, chip_id_inv; | 994 | u16 chip_id, chip_id_inv; |
995 | struct docg3 *docg3; | ||
996 | struct mtd_info *mtd; | ||
994 | 997 | ||
995 | ret = -ENOMEM; | 998 | ret = -ENOMEM; |
996 | docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); | 999 | docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); |
@@ -1000,69 +1003,132 @@ static int __init docg3_probe(struct platform_device *pdev) | |||
1000 | if (!mtd) | 1003 | if (!mtd) |
1001 | goto nomem2; | 1004 | goto nomem2; |
1002 | mtd->priv = docg3; | 1005 | mtd->priv = docg3; |
1006 | bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, | ||
1007 | 8 * DOC_LAYOUT_PAGE_SIZE); | ||
1008 | docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); | ||
1009 | if (!docg3->bbt) | ||
1010 | goto nomem3; | ||
1003 | 1011 | ||
1004 | ret = -ENXIO; | 1012 | docg3->dev = dev; |
1005 | ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1013 | docg3->device_id = floor; |
1006 | if (!ress) { | 1014 | docg3->base = base; |
1007 | dev_err(dev, "No I/O memory resource defined\n"); | ||
1008 | goto noress; | ||
1009 | } | ||
1010 | docg3->base = ioremap(ress->start, DOC_IOSPACE_SIZE); | ||
1011 | |||
1012 | docg3->dev = &pdev->dev; | ||
1013 | docg3->device_id = 0; | ||
1014 | doc_set_device_id(docg3, docg3->device_id); | 1015 | doc_set_device_id(docg3, docg3->device_id); |
1015 | doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); | 1016 | if (!floor) |
1017 | doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); | ||
1016 | doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); | 1018 | doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); |
1017 | 1019 | ||
1018 | chip_id = doc_register_readw(docg3, DOC_CHIPID); | 1020 | chip_id = doc_register_readw(docg3, DOC_CHIPID); |
1019 | chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); | 1021 | chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); |
1020 | 1022 | ||
1021 | ret = -ENODEV; | 1023 | ret = 0; |
1022 | if (chip_id != (u16)(~chip_id_inv)) { | 1024 | if (chip_id != (u16)(~chip_id_inv)) { |
1023 | doc_info("No device found at IO addr %p\n", | 1025 | goto nomem3; |
1024 | (void *)ress->start); | ||
1025 | goto nochipfound; | ||
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | switch (chip_id) { | 1028 | switch (chip_id) { |
1029 | case DOC_CHIPID_G3: | 1029 | case DOC_CHIPID_G3: |
1030 | doc_info("Found a G3 DiskOnChip at addr %p\n", | 1030 | doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n", |
1031 | (void *)ress->start); | 1031 | base, floor); |
1032 | break; | 1032 | break; |
1033 | default: | 1033 | default: |
1034 | doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); | 1034 | doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); |
1035 | goto nochipfound; | 1035 | goto nomem3; |
1036 | } | 1036 | } |
1037 | 1037 | ||
1038 | doc_set_driver_info(chip_id, mtd); | 1038 | doc_set_driver_info(chip_id, mtd); |
1039 | platform_set_drvdata(pdev, mtd); | ||
1040 | 1039 | ||
1041 | ret = -ENOMEM; | ||
1042 | bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, | ||
1043 | 8 * DOC_LAYOUT_PAGE_SIZE); | ||
1044 | docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); | ||
1045 | if (!docg3->bbt) | ||
1046 | goto nochipfound; | ||
1047 | doc_reload_bbt(docg3); | 1040 | doc_reload_bbt(docg3); |
1041 | return mtd; | ||
1048 | 1042 | ||
1049 | ret = mtd_device_parse_register(mtd, part_probes, | 1043 | nomem3: |
1050 | NULL, NULL, 0); | ||
1051 | if (ret) | ||
1052 | goto register_error; | ||
1053 | |||
1054 | doc_dbg_register(docg3); | ||
1055 | return 0; | ||
1056 | |||
1057 | register_error: | ||
1058 | kfree(docg3->bbt); | ||
1059 | nochipfound: | ||
1060 | iounmap(docg3->base); | ||
1061 | noress: | ||
1062 | kfree(mtd); | 1044 | kfree(mtd); |
1063 | nomem2: | 1045 | nomem2: |
1064 | kfree(docg3); | 1046 | kfree(docg3); |
1065 | nomem1: | 1047 | nomem1: |
1048 | return ERR_PTR(ret); | ||
1049 | } | ||
1050 | |||
1051 | /** | ||
1052 | * doc_release_device - Release a docg3 floor | ||
1053 | * @mtd: the device | ||
1054 | */ | ||
1055 | static void doc_release_device(struct mtd_info *mtd) | ||
1056 | { | ||
1057 | struct docg3 *docg3 = mtd->priv; | ||
1058 | |||
1059 | mtd_device_unregister(mtd); | ||
1060 | kfree(docg3->bbt); | ||
1061 | kfree(docg3); | ||
1062 | kfree(mtd->name); | ||
1063 | kfree(mtd); | ||
1064 | } | ||
1065 | |||
1066 | /** | ||
1067 | * doc_probe - Probe the IO space for a DiskOnChip G3 chip | ||
1068 | * @pdev: platform device | ||
1069 | * | ||
1070 | * Probes for a G3 chip at the specified IO space in the platform data | ||
1071 | * ressources. The floor 0 must be available. | ||
1072 | * | ||
1073 | * Returns 0 on success, -ENOMEM, -ENXIO on error | ||
1074 | */ | ||
1075 | static int __init docg3_probe(struct platform_device *pdev) | ||
1076 | { | ||
1077 | struct device *dev = &pdev->dev; | ||
1078 | struct mtd_info *mtd; | ||
1079 | struct resource *ress; | ||
1080 | void __iomem *base; | ||
1081 | int ret, floor, found = 0; | ||
1082 | struct mtd_info **docg3_floors; | ||
1083 | |||
1084 | ret = -ENXIO; | ||
1085 | ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1086 | if (!ress) { | ||
1087 | dev_err(dev, "No I/O memory resource defined\n"); | ||
1088 | goto noress; | ||
1089 | } | ||
1090 | base = ioremap(ress->start, DOC_IOSPACE_SIZE); | ||
1091 | |||
1092 | ret = -ENOMEM; | ||
1093 | docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS, | ||
1094 | GFP_KERNEL); | ||
1095 | if (!docg3_floors) | ||
1096 | goto nomem; | ||
1097 | |||
1098 | ret = 0; | ||
1099 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { | ||
1100 | mtd = doc_probe_device(base, floor, dev); | ||
1101 | if (floor == 0 && !mtd) | ||
1102 | goto notfound; | ||
1103 | if (!IS_ERR_OR_NULL(mtd)) | ||
1104 | ret = mtd_device_parse_register(mtd, part_probes, | ||
1105 | NULL, NULL, 0); | ||
1106 | else | ||
1107 | ret = PTR_ERR(mtd); | ||
1108 | docg3_floors[floor] = mtd; | ||
1109 | if (ret) | ||
1110 | goto err_probe; | ||
1111 | if (mtd) | ||
1112 | found++; | ||
1113 | } | ||
1114 | |||
1115 | if (!found) | ||
1116 | goto notfound; | ||
1117 | |||
1118 | platform_set_drvdata(pdev, docg3_floors); | ||
1119 | doc_dbg_register(docg3_floors[0]->priv); | ||
1120 | return 0; | ||
1121 | |||
1122 | notfound: | ||
1123 | ret = -ENODEV; | ||
1124 | dev_info(dev, "No supported DiskOnChip found\n"); | ||
1125 | err_probe: | ||
1126 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) | ||
1127 | if (docg3_floors[floor]) | ||
1128 | doc_release_device(docg3_floors[floor]); | ||
1129 | nomem: | ||
1130 | iounmap(base); | ||
1131 | noress: | ||
1066 | return ret; | 1132 | return ret; |
1067 | } | 1133 | } |
1068 | 1134 | ||
@@ -1074,15 +1140,18 @@ nomem1: | |||
1074 | */ | 1140 | */ |
1075 | static int __exit docg3_release(struct platform_device *pdev) | 1141 | static int __exit docg3_release(struct platform_device *pdev) |
1076 | { | 1142 | { |
1077 | struct mtd_info *mtd = platform_get_drvdata(pdev); | 1143 | struct mtd_info **docg3_floors = platform_get_drvdata(pdev); |
1078 | struct docg3 *docg3 = mtd->priv; | 1144 | struct docg3 *docg3 = docg3_floors[0]->priv; |
1145 | void __iomem *base = docg3->base; | ||
1146 | int floor; | ||
1079 | 1147 | ||
1080 | doc_dbg_unregister(docg3); | 1148 | doc_dbg_unregister(docg3); |
1081 | mtd_device_unregister(mtd); | 1149 | for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) |
1082 | iounmap(docg3->base); | 1150 | if (docg3_floors[floor]) |
1083 | kfree(docg3->bbt); | 1151 | doc_release_device(docg3_floors[floor]); |
1084 | kfree(docg3); | 1152 | |
1085 | kfree(mtd); | 1153 | kfree(docg3_floors); |
1154 | iounmap(base); | ||
1086 | return 0; | 1155 | return 0; |
1087 | } | 1156 | } |
1088 | 1157 | ||
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 62af5aaeec43..75df3a1060fb 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h | |||
@@ -80,6 +80,7 @@ | |||
80 | 80 | ||
81 | #define DOC_CHIPID_G3 0x200 | 81 | #define DOC_CHIPID_G3 0x200 |
82 | #define DOC_ERASE_MARK 0xaa | 82 | #define DOC_ERASE_MARK 0xaa |
83 | #define DOC_MAX_NBFLOORS 4 | ||
83 | /* | 84 | /* |
84 | * Flash registers | 85 | * Flash registers |
85 | */ | 86 | */ |