diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 1f0ec79aabf1..01ce462e265d 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -25,6 +25,10 @@ | |||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/of.h> | ||
29 | #include <linux/of_mtd.h> | ||
30 | #include <linux/of_device.h> | ||
31 | #include <linux/mtd/nand.h> | ||
28 | 32 | ||
29 | #include <linux/platform_data/mtd-nand-omap2.h> | 33 | #include <linux/platform_data/mtd-nand-omap2.h> |
30 | 34 | ||
@@ -34,6 +38,7 @@ | |||
34 | #include "common.h" | 38 | #include "common.h" |
35 | #include "omap_device.h" | 39 | #include "omap_device.h" |
36 | #include "gpmc.h" | 40 | #include "gpmc.h" |
41 | #include "gpmc-nand.h" | ||
37 | 42 | ||
38 | #define DEVICE_NAME "omap-gpmc" | 43 | #define DEVICE_NAME "omap-gpmc" |
39 | 44 | ||
@@ -1121,6 +1126,165 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t, | |||
1121 | return 0; | 1126 | return 0; |
1122 | } | 1127 | } |
1123 | 1128 | ||
1129 | #ifdef CONFIG_OF | ||
1130 | static struct of_device_id gpmc_dt_ids[] = { | ||
1131 | { .compatible = "ti,omap2420-gpmc" }, | ||
1132 | { .compatible = "ti,omap2430-gpmc" }, | ||
1133 | { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ | ||
1134 | { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ | ||
1135 | { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ | ||
1136 | { } | ||
1137 | }; | ||
1138 | MODULE_DEVICE_TABLE(of, gpmc_dt_ids); | ||
1139 | |||
1140 | static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, | ||
1141 | struct gpmc_timings *gpmc_t) | ||
1142 | { | ||
1143 | u32 val; | ||
1144 | |||
1145 | memset(gpmc_t, 0, sizeof(*gpmc_t)); | ||
1146 | |||
1147 | /* minimum clock period for syncronous mode */ | ||
1148 | if (!of_property_read_u32(np, "gpmc,sync-clk", &val)) | ||
1149 | gpmc_t->sync_clk = val; | ||
1150 | |||
1151 | /* chip select timtings */ | ||
1152 | if (!of_property_read_u32(np, "gpmc,cs-on", &val)) | ||
1153 | gpmc_t->cs_on = val; | ||
1154 | |||
1155 | if (!of_property_read_u32(np, "gpmc,cs-rd-off", &val)) | ||
1156 | gpmc_t->cs_rd_off = val; | ||
1157 | |||
1158 | if (!of_property_read_u32(np, "gpmc,cs-wr-off", &val)) | ||
1159 | gpmc_t->cs_wr_off = val; | ||
1160 | |||
1161 | /* ADV signal timings */ | ||
1162 | if (!of_property_read_u32(np, "gpmc,adv-on", &val)) | ||
1163 | gpmc_t->adv_on = val; | ||
1164 | |||
1165 | if (!of_property_read_u32(np, "gpmc,adv-rd-off", &val)) | ||
1166 | gpmc_t->adv_rd_off = val; | ||
1167 | |||
1168 | if (!of_property_read_u32(np, "gpmc,adv-wr-off", &val)) | ||
1169 | gpmc_t->adv_wr_off = val; | ||
1170 | |||
1171 | /* WE signal timings */ | ||
1172 | if (!of_property_read_u32(np, "gpmc,we-on", &val)) | ||
1173 | gpmc_t->we_on = val; | ||
1174 | |||
1175 | if (!of_property_read_u32(np, "gpmc,we-off", &val)) | ||
1176 | gpmc_t->we_off = val; | ||
1177 | |||
1178 | /* OE signal timings */ | ||
1179 | if (!of_property_read_u32(np, "gpmc,oe-on", &val)) | ||
1180 | gpmc_t->oe_on = val; | ||
1181 | |||
1182 | if (!of_property_read_u32(np, "gpmc,oe-off", &val)) | ||
1183 | gpmc_t->oe_off = val; | ||
1184 | |||
1185 | /* access and cycle timings */ | ||
1186 | if (!of_property_read_u32(np, "gpmc,page-burst-access", &val)) | ||
1187 | gpmc_t->page_burst_access = val; | ||
1188 | |||
1189 | if (!of_property_read_u32(np, "gpmc,access", &val)) | ||
1190 | gpmc_t->access = val; | ||
1191 | |||
1192 | if (!of_property_read_u32(np, "gpmc,rd-cycle", &val)) | ||
1193 | gpmc_t->rd_cycle = val; | ||
1194 | |||
1195 | if (!of_property_read_u32(np, "gpmc,wr-cycle", &val)) | ||
1196 | gpmc_t->wr_cycle = val; | ||
1197 | |||
1198 | /* only for OMAP3430 */ | ||
1199 | if (!of_property_read_u32(np, "gpmc,wr-access", &val)) | ||
1200 | gpmc_t->wr_access = val; | ||
1201 | |||
1202 | if (!of_property_read_u32(np, "gpmc,wr-data-mux-bus", &val)) | ||
1203 | gpmc_t->wr_data_mux_bus = val; | ||
1204 | } | ||
1205 | |||
1206 | #ifdef CONFIG_MTD_NAND | ||
1207 | |||
1208 | static const char * const nand_ecc_opts[] = { | ||
1209 | [OMAP_ECC_HAMMING_CODE_DEFAULT] = "sw", | ||
1210 | [OMAP_ECC_HAMMING_CODE_HW] = "hw", | ||
1211 | [OMAP_ECC_HAMMING_CODE_HW_ROMCODE] = "hw-romcode", | ||
1212 | [OMAP_ECC_BCH4_CODE_HW] = "bch4", | ||
1213 | [OMAP_ECC_BCH8_CODE_HW] = "bch8", | ||
1214 | }; | ||
1215 | |||
1216 | static int gpmc_probe_nand_child(struct platform_device *pdev, | ||
1217 | struct device_node *child) | ||
1218 | { | ||
1219 | u32 val; | ||
1220 | const char *s; | ||
1221 | struct gpmc_timings gpmc_t; | ||
1222 | struct omap_nand_platform_data *gpmc_nand_data; | ||
1223 | |||
1224 | if (of_property_read_u32(child, "reg", &val) < 0) { | ||
1225 | dev_err(&pdev->dev, "%s has no 'reg' property\n", | ||
1226 | child->full_name); | ||
1227 | return -ENODEV; | ||
1228 | } | ||
1229 | |||
1230 | gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data), | ||
1231 | GFP_KERNEL); | ||
1232 | if (!gpmc_nand_data) | ||
1233 | return -ENOMEM; | ||
1234 | |||
1235 | gpmc_nand_data->cs = val; | ||
1236 | gpmc_nand_data->of_node = child; | ||
1237 | |||
1238 | if (!of_property_read_string(child, "ti,nand-ecc-opt", &s)) | ||
1239 | for (val = 0; val < ARRAY_SIZE(nand_ecc_opts); val++) | ||
1240 | if (!strcasecmp(s, nand_ecc_opts[val])) { | ||
1241 | gpmc_nand_data->ecc_opt = val; | ||
1242 | break; | ||
1243 | } | ||
1244 | |||
1245 | val = of_get_nand_bus_width(child); | ||
1246 | if (val == 16) | ||
1247 | gpmc_nand_data->devsize = NAND_BUSWIDTH_16; | ||
1248 | |||
1249 | gpmc_read_timings_dt(child, &gpmc_t); | ||
1250 | gpmc_nand_init(gpmc_nand_data, &gpmc_t); | ||
1251 | |||
1252 | return 0; | ||
1253 | } | ||
1254 | #else | ||
1255 | static int gpmc_probe_nand_child(struct platform_device *pdev, | ||
1256 | struct device_node *child) | ||
1257 | { | ||
1258 | return 0; | ||
1259 | } | ||
1260 | #endif | ||
1261 | |||
1262 | static int gpmc_probe_dt(struct platform_device *pdev) | ||
1263 | { | ||
1264 | int ret; | ||
1265 | struct device_node *child; | ||
1266 | const struct of_device_id *of_id = | ||
1267 | of_match_device(gpmc_dt_ids, &pdev->dev); | ||
1268 | |||
1269 | if (!of_id) | ||
1270 | return 0; | ||
1271 | |||
1272 | for_each_node_by_name(child, "nand") { | ||
1273 | ret = gpmc_probe_nand_child(pdev, child); | ||
1274 | of_node_put(child); | ||
1275 | if (ret < 0) | ||
1276 | return ret; | ||
1277 | } | ||
1278 | |||
1279 | return 0; | ||
1280 | } | ||
1281 | #else | ||
1282 | static int gpmc_probe_dt(struct platform_device *pdev) | ||
1283 | { | ||
1284 | return 0; | ||
1285 | } | ||
1286 | #endif | ||
1287 | |||
1124 | static int gpmc_probe(struct platform_device *pdev) | 1288 | static int gpmc_probe(struct platform_device *pdev) |
1125 | { | 1289 | { |
1126 | int rc; | 1290 | int rc; |
@@ -1174,6 +1338,14 @@ static int gpmc_probe(struct platform_device *pdev) | |||
1174 | if (IS_ERR_VALUE(gpmc_setup_irq())) | 1338 | if (IS_ERR_VALUE(gpmc_setup_irq())) |
1175 | dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); | 1339 | dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); |
1176 | 1340 | ||
1341 | rc = gpmc_probe_dt(pdev); | ||
1342 | if (rc < 0) { | ||
1343 | clk_disable_unprepare(gpmc_l3_clk); | ||
1344 | clk_put(gpmc_l3_clk); | ||
1345 | dev_err(gpmc_dev, "failed to probe DT parameters\n"); | ||
1346 | return rc; | ||
1347 | } | ||
1348 | |||
1177 | return 0; | 1349 | return 0; |
1178 | } | 1350 | } |
1179 | 1351 | ||
@@ -1191,6 +1363,7 @@ static struct platform_driver gpmc_driver = { | |||
1191 | .driver = { | 1363 | .driver = { |
1192 | .name = DEVICE_NAME, | 1364 | .name = DEVICE_NAME, |
1193 | .owner = THIS_MODULE, | 1365 | .owner = THIS_MODULE, |
1366 | .of_match_table = of_match_ptr(gpmc_dt_ids), | ||
1194 | }, | 1367 | }, |
1195 | }; | 1368 | }; |
1196 | 1369 | ||