Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
latch-addr-flash.c
Go to the documentation of this file.
1 /*
2  * Interface for NOR flash driver whose high address lines are latched
3  *
4  * Copyright © 2000 Nicolas Pitre <[email protected]>
5  * Copyright © 2005-2008 Analog Devices Inc.
6  * Copyright © 2008 MontaVista Software, Inc. <[email protected]>
7  *
8  * This file is licensed under the terms of the GNU General Public License
9  * version 2. This program is licensed "as is" without any warranty of any
10  * kind, whether express or implied.
11  */
12 
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/mtd/mtd.h>
17 #include <linux/mtd/map.h>
18 #include <linux/mtd/partitions.h>
19 #include <linux/platform_device.h>
21 #include <linux/slab.h>
22 
23 #define DRIVER_NAME "latch-addr-flash"
24 
26  struct mtd_info *mtd;
27  struct map_info map;
28  struct resource *res;
29 
30  void (*set_window)(unsigned long offset, void *data);
31  void *data;
32 
33  /* cache; could be found out of res */
34  unsigned long win_mask;
35 
37 };
38 
39 static map_word lf_read(struct map_info *map, unsigned long ofs)
40 {
42  map_word datum;
43 
44  info = (struct latch_addr_flash_info *)map->map_priv_1;
45 
46  spin_lock(&info->lock);
47 
48  info->set_window(ofs, info->data);
49  datum = inline_map_read(map, info->win_mask & ofs);
50 
51  spin_unlock(&info->lock);
52 
53  return datum;
54 }
55 
56 static void lf_write(struct map_info *map, map_word datum, unsigned long ofs)
57 {
59 
60  info = (struct latch_addr_flash_info *)map->map_priv_1;
61 
62  spin_lock(&info->lock);
63 
64  info->set_window(ofs, info->data);
65  inline_map_write(map, datum, info->win_mask & ofs);
66 
67  spin_unlock(&info->lock);
68 }
69 
70 static void lf_copy_from(struct map_info *map, void *to,
71  unsigned long from, ssize_t len)
72 {
73  struct latch_addr_flash_info *info =
74  (struct latch_addr_flash_info *) map->map_priv_1;
75  unsigned n;
76 
77  while (len > 0) {
78  n = info->win_mask + 1 - (from & info->win_mask);
79  if (n > len)
80  n = len;
81 
82  spin_lock(&info->lock);
83 
84  info->set_window(from, info->data);
85  memcpy_fromio(to, map->virt + (from & info->win_mask), n);
86 
87  spin_unlock(&info->lock);
88 
89  to += n;
90  from += n;
91  len -= n;
92  }
93 }
94 
95 static char *rom_probe_types[] = { "cfi_probe", NULL };
96 
97 static int latch_addr_flash_remove(struct platform_device *dev)
98 {
100  struct latch_addr_flash_data *latch_addr_data;
101 
102  info = platform_get_drvdata(dev);
103  if (info == NULL)
104  return 0;
105  platform_set_drvdata(dev, NULL);
106 
107  latch_addr_data = dev->dev.platform_data;
108 
109  if (info->mtd != NULL) {
110  mtd_device_unregister(info->mtd);
111  map_destroy(info->mtd);
112  }
113 
114  if (info->map.virt != NULL)
115  iounmap(info->map.virt);
116 
117  if (info->res != NULL)
118  release_mem_region(info->res->start, resource_size(info->res));
119 
120  kfree(info);
121 
122  if (latch_addr_data->done)
123  latch_addr_data->done(latch_addr_data->data);
124 
125  return 0;
126 }
127 
128 static int __devinit latch_addr_flash_probe(struct platform_device *dev)
129 {
130  struct latch_addr_flash_data *latch_addr_data;
131  struct latch_addr_flash_info *info;
132  resource_size_t win_base = dev->resource->start;
133  resource_size_t win_size = resource_size(dev->resource);
134  char **probe_type;
135  int chipsel;
136  int err;
137 
138  latch_addr_data = dev->dev.platform_data;
139  if (latch_addr_data == NULL)
140  return -ENODEV;
141 
142  pr_notice("latch-addr platform flash device: %#llx byte "
143  "window at %#.8llx\n",
144  (unsigned long long)win_size, (unsigned long long)win_base);
145 
146  chipsel = dev->id;
147 
148  if (latch_addr_data->init) {
149  err = latch_addr_data->init(latch_addr_data->data, chipsel);
150  if (err != 0)
151  return err;
152  }
153 
154  info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL);
155  if (info == NULL) {
156  err = -ENOMEM;
157  goto done;
158  }
159 
160  platform_set_drvdata(dev, info);
161 
162  info->res = request_mem_region(win_base, win_size, DRIVER_NAME);
163  if (info->res == NULL) {
164  dev_err(&dev->dev, "Could not reserve memory region\n");
165  err = -EBUSY;
166  goto free_info;
167  }
168 
169  info->map.name = DRIVER_NAME;
170  info->map.size = latch_addr_data->size;
171  info->map.bankwidth = latch_addr_data->width;
172 
173  info->map.phys = NO_XIP;
174  info->map.virt = ioremap(win_base, win_size);
175  if (!info->map.virt) {
176  err = -ENOMEM;
177  goto free_res;
178  }
179 
180  info->map.map_priv_1 = (unsigned long)info;
181 
182  info->map.read = lf_read;
183  info->map.copy_from = lf_copy_from;
184  info->map.write = lf_write;
185  info->set_window = latch_addr_data->set_window;
186  info->data = latch_addr_data->data;
187  info->win_mask = win_size - 1;
188 
189  spin_lock_init(&info->lock);
190 
191  for (probe_type = rom_probe_types; !info->mtd && *probe_type;
192  probe_type++)
193  info->mtd = do_map_probe(*probe_type, &info->map);
194 
195  if (info->mtd == NULL) {
196  dev_err(&dev->dev, "map_probe failed\n");
197  err = -ENODEV;
198  goto iounmap;
199  }
200  info->mtd->owner = THIS_MODULE;
201 
203  latch_addr_data->parts,
204  latch_addr_data->nr_parts);
205  return 0;
206 
207 iounmap:
208  iounmap(info->map.virt);
209 free_res:
210  release_mem_region(info->res->start, resource_size(info->res));
211 free_info:
212  kfree(info);
213 done:
214  if (latch_addr_data->done)
215  latch_addr_data->done(latch_addr_data->data);
216  return err;
217 }
218 
219 static struct platform_driver latch_addr_flash_driver = {
220  .probe = latch_addr_flash_probe,
221  .remove = __devexit_p(latch_addr_flash_remove),
222  .driver = {
223  .name = DRIVER_NAME,
224  },
225 };
226 
227 module_platform_driver(latch_addr_flash_driver);
228 
229 MODULE_AUTHOR("David Griego <[email protected]>");
230 MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper "
231  "address lines being set board specifically");
232 MODULE_LICENSE("GPL v2");