1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* linux/drivers/mtd/maps/scx200_docflash.c |
3 | |
4 | Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> |
5 | |
6 | National Semiconductor SCx200 flash mapped with DOCCS |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/types.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> |
13 | #include <asm/io.h> |
14 | #include <linux/mtd/mtd.h> |
15 | #include <linux/mtd/map.h> |
16 | #include <linux/mtd/partitions.h> |
17 | |
18 | #include <linux/pci.h> |
19 | #include <linux/scx200.h> |
20 | |
21 | #define NAME "scx200_docflash" |
22 | |
23 | MODULE_AUTHOR("Christer Weinigel <wingel@hack.org>" ); |
24 | MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver" ); |
25 | MODULE_LICENSE("GPL" ); |
26 | |
27 | static int probe = 0; /* Don't autoprobe */ |
28 | static unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */ |
29 | static unsigned width = 8; /* Default to 8 bits wide */ |
30 | static char *flashtype = "cfi_probe" ; |
31 | |
32 | module_param(probe, int, 0); |
33 | MODULE_PARM_DESC(probe, "Probe for a BIOS mapping" ); |
34 | module_param(size, int, 0); |
35 | MODULE_PARM_DESC(size, "Size of the flash mapping" ); |
36 | module_param(width, int, 0); |
37 | MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)" ); |
38 | module_param(flashtype, charp, 0); |
39 | MODULE_PARM_DESC(flashtype, "Type of MTD probe to do" ); |
40 | |
41 | static struct resource docmem = { |
42 | .flags = IORESOURCE_MEM, |
43 | .name = "NatSemi SCx200 DOCCS Flash" , |
44 | }; |
45 | |
46 | static struct mtd_info *mymtd; |
47 | |
48 | static struct mtd_partition partition_info[] = { |
49 | { |
50 | .name = "DOCCS Boot kernel" , |
51 | .offset = 0, |
52 | .size = 0xc0000 |
53 | }, |
54 | { |
55 | .name = "DOCCS Low BIOS" , |
56 | .offset = 0xc0000, |
57 | .size = 0x40000 |
58 | }, |
59 | { |
60 | .name = "DOCCS File system" , |
61 | .offset = 0x100000, |
62 | .size = ~0 /* calculate from flash size */ |
63 | }, |
64 | { |
65 | .name = "DOCCS High BIOS" , |
66 | .offset = ~0, /* calculate from flash size */ |
67 | .size = 0x80000 |
68 | }, |
69 | }; |
70 | #define NUM_PARTITIONS ARRAY_SIZE(partition_info) |
71 | |
72 | static struct map_info scx200_docflash_map = { |
73 | .name = "NatSemi SCx200 DOCCS Flash" , |
74 | }; |
75 | |
76 | static int __init init_scx200_docflash(void) |
77 | { |
78 | unsigned u; |
79 | unsigned base; |
80 | unsigned ctrl; |
81 | unsigned pmr; |
82 | struct pci_dev *bridge; |
83 | |
84 | printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n" ); |
85 | |
86 | if ((bridge = pci_get_device(PCI_VENDOR_ID_NS, |
87 | PCI_DEVICE_ID_NS_SCx200_BRIDGE, |
88 | NULL)) == NULL) |
89 | return -ENODEV; |
90 | |
91 | /* check that we have found the configuration block */ |
92 | if (!scx200_cb_present()) { |
93 | pci_dev_put(dev: bridge); |
94 | return -ENODEV; |
95 | } |
96 | |
97 | if (probe) { |
98 | /* Try to use the present flash mapping if any */ |
99 | pci_read_config_dword(dev: bridge, SCx200_DOCCS_BASE, val: &base); |
100 | pci_read_config_dword(dev: bridge, SCx200_DOCCS_CTRL, val: &ctrl); |
101 | pci_dev_put(dev: bridge); |
102 | |
103 | pmr = inl(port: scx200_cb_base + SCx200_PMR); |
104 | |
105 | if (base == 0 |
106 | || (ctrl & 0x07000000) != 0x07000000 |
107 | || (ctrl & 0x0007ffff) == 0) |
108 | return -ENODEV; |
109 | |
110 | size = ((ctrl&0x1fff)<<13) + (1<<13); |
111 | |
112 | for (u = size; u > 1; u >>= 1) |
113 | ; |
114 | if (u != 1) |
115 | return -ENODEV; |
116 | |
117 | if (pmr & (1<<6)) |
118 | width = 16; |
119 | else |
120 | width = 8; |
121 | |
122 | docmem.start = base; |
123 | docmem.end = base + size; |
124 | |
125 | if (request_resource(root: &iomem_resource, new: &docmem)) { |
126 | printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n" ); |
127 | return -ENOMEM; |
128 | } |
129 | } else { |
130 | pci_dev_put(dev: bridge); |
131 | for (u = size; u > 1; u >>= 1) |
132 | ; |
133 | if (u != 1) { |
134 | printk(KERN_ERR NAME ": invalid size for flash mapping\n" ); |
135 | return -EINVAL; |
136 | } |
137 | |
138 | if (width != 8 && width != 16) { |
139 | printk(KERN_ERR NAME ": invalid bus width for flash mapping\n" ); |
140 | return -EINVAL; |
141 | } |
142 | |
143 | if (allocate_resource(root: &iomem_resource, new: &docmem, |
144 | size, |
145 | min: 0xc0000000, max: 0xffffffff, |
146 | align: size, NULL, NULL)) { |
147 | printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n" ); |
148 | return -ENOMEM; |
149 | } |
150 | |
151 | ctrl = 0x07000000 | ((size-1) >> 13); |
152 | |
153 | printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n" , (long)docmem.start, (long)ctrl); |
154 | |
155 | pci_write_config_dword(dev: bridge, SCx200_DOCCS_BASE, val: docmem.start); |
156 | pci_write_config_dword(dev: bridge, SCx200_DOCCS_CTRL, val: ctrl); |
157 | pmr = inl(port: scx200_cb_base + SCx200_PMR); |
158 | |
159 | if (width == 8) { |
160 | pmr &= ~(1<<6); |
161 | } else { |
162 | pmr |= (1<<6); |
163 | } |
164 | outl(value: pmr, port: scx200_cb_base + SCx200_PMR); |
165 | } |
166 | |
167 | printk(KERN_INFO NAME ": DOCCS mapped at %pR, width %d\n" , |
168 | &docmem, width); |
169 | |
170 | scx200_docflash_map.size = size; |
171 | if (width == 8) |
172 | scx200_docflash_map.bankwidth = 1; |
173 | else |
174 | scx200_docflash_map.bankwidth = 2; |
175 | |
176 | simple_map_init(&scx200_docflash_map); |
177 | |
178 | scx200_docflash_map.phys = docmem.start; |
179 | scx200_docflash_map.virt = ioremap(offset: docmem.start, size: scx200_docflash_map.size); |
180 | if (!scx200_docflash_map.virt) { |
181 | printk(KERN_ERR NAME ": failed to ioremap the flash\n" ); |
182 | release_resource(new: &docmem); |
183 | return -EIO; |
184 | } |
185 | |
186 | mymtd = do_map_probe(name: flashtype, map: &scx200_docflash_map); |
187 | if (!mymtd) { |
188 | printk(KERN_ERR NAME ": unable to detect flash\n" ); |
189 | iounmap(addr: scx200_docflash_map.virt); |
190 | release_resource(new: &docmem); |
191 | return -ENXIO; |
192 | } |
193 | |
194 | if (size < mymtd->size) |
195 | printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n" ); |
196 | |
197 | mymtd->owner = THIS_MODULE; |
198 | |
199 | partition_info[3].offset = mymtd->size-partition_info[3].size; |
200 | partition_info[2].size = partition_info[3].offset-partition_info[2].offset; |
201 | mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static void __exit cleanup_scx200_docflash(void) |
207 | { |
208 | if (mymtd) { |
209 | mtd_device_unregister(master: mymtd); |
210 | map_destroy(mtd: mymtd); |
211 | } |
212 | if (scx200_docflash_map.virt) { |
213 | iounmap(addr: scx200_docflash_map.virt); |
214 | release_resource(new: &docmem); |
215 | } |
216 | } |
217 | |
218 | module_init(init_scx200_docflash); |
219 | module_exit(cleanup_scx200_docflash); |
220 | |