This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / mtd / maps / ichxrom.c
1 /*
2  * ichxrom.c
3  *
4  * Normal mappings of chips in physical memory
5  * $Id: ichxrom.c,v 1.7 2004/07/14 18:14:09 eric Exp $
6  */
7
8 #include <linux/module.h>
9 #include <linux/types.h>
10 #include <linux/kernel.h>
11 #include <linux/init.h>
12 #include <asm/io.h>
13 #include <linux/mtd/mtd.h>
14 #include <linux/mtd/map.h>
15 #include <linux/config.h>
16 #include <linux/pci.h>
17 #include <linux/pci_ids.h>
18 #include <linux/mtd/cfi.h>
19
20 #define xstr(s) str(s)
21 #define str(s) #s
22 #define MOD_NAME xstr(KBUILD_BASENAME)
23
24 #define MTD_DEV_NAME_LENGTH 16
25
26 #define RESERVE_MEM_REGION 0
27
28
29 #define MANUFACTURER_INTEL      0x0089
30 #define I82802AB        0x00ad
31 #define I82802AC        0x00ac
32
33 #define ICHX_FWH_REGION_START   0xFF000000UL
34 #define ICHX_FWH_REGION_SIZE    0x01000000UL
35 #define BIOS_CNTL       0x4e
36 #define FWH_DEC_EN1     0xE3
37 #define FWH_DEC_EN2     0xF0
38 #define FWH_SEL1        0xE8
39 #define FWH_SEL2        0xEE
40
41 struct ichxrom_map_info {
42         struct map_info map;
43         struct mtd_info *mtd;
44         unsigned long window_addr;
45         struct pci_dev *pdev;
46         struct resource window_rsrc;
47         struct resource rom_rsrc;
48         char mtd_name[MTD_DEV_NAME_LENGTH];
49 };
50
51 static inline unsigned long addr(struct map_info *map, unsigned long ofs)
52 {
53         unsigned long offset;
54         offset = ((8*1024*1024) - map->size) + ofs;
55         if (offset >= (4*1024*1024)) {
56                 offset += 0x400000;
57         }
58         return map->map_priv_1 + 0x400000 + offset;
59 }
60
61 static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
62 {
63         return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
64 }
65         
66 static map_word ichxrom_read(struct map_info *map, unsigned long ofs)
67 {
68         map_word val;
69         int i;
70         switch(map->bankwidth) {
71         case 1:  val.x[0] = __raw_readb(addr(map, ofs)); break;
72         case 2:  val.x[0] = __raw_readw(addr(map, ofs)); break;
73         case 4:  val.x[0] = __raw_readl(addr(map, ofs)); break;
74 #if BITS_PER_LONG >= 64
75         case 8:  val.x[0] = __raw_readq(addr(map, ofs)); break;
76 #endif
77         default: val.x[0] = 0; break;
78         }
79         for(i = 1; i < map_words(map); i++) {
80                 val.x[i] = 0;
81         }
82         return val;
83 }
84
85 static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
86 {
87         memcpy_fromio(to, addr(map, from), len);
88 }
89
90 static void ichxrom_write(struct map_info *map, map_word d, unsigned long ofs)
91 {
92         switch(map->bankwidth) {
93         case 1: __raw_writeb(d.x[0], addr(map,ofs)); break;
94         case 2: __raw_writew(d.x[0], addr(map,ofs)); break;
95         case 4: __raw_writel(d.x[0], addr(map,ofs)); break;
96 #if BITS_PER_LONG >= 64
97         case 8: __raw_writeq(d.x[0], addr(map,ofs)); break;
98 #endif
99         }
100         mb();
101 }
102
103 static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
104 {
105         memcpy_toio(addr(map, to), from, len);
106 }
107
108 static struct ichxrom_map_info ichxrom_map = {
109         .map = {
110                 .name = MOD_NAME,
111                 .phys = NO_XIP,
112                 .size = 0,
113                 .bankwidth = 1,
114                 .read = ichxrom_read,
115                 .copy_from = ichxrom_copy_from,
116                 .write = ichxrom_write,
117                 .copy_to = ichxrom_copy_to,
118                 /* Firmware hubs only use vpp when being programmed
119                  * in a factory setting.  So in-place programming
120                  * needs to use a different method.
121                  */
122         },
123         /* remaining fields of structure are initialized to 0 */
124 };
125
126 enum fwh_lock_state {
127         FWH_DENY_WRITE = 1,
128         FWH_IMMUTABLE  = 2,
129         FWH_DENY_READ  = 4,
130 };
131
132 static void ichxrom_cleanup(struct ichxrom_map_info *info)
133 {
134         u16 word;
135
136         /* Disable writes through the rom window */
137         pci_read_config_word(info->pdev, BIOS_CNTL, &word);
138         pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
139
140         if (info->mtd) {
141                 del_mtd_device(info->mtd);
142                 map_destroy(info->mtd);
143                 info->mtd = NULL;
144                 info->map.virt = 0;
145         }
146         if (info->rom_rsrc.parent)
147                 release_resource(&info->rom_rsrc);
148         if (info->window_rsrc.parent)
149                 release_resource(&info->window_rsrc);
150
151         if (info->window_addr) {
152                 iounmap((void *)(info->window_addr));
153                 info->window_addr = 0;
154         }
155 }
156
157
158 static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
159         enum fwh_lock_state state)
160 {
161         struct map_info *map = mtd->priv;
162         unsigned long start = ofs;
163         unsigned long end = start + len -1;
164
165         /* FIXME do I need to guard against concurrency here? */
166         /* round down to 64K boundaries */
167         start = start & ~0xFFFF;
168         end = end & ~0xFFFF;
169         while (start <= end) {
170                 unsigned long ctrl_addr;
171                 ctrl_addr = addr(map, start) - 0x400000 + 2;
172                 writeb(state, ctrl_addr);
173                 start = start + 0x10000;
174         }
175         return 0;
176 }
177
178 static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
179 {
180         return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
181 }
182
183 static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
184 {
185         return ichxrom_set_lock_state(mtd, ofs, len, 0);
186 }
187
188 static int __devinit ichxrom_init_one (struct pci_dev *pdev,
189         const struct pci_device_id *ent)
190 {
191         u16 word;
192         struct ichxrom_map_info *info = &ichxrom_map;
193         unsigned long map_size;
194         static char *probes[] = { "cfi_probe", "jedec_probe" };
195         struct cfi_private *cfi;
196
197         /* For now I just handle the ichx and I assume there
198          * are not a lot of resources up at the top of the address
199          * space.  It is possible to handle other devices in the
200          * top 16MB but it is very painful.  Also since
201          * you can only really attach a FWH to an ICHX there
202          * a number of simplifications you can make.
203          *
204          * Also you can page firmware hubs if an 8MB window isn't enough 
205          * but don't currently handle that case either.
206          */
207
208         info->pdev = pdev;
209
210         /*
211          * Try to reserve the window mem region.  If this fails then
212          * it is likely due to the window being "reseved" by the BIOS.
213          */
214         info->window_rsrc.name = MOD_NAME;
215         info->window_rsrc.start = ICHX_FWH_REGION_START;
216         info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
217         info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
218         if (request_resource(&iomem_resource, &info->window_rsrc)) {
219                 info->window_rsrc.parent = NULL;
220                 printk(KERN_ERR MOD_NAME
221                        " %s(): Unable to register resource"
222                        " 0x%.08lx-0x%.08lx - kernel bug?\n",
223                        __func__,
224                        info->window_rsrc.start, info->window_rsrc.end);
225         }
226         
227         /* Enable writes through the rom window */
228         pci_read_config_word(pdev, BIOS_CNTL, &word);
229         if (!(word & 1)  && (word & (1<<1))) {
230                 /* The BIOS will generate an error if I enable
231                  * this device, so don't even try.
232                  */
233                 printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
234                 goto failed;
235         }
236         pci_write_config_word(pdev, BIOS_CNTL, word | 1);
237
238
239         /* Map the firmware hub into my address space. */
240         /* Does this use too much virtual address space? */
241         info->window_addr = (unsigned long)ioremap(
242                 ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
243         if (!info->window_addr) {
244                 printk(KERN_ERR "Failed to ioremap\n");
245                 goto failed;
246         }
247
248         /* For now assume the firmware has setup all relevant firmware
249          * windows.  We don't have enough information to handle this case
250          * intelligently.
251          */
252
253         /* FIXME select the firmware hub and enable a window to it. */
254
255         info->mtd = NULL;
256         info->map.map_priv_1 = info->window_addr;
257
258         /* Loop through the possible bankwidths */
259         for(ichxrom_map.map.bankwidth = 4; ichxrom_map.map.bankwidth; ichxrom_map.map.bankwidth >>= 1) {
260                 map_size = ICHX_FWH_REGION_SIZE;
261                 while(!info->mtd && (map_size > 0)) {
262                         int i;
263                         info->map.size = map_size;
264                         for(i = 0; i < sizeof(probes)/sizeof(char *); i++) {
265                                 info->mtd = do_map_probe(probes[i], &ichxrom_map.map);
266                                 if (info->mtd)
267                                         break;
268                         }
269                         map_size -= 512*1024;
270                 }
271                 if (info->mtd)
272                         break;
273         }
274         if (!info->mtd) {
275                 goto failed;
276         }
277         cfi = ichxrom_map.map.fldrv_priv;
278         if ((cfi->mfr == MANUFACTURER_INTEL) && (
279                     (cfi->id == I82802AB) ||
280                     (cfi->id == I82802AC))) 
281         {
282                 /* If it is a firmware hub put in the special lock
283                  * and unlock routines.
284                  */
285                 info->mtd->lock = ichxrom_lock;
286                 info->mtd->unlock = ichxrom_unlock;
287         }
288         if (info->mtd->size > info->map.size) {
289                 printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%u). fixing...\n",
290                        info->mtd->size, info->map.size);
291                 info->mtd->size = info->map.size;
292         }
293                 
294         info->mtd->owner = THIS_MODULE;
295         add_mtd_device(info->mtd);
296
297         if (info->window_rsrc.parent) {
298                 /*
299                  * Registering the MTD device in iomem may not be possible
300                  * if there is a BIOS "reserved" and BUSY range.  If this
301                  * fails then continue anyway.
302                  */
303                 snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
304                          "mtd%d", info->mtd->index);
305
306                 info->rom_rsrc.name = info->mtd_name;
307                 info->rom_rsrc.start = ICHX_FWH_REGION_START
308                         + ICHX_FWH_REGION_SIZE - map_size;
309                 info->rom_rsrc.end = ICHX_FWH_REGION_START
310                         + ICHX_FWH_REGION_SIZE;
311                 info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
312                 if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
313                         printk(KERN_ERR MOD_NAME
314                                ": cannot reserve MTD resource\n");
315                         info->rom_rsrc.parent = NULL;
316                 }
317         }
318
319         return 0;
320
321  failed:
322         ichxrom_cleanup(info);
323         return -ENODEV;
324 }
325
326
327 static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
328 {
329         struct ichxrom_map_info *info = &ichxrom_map;
330         u16 word;
331
332         del_mtd_device(info->mtd);
333         map_destroy(info->mtd);
334         info->mtd = NULL;
335         info->map.map_priv_1 = 0;
336
337         iounmap((void *)(info->window_addr));
338         info->window_addr = 0;
339
340         /* Disable writes through the rom window */
341         pci_read_config_word(pdev, BIOS_CNTL, &word);
342         pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
343
344 #if RESERVE_MEM_REGION  
345         release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
346 #endif
347 }
348
349 static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
350         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, 
351           PCI_ANY_ID, PCI_ANY_ID, },
352         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, 
353           PCI_ANY_ID, PCI_ANY_ID, },
354         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, 
355           PCI_ANY_ID, PCI_ANY_ID, },
356         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
357           PCI_ANY_ID, PCI_ANY_ID, },
358         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
359           PCI_ANY_ID, PCI_ANY_ID, },
360         { 0, },
361 };
362
363 MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
364
365 #if 0
366 static struct pci_driver ichxrom_driver = {
367         .name =         MOD_NAME,
368         .id_table =     ichxrom_pci_tbl,
369         .probe =        ichxrom_init_one,
370         .remove =       ichxrom_remove_one,
371 };
372 #endif
373
374 static struct pci_dev *mydev;
375 int __init init_ichxrom(void)
376 {
377         struct pci_dev *pdev;
378         struct pci_device_id *id;
379
380         pdev = NULL;
381         for (id = ichxrom_pci_tbl; id->vendor; id++) {
382                 pdev = pci_find_device(id->vendor, id->device, NULL);
383                 if (pdev) {
384                         break;
385                 }
386         }
387         if (pdev) {
388                 mydev = pdev;
389                 return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
390         }
391         return -ENXIO;
392 #if 0
393         return pci_module_init(&ichxrom_driver);
394 #endif
395 }
396
397 static void __exit cleanup_ichxrom(void)
398 {
399         ichxrom_remove_one(mydev);
400 }
401
402 module_init(init_ichxrom);
403 module_exit(cleanup_ichxrom);
404
405 MODULE_LICENSE("GPL");
406 MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
407 MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge");