]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/mtd/maps/imx6x-weimnor.c
60a96da32dcff837d9bf0097e4b8765877b44d06
[karo-tx-linux.git] / drivers / mtd / maps / imx6x-weimnor.c
1 /*
2  * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <linux/module.h>
20 #include <linux/types.h>
21 #include <linux/kernel.h>
22 #include <linux/init.h>
23 #include <linux/slab.h>
24 #include <linux/device.h>
25 #include <linux/platform_device.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/mtd/map.h>
28 #include <linux/mtd/partitions.h>
29 #include <linux/mtd/physmap.h>
30 #include <linux/mtd/concat.h>
31 #include <linux/io.h>
32
33 #include <asm/outercache.h>
34
35 #define MAX_RESOURCES           4
36
37 struct imx6x_weimnor_info {
38         struct mtd_info         *mtd[MAX_RESOURCES];
39         struct mtd_info         *cmtd;
40         struct map_info         map[MAX_RESOURCES];
41         int                     nr_parts;
42         struct mtd_partition    *parts;
43 };
44
45 static int imx6x_weimnor_remove(struct platform_device *dev)
46 {
47         struct imx6x_weimnor_info *info;
48         int i;
49
50         info = platform_get_drvdata(dev);
51
52         if (info == NULL)
53                 return 0;
54
55         platform_set_drvdata(dev, NULL);
56
57         if (info->cmtd) {
58                 mtd_device_unregister(info->cmtd);
59                 if (info->nr_parts)
60                         kfree(info->parts);
61                 if (info->cmtd != info->mtd[0])
62                         mtd_concat_destroy(info->cmtd);
63         }
64
65         for (i = 0; i < MAX_RESOURCES; i++) {
66                 if (info->mtd[i] != NULL)
67                         map_destroy(info->mtd[i]);
68                 if (info->map[i].cached)
69                         iounmap(info->map[i].cached);
70         }
71
72         kfree(info);
73
74         return 0;
75 }
76
77 static void imx6x_set_vpp(struct map_info *map, int state)
78 {
79         struct platform_device *pdev;
80         struct physmap_flash_data *flash;
81
82         pdev = (struct platform_device *)map->map_priv_1;
83         flash = pdev->dev.platform_data;
84
85         if (flash->set_vpp)
86                 flash->set_vpp(pdev, state);
87 }
88
89 #define CACHELINESIZE 32
90 static void imx6x_map_inval_cache(struct map_info *map, unsigned long from,
91 ssize_t len)
92 {
93         unsigned long start;
94         unsigned long end;
95         unsigned long phys_start;
96         unsigned long phys_end;
97
98         if (from > map->size) {
99                 start = (unsigned long)map->cached + map->size;
100                 phys_start = (unsigned long)map->phys +  map->size;
101         } else {
102                 start = (unsigned long)map->cached + from;
103                 phys_start = (unsigned long)map->phys + from;
104         }
105
106         if ((from + len)  > map->size) {
107                 end = start + map->size;
108                 phys_end = phys_start + map->size;
109         } else {
110                 end = start + len;
111                 phys_end = phys_start + len;
112         }
113
114         start &= ~(CACHELINESIZE - 1);
115         while (start < end) {
116                 /* invalidate D cache line */
117                 asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start));
118                 start += CACHELINESIZE;
119         }
120         outer_inv_range(phys_start, phys_end);
121 }
122
123 static const char *rom_probe_types[] = {
124         "cfi_probe",
125         "jedec_probe",
126         "qinfo_probe",
127         "map_rom",
128         NULL};
129
130 static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "afs",
131         NULL};
132
133 static int imx6x_weimnor_probe(struct platform_device *dev)
134 {
135         struct physmap_flash_data *flash;
136         struct imx6x_weimnor_info *info;
137         const char **probe_type;
138         int err = 0;
139         int i;
140         int devices_found = 0;
141
142         flash = dev->dev.platform_data;
143         if (flash == NULL)
144                 return -ENODEV;
145
146         info = devm_kzalloc(&dev->dev, sizeof(struct imx6x_weimnor_info),
147                GFP_KERNEL);
148         if (info == NULL) {
149                 err = -ENOMEM;
150                 goto err_out;
151         }
152
153         if (flash->init) {
154                 err = flash->init(dev);
155                 if (err)
156                         goto err_out;
157         }
158
159         platform_set_drvdata(dev, info);
160
161         for (i = 0; i < dev->num_resources; i++) {
162                 printk(KERN_NOTICE
163                        "imx6x-flash (physmap) platform "
164                        "flash device: %.8llx at %.8llx\n",
165                 (unsigned long long)resource_size(&dev->resource[i]),
166                 (unsigned long long)dev->resource[i].start);
167
168                 if (!devm_request_mem_region(&dev->dev,
169                 dev->resource[i].start,
170                 resource_size(&dev->resource[i]),
171                 dev_name(&dev->dev))) {
172                         dev_err(&dev->dev, "Could not reserve memory region\n");
173                         err = -ENOMEM;
174                         goto err_out;
175                 }
176
177                 info->map[i].name = dev_name(&dev->dev);
178                 info->map[i].phys = dev->resource[i].start;
179                 info->map[i].size = resource_size(&dev->resource[i]);
180                 info->map[i].bankwidth = flash->width;
181                 info->map[i].set_vpp = imx6x_set_vpp;
182                 info->map[i].pfow_base = 0;
183                 info->map[i].map_priv_1 = (unsigned long)dev;
184                 info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
185                                     info->map[i].size);
186                 if (info->map[i].virt == NULL) {
187                         dev_err(&dev->dev, "Failed to ioremap flash region\n");
188                         err = -EIO;
189                         goto err_out;
190                 }
191
192                 info->map[i].cached =
193                 ioremap_cached(info->map[i].phys, info->map[i].size);
194                 if (!info->map[i].cached)
195                         printk(KERN_WARNING "Failed to ioremap cached %s\n",
196                         info->map[i].name);
197
198                 info->map[i].inval_cache = imx6x_map_inval_cache;
199
200                 simple_map_init(&info->map[i]);
201                 probe_type = rom_probe_types;
202
203                 for (; info->mtd[i] == NULL && *probe_type != NULL;
204                       probe_type++)
205                         info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
206
207                 if (info->mtd[i] == NULL) {
208                         dev_err(&dev->dev, "map_probe failed\n");
209                         err = -ENXIO;
210                         goto err_out;
211                 } else {
212                         devices_found++;
213                 }
214                 info->mtd[i]->owner = THIS_MODULE;
215                 info->mtd[i]->dev.parent = &dev->dev;
216         }
217
218         if (devices_found == 1) {
219                 info->cmtd = info->mtd[0];
220         } else if (devices_found > 1) {
221                 /*
222                  * We detected multiple devices. Concatenate them together.
223                  */
224                 info->cmtd = mtd_concat_create(info->mtd, devices_found,
225                                                dev_name(&dev->dev));
226                 if (info->cmtd == NULL)
227                         err = -ENXIO;
228         }
229         if (err)
230                 goto err_out;
231
232         err = parse_mtd_partitions(info->cmtd, part_probe_types,
233               &info->parts, 0);
234         if (err > 0) {
235                 mtd_device_register(info->cmtd, info->parts, err);
236                 info->nr_parts = err;
237                 return 0;
238         }
239
240         if (flash->nr_parts) {
241                 printk(KERN_NOTICE "Using physmap partition information\n");
242                 mtd_device_register(info->cmtd, flash->parts,
243                 flash->nr_parts);
244                 return 0;
245         }
246
247         mtd_device_register(info->cmtd, NULL, 0);
248
249         return 0;
250
251 err_out:
252         imx6x_weimnor_remove(dev);
253         return err;
254 }
255
256 #ifdef CONFIG_PM
257 static void imx6x_weimnor_shutdown(struct platform_device *dev)
258 {
259         struct imx6x_weimnor_info *info = platform_get_drvdata(dev);
260         int i;
261
262         for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
263                 if (info->mtd[i]->suspend && info->mtd[i]->resume)
264                         if (info->mtd[i]->suspend(info->mtd[i]) == 0)
265                                 info->mtd[i]->resume(info->mtd[i]);
266 }
267 #else
268 #define imx6x_weimnor_shutdown NULL
269 #endif
270
271 static struct platform_driver imx6x_weimnor_driver = {
272         .probe          = imx6x_weimnor_probe,
273         .remove         = __devexit_p(imx6x_weimnor_remove),
274                           .shutdown       = imx6x_weimnor_shutdown,
275         .driver         = {
276                 .name   = "imx6x-weimnor",
277                 .owner  = THIS_MODULE,
278         },
279 };
280
281 static int __init imx6x_init(void)
282 {
283         int err;
284         err = platform_driver_register(&imx6x_weimnor_driver);
285         return err;
286 }
287
288 static void __exit imx6x_exit(void)
289 {
290         platform_driver_unregister(&imx6x_weimnor_driver);
291 }
292
293 module_init(imx6x_init);
294 module_exit(imx6x_exit);
295
296 MODULE_LICENSE("GPL");
297 MODULE_AUTHOR("Oliver Brown <oliver.brown@freescale.com>");
298 MODULE_DESCRIPTION("MTD map driver for Freescale iMX");
299