diff options
Diffstat (limited to 'drivers/mtd/maps/bfin-async-flash.c')
-rw-r--r-- | drivers/mtd/maps/bfin-async-flash.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c new file mode 100644 index 000000000000..6fec86aaed7e --- /dev/null +++ b/drivers/mtd/maps/bfin-async-flash.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * drivers/mtd/maps/bfin-async-flash.c | ||
3 | * | ||
4 | * Handle the case where flash memory and ethernet mac/phy are | ||
5 | * mapped onto the same async bank. The BF533-STAMP does this | ||
6 | * for example. All board-specific configuration goes in your | ||
7 | * board resources file. | ||
8 | * | ||
9 | * Copyright 2000 Nicolas Pitre <nico@cam.org> | ||
10 | * Copyright 2005-2008 Analog Devices Inc. | ||
11 | * | ||
12 | * Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * Licensed under the GPL-2 or later. | ||
15 | */ | ||
16 | |||
17 | #include <linux/init.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/mtd/mtd.h> | ||
21 | #include <linux/mtd/map.h> | ||
22 | #include <linux/mtd/partitions.h> | ||
23 | #include <linux/mtd/physmap.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/types.h> | ||
26 | |||
27 | #include <asm/blackfin.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <asm/unaligned.h> | ||
31 | |||
32 | #define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) | ||
33 | |||
34 | #define DRIVER_NAME "bfin-async-flash" | ||
35 | |||
36 | struct async_state { | ||
37 | struct mtd_info *mtd; | ||
38 | struct map_info map; | ||
39 | int enet_flash_pin; | ||
40 | uint32_t flash_ambctl0, flash_ambctl1; | ||
41 | uint32_t save_ambctl0, save_ambctl1; | ||
42 | unsigned long irq_flags; | ||
43 | }; | ||
44 | |||
45 | static void switch_to_flash(struct async_state *state) | ||
46 | { | ||
47 | local_irq_save(state->irq_flags); | ||
48 | |||
49 | gpio_set_value(state->enet_flash_pin, 0); | ||
50 | |||
51 | state->save_ambctl0 = bfin_read_EBIU_AMBCTL0(); | ||
52 | state->save_ambctl1 = bfin_read_EBIU_AMBCTL1(); | ||
53 | bfin_write_EBIU_AMBCTL0(state->flash_ambctl0); | ||
54 | bfin_write_EBIU_AMBCTL1(state->flash_ambctl1); | ||
55 | SSYNC(); | ||
56 | } | ||
57 | |||
58 | static void switch_back(struct async_state *state) | ||
59 | { | ||
60 | bfin_write_EBIU_AMBCTL0(state->save_ambctl0); | ||
61 | bfin_write_EBIU_AMBCTL1(state->save_ambctl1); | ||
62 | SSYNC(); | ||
63 | |||
64 | gpio_set_value(state->enet_flash_pin, 1); | ||
65 | |||
66 | local_irq_restore(state->irq_flags); | ||
67 | } | ||
68 | |||
69 | static map_word bfin_read(struct map_info *map, unsigned long ofs) | ||
70 | { | ||
71 | struct async_state *state = (struct async_state *)map->map_priv_1; | ||
72 | uint16_t word; | ||
73 | map_word test; | ||
74 | |||
75 | switch_to_flash(state); | ||
76 | |||
77 | word = readw(map->virt + ofs); | ||
78 | |||
79 | switch_back(state); | ||
80 | |||
81 | test.x[0] = word; | ||
82 | return test; | ||
83 | } | ||
84 | |||
85 | static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) | ||
86 | { | ||
87 | struct async_state *state = (struct async_state *)map->map_priv_1; | ||
88 | |||
89 | switch_to_flash(state); | ||
90 | |||
91 | memcpy(to, map->virt + from, len); | ||
92 | |||
93 | switch_back(state); | ||
94 | } | ||
95 | |||
96 | static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) | ||
97 | { | ||
98 | struct async_state *state = (struct async_state *)map->map_priv_1; | ||
99 | uint16_t d; | ||
100 | |||
101 | d = d1.x[0]; | ||
102 | |||
103 | switch_to_flash(state); | ||
104 | |||
105 | writew(d, map->virt + ofs); | ||
106 | SSYNC(); | ||
107 | |||
108 | switch_back(state); | ||
109 | } | ||
110 | |||
111 | static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) | ||
112 | { | ||
113 | struct async_state *state = (struct async_state *)map->map_priv_1; | ||
114 | |||
115 | switch_to_flash(state); | ||
116 | |||
117 | memcpy(map->virt + to, from, len); | ||
118 | SSYNC(); | ||
119 | |||
120 | switch_back(state); | ||
121 | } | ||
122 | |||
123 | #ifdef CONFIG_MTD_PARTITIONS | ||
124 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | ||
125 | #endif | ||
126 | |||
127 | static int __devinit bfin_flash_probe(struct platform_device *pdev) | ||
128 | { | ||
129 | int ret; | ||
130 | struct physmap_flash_data *pdata = pdev->dev.platform_data; | ||
131 | struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
132 | struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
133 | struct async_state *state; | ||
134 | |||
135 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
136 | if (!state) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | state->map.name = DRIVER_NAME; | ||
140 | state->map.read = bfin_read; | ||
141 | state->map.copy_from = bfin_copy_from; | ||
142 | state->map.write = bfin_write; | ||
143 | state->map.copy_to = bfin_copy_to; | ||
144 | state->map.bankwidth = pdata->width; | ||
145 | state->map.size = memory->end - memory->start + 1; | ||
146 | state->map.virt = (void __iomem *)memory->start; | ||
147 | state->map.phys = memory->start; | ||
148 | state->map.map_priv_1 = (unsigned long)state; | ||
149 | state->enet_flash_pin = platform_get_irq(pdev, 0); | ||
150 | state->flash_ambctl0 = flash_ambctl->start; | ||
151 | state->flash_ambctl1 = flash_ambctl->end; | ||
152 | |||
153 | if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) { | ||
154 | pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin); | ||
155 | return -EBUSY; | ||
156 | } | ||
157 | gpio_direction_output(state->enet_flash_pin, 1); | ||
158 | |||
159 | pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8); | ||
160 | state->mtd = do_map_probe(memory->name, &state->map); | ||
161 | if (!state->mtd) | ||
162 | return -ENXIO; | ||
163 | |||
164 | #ifdef CONFIG_MTD_PARTITIONS | ||
165 | ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); | ||
166 | if (ret > 0) { | ||
167 | pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); | ||
168 | add_mtd_partitions(state->mtd, pdata->parts, ret); | ||
169 | |||
170 | } else if (pdata->nr_parts) { | ||
171 | pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); | ||
172 | add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); | ||
173 | |||
174 | } else | ||
175 | #endif | ||
176 | { | ||
177 | pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n"); | ||
178 | add_mtd_device(state->mtd); | ||
179 | } | ||
180 | |||
181 | platform_set_drvdata(pdev, state); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int __devexit bfin_flash_remove(struct platform_device *pdev) | ||
187 | { | ||
188 | struct async_state *state = platform_get_drvdata(pdev); | ||
189 | gpio_free(state->enet_flash_pin); | ||
190 | #ifdef CONFIG_MTD_PARTITIONS | ||
191 | del_mtd_partitions(state->mtd); | ||
192 | #endif | ||
193 | map_destroy(state->mtd); | ||
194 | kfree(state); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static struct platform_driver bfin_flash_driver = { | ||
199 | .probe = bfin_flash_probe, | ||
200 | .remove = __devexit_p(bfin_flash_remove), | ||
201 | .driver = { | ||
202 | .name = DRIVER_NAME, | ||
203 | }, | ||
204 | }; | ||
205 | |||
206 | static int __init bfin_flash_init(void) | ||
207 | { | ||
208 | return platform_driver_register(&bfin_flash_driver); | ||
209 | } | ||
210 | module_init(bfin_flash_init); | ||
211 | |||
212 | static void __exit bfin_flash_exit(void) | ||
213 | { | ||
214 | platform_driver_unregister(&bfin_flash_driver); | ||
215 | } | ||
216 | module_exit(bfin_flash_exit); | ||
217 | |||
218 | MODULE_LICENSE("GPL"); | ||
219 | MODULE_DESCRIPTION("MTD map driver for Blackfins with flash/ethernet on same async bank"); | ||