ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / mtd / maps / ich2rom.c
1 /*
2  * ich2rom.c
3  *
4  * Normal mappings of chips in physical memory
5  * $Id: ich2rom.c,v 1.7 2003/05/21 12:45:18 dwmw2 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
19 #define RESERVE_MEM_REGION 0
20
21 #define ICH2_FWH_REGION_START   0xFF000000UL
22 #define ICH2_FWH_REGION_SIZE    0x01000000UL
23 #define BIOS_CNTL       0x4e
24 #define FWH_DEC_EN1     0xE3
25 #define FWH_DEC_EN2     0xF0
26 #define FWH_SEL1        0xE8
27 #define FWH_SEL2        0xEE
28
29 struct ich2rom_map_info {
30         struct map_info map;
31         struct mtd_info *mtd;
32         unsigned long window_addr;
33 };
34
35 static inline unsigned long addr(struct map_info *map, unsigned long ofs)
36 {
37         unsigned long offset;
38         offset = ((8*1024*1024) - map->size) + ofs;
39         if (offset >= (4*1024*1024)) {
40                 offset += 0x400000;
41         }
42         return map->map_priv_1 + 0x400000 + offset;
43 }
44
45 static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
46 {
47         return addr - map->map_priv_1 + ICH2_FWH_REGION_START;
48 }
49         
50 static __u8 ich2rom_read8(struct map_info *map, unsigned long ofs)
51 {
52         return __raw_readb(addr(map, ofs));
53 }
54
55 static __u16 ich2rom_read16(struct map_info *map, unsigned long ofs)
56 {
57         return __raw_readw(addr(map, ofs));
58 }
59
60 static __u32 ich2rom_read32(struct map_info *map, unsigned long ofs)
61 {
62         return __raw_readl(addr(map, ofs));
63 }
64
65 static void ich2rom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
66 {
67         memcpy_fromio(to, addr(map, from), len);
68 }
69
70 static void ich2rom_write8(struct map_info *map, __u8 d, unsigned long ofs)
71 {
72         __raw_writeb(d, addr(map,ofs));
73         mb();
74 }
75
76 static void ich2rom_write16(struct map_info *map, __u16 d, unsigned long ofs)
77 {
78         __raw_writew(d, addr(map, ofs));
79         mb();
80 }
81
82 static void ich2rom_write32(struct map_info *map, __u32 d, unsigned long ofs)
83 {
84         __raw_writel(d, addr(map, ofs));
85         mb();
86 }
87
88 static void ich2rom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
89 {
90         memcpy_toio(addr(map, to), from, len);
91 }
92
93 static struct ich2rom_map_info ich2rom_map = {
94         .map = {
95                 .name = "ICH2 rom",
96                 .phys = NO_XIP,
97                 .size = 0,
98                 .buswidth = 1,
99                 .read8 = ich2rom_read8,
100                 .read16 = ich2rom_read16,
101                 .read32 = ich2rom_read32,
102                 .copy_from = ich2rom_copy_from,
103                 .write8 = ich2rom_write8,
104                 .write16 = ich2rom_write16,
105                 .write32 = ich2rom_write32,
106                 .copy_to = ich2rom_copy_to,
107                 /* Firmware hubs only use vpp when being programmed
108                  * in a factory setting.  So in place programming
109                  * needs to use a different method.
110                  */
111         },
112         .mtd = 0,
113         .window_addr = 0,
114 };
115
116 enum fwh_lock_state {
117         FWH_DENY_WRITE = 1,
118         FWH_IMMUTABLE  = 2,
119         FWH_DENY_READ  = 4,
120 };
121
122 static int ich2rom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
123         enum fwh_lock_state state)
124 {
125         struct map_info *map = mtd->priv;
126         unsigned long start = ofs;
127         unsigned long end = start + len -1;
128
129         /* FIXME do I need to guard against concurrency here? */
130         /* round down to 64K boundaries */
131         start = start & ~0xFFFF;
132         end = end & ~0xFFFF;
133         while (start <= end) {
134                 unsigned long ctrl_addr;
135                 ctrl_addr = addr(map, start) - 0x400000 + 2;
136                 writeb(state, ctrl_addr);
137                 start = start + 0x10000;
138         }
139         return 0;
140 }
141
142 static int ich2rom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
143 {
144         return ich2rom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
145 }
146
147 static int ich2rom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
148 {
149         return ich2rom_set_lock_state(mtd, ofs, len, 0);
150 }
151
152 static int __devinit ich2rom_init_one (struct pci_dev *pdev,
153         const struct pci_device_id *ent)
154 {
155         u16 word;
156         struct ich2rom_map_info *info = &ich2rom_map;
157         unsigned long map_size;
158
159         /* For now I just handle the ich2 and I assume there
160          * are not a lot of resources up at the top of the address
161          * space.  It is possible to handle other devices in the
162          * top 16MB but it is very painful.  Also since
163          * you can only really attach a FWH to an ICH2 there
164          * a number of simplifications you can make.
165          *
166          * Also you can page firmware hubs if an 8MB window isn't enough 
167          * but don't currently handle that case either.
168          */
169
170 #if RESERVE_MEM_REGION
171         /* Some boards have this reserved and I haven't found a good work
172          * around to say I know what I'm doing!
173          */
174         if (!request_mem_region(ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE, "ich2rom")) {
175                 printk(KERN_ERR "ich2rom: cannot reserve rom window\n");
176                 goto err_out_none;
177         }
178 #endif /* RESERVE_MEM_REGION */
179         
180         /* Enable writes through the rom window */
181         pci_read_config_word(pdev, BIOS_CNTL, &word);
182         if (!(word & 1)  && (word & (1<<1))) {
183                 /* The BIOS will generate an error if I enable
184                  * this device, so don't even try.
185                  */
186                 printk(KERN_ERR "ich2rom: firmware access control, I can't enable writes\n");
187                 goto err_out_none;
188         }
189         pci_write_config_word(pdev, BIOS_CNTL, word | 1);
190
191
192         /* Map the firmware hub into my address space. */
193         /* Does this use to much virtual address space? */
194         info->window_addr = (unsigned long)ioremap(
195                 ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE);
196         if (!info->window_addr) {
197                 printk(KERN_ERR "Failed to ioremap\n");
198                 goto err_out_free_mmio_region;
199         }
200
201         /* For now assume the firmware has setup all relevant firmware
202          * windows.  We don't have enough information to handle this case
203          * intelligently.
204          */
205
206         /* FIXME select the firmware hub and enable a window to it. */
207
208         info->mtd = 0;
209         info->map.map_priv_1 =  info->window_addr;
210
211         map_size = ICH2_FWH_REGION_SIZE;
212         while(!info->mtd && (map_size > 0)) {
213                 info->map.size = map_size;
214                 info->mtd = do_map_probe("jedec_probe", &ich2rom_map.map);
215                 map_size -= 512*1024;
216         }
217         if (!info->mtd) {
218                 goto err_out_iounmap;
219         }
220         /* I know I can only be a firmware hub here so put
221          * in the special lock and unlock routines.
222          */
223         info->mtd->lock = ich2rom_lock;
224         info->mtd->unlock = ich2rom_unlock;
225                 
226         info->mtd->owner = THIS_MODULE;
227         add_mtd_device(info->mtd);
228         return 0;
229
230 err_out_iounmap:
231         iounmap((void *)(info->window_addr));
232 err_out_free_mmio_region:
233 #if RESERVE_MEM_REGION
234         release_mem_region(ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE);
235 #endif
236 err_out_none:
237         return -ENODEV;
238 }
239
240
241 static void __devexit ich2rom_remove_one (struct pci_dev *pdev)
242 {
243         struct ich2rom_map_info *info = &ich2rom_map;
244         u16 word;
245
246         del_mtd_device(info->mtd);
247         map_destroy(info->mtd);
248         info->mtd = 0;
249         info->map.map_priv_1 = 0;
250
251         iounmap((void *)(info->window_addr));
252         info->window_addr = 0;
253
254         /* Disable writes through the rom window */
255         pci_read_config_word(pdev, BIOS_CNTL, &word);
256         pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
257
258 #if RESERVE_MEM_REGION  
259         release_mem_region(ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE);
260 #endif
261 }
262
263 static struct pci_device_id ich2rom_pci_tbl[] = {
264         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, 
265           PCI_ANY_ID, PCI_ANY_ID, },
266         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, 
267           PCI_ANY_ID, PCI_ANY_ID, },
268         { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, 
269           PCI_ANY_ID, PCI_ANY_ID, },
270         { 0, },
271 };
272
273 MODULE_DEVICE_TABLE(pci, ich2rom_pci_tbl);
274
275 #if 0
276 static struct pci_driver ich2rom_driver = {
277         .name =         "ich2rom",
278         .id_table =     ich2rom_pci_tbl,
279         .probe =        ich2rom_init_one,
280         .remove =       ich2rom_remove_one,
281 };
282 #endif
283
284 static struct pci_dev *mydev;
285 int __init init_ich2rom(void)
286 {
287         struct pci_dev *pdev;
288         struct pci_device_id *id;
289         pdev = 0;
290         for(id = ich2rom_pci_tbl; id->vendor; id++) {
291                 pdev = pci_find_device(id->vendor, id->device, 0);
292                 if (pdev) {
293                         break;
294                 }
295         }
296         if (pdev) {
297                 mydev = pdev;
298                 return ich2rom_init_one(pdev, &ich2rom_pci_tbl[0]);
299         }
300         return -ENXIO;
301 #if 0
302         return pci_module_init(&ich2rom_driver);
303 #endif
304 }
305
306 static void __exit cleanup_ich2rom(void)
307 {
308         ich2rom_remove_one(mydev);
309 }
310
311 module_init(init_ich2rom);
312 module_exit(cleanup_ich2rom);
313
314 MODULE_LICENSE("GPL");
315 MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
316 MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICH2 southbridge");