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