1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX, |
3 | SBC-GXm and SBC-GX1 series boards. |
4 | |
5 | Copyright (C) 2001 Arcom Control System Ltd |
6 | |
7 | |
8 | The SBC-MediaGX / SBC-GXx has up to 16 MiB of |
9 | Intel StrataFlash (28F320/28F640) in x8 mode. |
10 | |
11 | This driver uses the CFI probe and Intel Extended Command Set drivers. |
12 | |
13 | The flash is accessed as follows: |
14 | |
15 | 16 KiB memory window at 0xdc000-0xdffff |
16 | |
17 | Two IO address locations for paging |
18 | |
19 | 0x258 |
20 | bit 0-7: address bit 14-21 |
21 | 0x259 |
22 | bit 0-1: address bit 22-23 |
23 | bit 7: 0 - reset/powered down |
24 | 1 - device enabled |
25 | |
26 | The single flash device is divided into 3 partition which appear as |
27 | separate MTD devices. |
28 | |
29 | 25/04/2001 AJL (Arcom) Modified signon strings and partition sizes |
30 | (to support bzImages up to 638KiB-ish) |
31 | */ |
32 | |
33 | // Includes |
34 | |
35 | #include <linux/module.h> |
36 | #include <linux/ioport.h> |
37 | #include <linux/init.h> |
38 | #include <asm/io.h> |
39 | |
40 | #include <linux/mtd/mtd.h> |
41 | #include <linux/mtd/map.h> |
42 | #include <linux/mtd/partitions.h> |
43 | |
44 | // Defines |
45 | |
46 | // - Hardware specific |
47 | |
48 | #define WINDOW_START 0xdc000 |
49 | |
50 | /* Number of bits in offset. */ |
51 | #define WINDOW_SHIFT 14 |
52 | #define WINDOW_LENGTH (1 << WINDOW_SHIFT) |
53 | |
54 | /* The bits for the offset into the window. */ |
55 | #define WINDOW_MASK (WINDOW_LENGTH-1) |
56 | #define PAGE_IO 0x258 |
57 | #define PAGE_IO_SIZE 2 |
58 | |
59 | /* bit 7 of 0x259 must be 1 to enable device. */ |
60 | #define DEVICE_ENABLE 0x8000 |
61 | |
62 | // - Flash / Partition sizing |
63 | |
64 | #define MAX_SIZE_KiB 16384 |
65 | #define BOOT_PARTITION_SIZE_KiB 768 |
66 | #define DATA_PARTITION_SIZE_KiB 1280 |
67 | #define APP_PARTITION_SIZE_KiB 6144 |
68 | |
69 | // Globals |
70 | |
71 | static volatile int page_in_window = -1; // Current page in window. |
72 | static void __iomem *iomapadr; |
73 | static DEFINE_SPINLOCK(sbc_gxx_spin); |
74 | |
75 | /* partition_info gives details on the logical partitions that the split the |
76 | * single flash device into. If the size if zero we use up to the end of the |
77 | * device. */ |
78 | static const struct mtd_partition partition_info[] = { |
79 | { .name = "SBC-GXx flash boot partition" , |
80 | .offset = 0, |
81 | .size = BOOT_PARTITION_SIZE_KiB*1024 }, |
82 | { .name = "SBC-GXx flash data partition" , |
83 | .offset = BOOT_PARTITION_SIZE_KiB*1024, |
84 | .size = (DATA_PARTITION_SIZE_KiB)*1024 }, |
85 | { .name = "SBC-GXx flash application partition" , |
86 | .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } |
87 | }; |
88 | |
89 | #define NUM_PARTITIONS 3 |
90 | |
91 | static inline void sbc_gxx_page(struct map_info *map, unsigned long ofs) |
92 | { |
93 | unsigned long page = ofs >> WINDOW_SHIFT; |
94 | |
95 | if( page!=page_in_window ) { |
96 | outw( value: page | DEVICE_ENABLE, PAGE_IO ); |
97 | page_in_window = page; |
98 | } |
99 | } |
100 | |
101 | |
102 | static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) |
103 | { |
104 | map_word ret; |
105 | spin_lock(lock: &sbc_gxx_spin); |
106 | sbc_gxx_page(map, ofs); |
107 | ret.x[0] = readb(addr: iomapadr + (ofs & WINDOW_MASK)); |
108 | spin_unlock(lock: &sbc_gxx_spin); |
109 | return ret; |
110 | } |
111 | |
112 | static void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) |
113 | { |
114 | while(len) { |
115 | unsigned long thislen = len; |
116 | if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) |
117 | thislen = WINDOW_LENGTH-(from & WINDOW_MASK); |
118 | |
119 | spin_lock(lock: &sbc_gxx_spin); |
120 | sbc_gxx_page(map, ofs: from); |
121 | memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); |
122 | spin_unlock(lock: &sbc_gxx_spin); |
123 | to += thislen; |
124 | from += thislen; |
125 | len -= thislen; |
126 | } |
127 | } |
128 | |
129 | static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr) |
130 | { |
131 | spin_lock(lock: &sbc_gxx_spin); |
132 | sbc_gxx_page(map, ofs: adr); |
133 | writeb(val: d.x[0], addr: iomapadr + (adr & WINDOW_MASK)); |
134 | spin_unlock(lock: &sbc_gxx_spin); |
135 | } |
136 | |
137 | static void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) |
138 | { |
139 | while(len) { |
140 | unsigned long thislen = len; |
141 | if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) |
142 | thislen = WINDOW_LENGTH-(to & WINDOW_MASK); |
143 | |
144 | spin_lock(lock: &sbc_gxx_spin); |
145 | sbc_gxx_page(map, ofs: to); |
146 | memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); |
147 | spin_unlock(lock: &sbc_gxx_spin); |
148 | to += thislen; |
149 | from += thislen; |
150 | len -= thislen; |
151 | } |
152 | } |
153 | |
154 | static struct map_info sbc_gxx_map = { |
155 | .name = "SBC-GXx flash" , |
156 | .phys = NO_XIP, |
157 | .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount |
158 | of flash so the cfi probe routines find all |
159 | the chips */ |
160 | .bankwidth = 1, |
161 | .read = sbc_gxx_read8, |
162 | .copy_from = sbc_gxx_copy_from, |
163 | .write = sbc_gxx_write8, |
164 | .copy_to = sbc_gxx_copy_to |
165 | }; |
166 | |
167 | /* MTD device for all of the flash. */ |
168 | static struct mtd_info *all_mtd; |
169 | |
170 | static void cleanup_sbc_gxx(void) |
171 | { |
172 | if( all_mtd ) { |
173 | mtd_device_unregister(master: all_mtd); |
174 | map_destroy( mtd: all_mtd ); |
175 | } |
176 | |
177 | iounmap(addr: iomapadr); |
178 | release_region(PAGE_IO,PAGE_IO_SIZE); |
179 | } |
180 | |
181 | static int __init init_sbc_gxx(void) |
182 | { |
183 | iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); |
184 | if (!iomapadr) { |
185 | printk( KERN_ERR"%s: failed to ioremap memory region\n" , |
186 | sbc_gxx_map.name ); |
187 | return -EIO; |
188 | } |
189 | |
190 | if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" )) { |
191 | printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n" , |
192 | sbc_gxx_map.name, |
193 | PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); |
194 | iounmap(addr: iomapadr); |
195 | return -EAGAIN; |
196 | } |
197 | |
198 | |
199 | printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n" , |
200 | sbc_gxx_map.name, |
201 | PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, |
202 | WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); |
203 | |
204 | /* Probe for chip. */ |
205 | all_mtd = do_map_probe( name: "cfi_probe" , map: &sbc_gxx_map ); |
206 | if( !all_mtd ) { |
207 | cleanup_sbc_gxx(); |
208 | return -ENXIO; |
209 | } |
210 | |
211 | all_mtd->owner = THIS_MODULE; |
212 | |
213 | /* Create MTD devices for each partition. */ |
214 | mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | module_init(init_sbc_gxx); |
220 | module_exit(cleanup_sbc_gxx); |
221 | |
222 | MODULE_LICENSE("GPL" ); |
223 | MODULE_AUTHOR("Arcom Control Systems Ltd." ); |
224 | MODULE_DESCRIPTION("MTD map driver for SBC-GXm and SBC-GX1 series boards" ); |
225 | |