Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2c1f3b7a AV |
2 | /* |
3 | * at91_cf.c -- AT91 CompactFlash controller driver | |
4 | * | |
5 | * Copyright (C) 2005 David Brownell | |
2c1f3b7a AV |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/kernel.h> | |
2c1f3b7a AV |
10 | #include <linux/platform_device.h> |
11 | #include <linux/errno.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/interrupt.h> | |
5a0e3ad6 | 14 | #include <linux/slab.h> |
80af9e6d | 15 | #include <linux/gpio.h> |
bcd2360c | 16 | #include <linux/platform_data/atmel.h> |
a843168d JE |
17 | #include <linux/io.h> |
18 | #include <linux/sizes.h> | |
eaa9a21d AB |
19 | #include <linux/mfd/syscon.h> |
20 | #include <linux/mfd/syscon/atmel-mc.h> | |
ed9084ec JE |
21 | #include <linux/of.h> |
22 | #include <linux/of_device.h> | |
23 | #include <linux/of_gpio.h> | |
eaa9a21d | 24 | #include <linux/regmap.h> |
2c1f3b7a AV |
25 | |
26 | #include <pcmcia/ss.h> | |
27 | ||
2c1f3b7a AV |
28 | /* |
29 | * A0..A10 work in each range; A23 indicates I/O space; A25 is CFRNW; | |
30 | * some other bit in {A24,A22..A11} is nREG to flag memory access | |
31 | * (vs attributes). So more than 2KB/region would just be waste. | |
ebe5cfb3 | 32 | * Note: These are offsets from the physical base address. |
2c1f3b7a | 33 | */ |
ebe5cfb3 AV |
34 | #define CF_ATTR_PHYS (0) |
35 | #define CF_IO_PHYS (1 << 23) | |
36 | #define CF_MEM_PHYS (0x017ff800) | |
2c1f3b7a | 37 | |
eaa9a21d AB |
38 | struct regmap *mc; |
39 | ||
2c1f3b7a AV |
40 | /*--------------------------------------------------------------------------*/ |
41 | ||
2c1f3b7a AV |
42 | struct at91_cf_socket { |
43 | struct pcmcia_socket socket; | |
44 | ||
45 | unsigned present:1; | |
46 | ||
47 | struct platform_device *pdev; | |
48 | struct at91_cf_data *board; | |
ebe5cfb3 AV |
49 | |
50 | unsigned long phys_baseaddr; | |
2c1f3b7a AV |
51 | }; |
52 | ||
2c1f3b7a AV |
53 | static inline int at91_cf_present(struct at91_cf_socket *cf) |
54 | { | |
4c1fc445 | 55 | return !gpio_get_value(cf->board->det_pin); |
2c1f3b7a AV |
56 | } |
57 | ||
58 | /*--------------------------------------------------------------------------*/ | |
59 | ||
60 | static int at91_cf_ss_init(struct pcmcia_socket *s) | |
61 | { | |
62 | return 0; | |
63 | } | |
64 | ||
7d12e780 | 65 | static irqreturn_t at91_cf_irq(int irq, void *_cf) |
2c1f3b7a | 66 | { |
c7bec5ab | 67 | struct at91_cf_socket *cf = _cf; |
2c1f3b7a | 68 | |
80af9e6d | 69 | if (irq == gpio_to_irq(cf->board->det_pin)) { |
2c1f3b7a AV |
70 | unsigned present = at91_cf_present(cf); |
71 | ||
72 | /* kick pccard as needed */ | |
73 | if (present != cf->present) { | |
74 | cf->present = present; | |
40ca0209 | 75 | dev_dbg(&cf->pdev->dev, "card %s\n", |
2c536200 | 76 | present ? "present" : "gone"); |
2c1f3b7a AV |
77 | pcmcia_parse_events(&cf->socket, SS_DETECT); |
78 | } | |
79 | } | |
80 | ||
81 | return IRQ_HANDLED; | |
82 | } | |
83 | ||
84 | static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp) | |
85 | { | |
86 | struct at91_cf_socket *cf; | |
87 | ||
88 | if (!sp) | |
89 | return -EINVAL; | |
90 | ||
91 | cf = container_of(s, struct at91_cf_socket, socket); | |
92 | ||
2c536200 | 93 | /* NOTE: CF is always 3VCARD */ |
2c1f3b7a | 94 | if (at91_cf_present(cf)) { |
80af9e6d JE |
95 | int rdy = gpio_is_valid(cf->board->irq_pin); /* RDY/nIRQ */ |
96 | int vcc = gpio_is_valid(cf->board->vcc_pin); | |
2c1f3b7a AV |
97 | |
98 | *sp = SS_DETECT | SS_3VCARD; | |
e39506b4 | 99 | if (!rdy || gpio_get_value(cf->board->irq_pin)) |
2c1f3b7a | 100 | *sp |= SS_READY; |
e39506b4 | 101 | if (!vcc || gpio_get_value(cf->board->vcc_pin)) |
2c1f3b7a AV |
102 | *sp |= SS_POWERON; |
103 | } else | |
104 | *sp = 0; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
2c536200 DB |
109 | static int |
110 | at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) | |
2c1f3b7a AV |
111 | { |
112 | struct at91_cf_socket *cf; | |
113 | ||
114 | cf = container_of(sock, struct at91_cf_socket, socket); | |
115 | ||
116 | /* switch Vcc if needed and possible */ | |
80af9e6d | 117 | if (gpio_is_valid(cf->board->vcc_pin)) { |
2c1f3b7a | 118 | switch (s->Vcc) { |
d652f702 LN |
119 | case 0: |
120 | gpio_set_value(cf->board->vcc_pin, 0); | |
121 | break; | |
122 | case 33: | |
123 | gpio_set_value(cf->board->vcc_pin, 1); | |
124 | break; | |
125 | default: | |
126 | return -EINVAL; | |
2c1f3b7a AV |
127 | } |
128 | } | |
129 | ||
130 | /* toggle reset if needed */ | |
4c1fc445 | 131 | gpio_set_value(cf->board->rst_pin, s->flags & SS_RESET); |
2c1f3b7a | 132 | |
40ca0209 JE |
133 | dev_dbg(&cf->pdev->dev, "Vcc %d, io_irq %d, flags %04x csc %04x\n", |
134 | s->Vcc, s->io_irq, s->flags, s->csc_mask); | |
2c1f3b7a AV |
135 | |
136 | return 0; | |
137 | } | |
138 | ||
139 | static int at91_cf_ss_suspend(struct pcmcia_socket *s) | |
140 | { | |
141 | return at91_cf_set_socket(s, &dead_socket); | |
142 | } | |
143 | ||
144 | /* we already mapped the I/O region */ | |
145 | static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) | |
146 | { | |
147 | struct at91_cf_socket *cf; | |
148 | u32 csr; | |
149 | ||
150 | cf = container_of(s, struct at91_cf_socket, socket); | |
151 | io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ); | |
152 | ||
153 | /* | |
154 | * Use 16 bit accesses unless/until we need 8-bit i/o space. | |
eaa9a21d | 155 | * |
2c1f3b7a AV |
156 | * NOTE: this CF controller ignores IOIS16, so we can't really do |
157 | * MAP_AUTOSZ. The 16bit mode allows single byte access on either | |
158 | * D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many | |
159 | * purposes (and handles ide-cs). | |
160 | * | |
161 | * The 8bit mode is needed for odd byte access on D0-D7. It seems | |
162 | * some cards only like that way to get at the odd byte, despite | |
163 | * CF 3.0 spec table 35 also giving the D8-D15 option. | |
164 | */ | |
ebe5cfb3 | 165 | if (!(io->flags & (MAP_16BIT | MAP_AUTOSZ))) { |
eaa9a21d | 166 | csr = AT91_MC_SMC_DBW_8; |
40ca0209 | 167 | dev_dbg(&cf->pdev->dev, "8bit i/o bus\n"); |
2c1f3b7a | 168 | } else { |
eaa9a21d | 169 | csr = AT91_MC_SMC_DBW_16; |
40ca0209 | 170 | dev_dbg(&cf->pdev->dev, "16bit i/o bus\n"); |
2c1f3b7a | 171 | } |
eaa9a21d AB |
172 | regmap_update_bits(mc, AT91_MC_SMC_CSR(cf->board->chipselect), |
173 | AT91_MC_SMC_DBW, csr); | |
2c1f3b7a AV |
174 | |
175 | io->start = cf->socket.io_offset; | |
176 | io->stop = io->start + SZ_2K - 1; | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | /* pcmcia layer maps/unmaps mem regions */ | |
2c536200 DB |
182 | static int |
183 | at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) | |
2c1f3b7a AV |
184 | { |
185 | struct at91_cf_socket *cf; | |
186 | ||
187 | if (map->card_start) | |
188 | return -EINVAL; | |
189 | ||
190 | cf = container_of(s, struct at91_cf_socket, socket); | |
191 | ||
ebe5cfb3 | 192 | map->flags &= (MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT); |
2c1f3b7a | 193 | if (map->flags & MAP_ATTRIB) |
ebe5cfb3 | 194 | map->static_start = cf->phys_baseaddr + CF_ATTR_PHYS; |
2c1f3b7a | 195 | else |
ebe5cfb3 | 196 | map->static_start = cf->phys_baseaddr + CF_MEM_PHYS; |
2c1f3b7a AV |
197 | |
198 | return 0; | |
199 | } | |
200 | ||
201 | static struct pccard_operations at91_cf_ops = { | |
202 | .init = at91_cf_ss_init, | |
203 | .suspend = at91_cf_ss_suspend, | |
204 | .get_status = at91_cf_get_status, | |
205 | .set_socket = at91_cf_set_socket, | |
206 | .set_io_map = at91_cf_set_io_map, | |
207 | .set_mem_map = at91_cf_set_mem_map, | |
208 | }; | |
209 | ||
210 | /*--------------------------------------------------------------------------*/ | |
211 | ||
ed9084ec JE |
212 | #if defined(CONFIG_OF) |
213 | static const struct of_device_id at91_cf_dt_ids[] = { | |
214 | { .compatible = "atmel,at91rm9200-cf" }, | |
215 | { /* sentinel */ } | |
216 | }; | |
217 | MODULE_DEVICE_TABLE(of, at91_cf_dt_ids); | |
218 | ||
219 | static int at91_cf_dt_init(struct platform_device *pdev) | |
220 | { | |
221 | struct at91_cf_data *board; | |
222 | ||
223 | board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL); | |
224 | if (!board) | |
225 | return -ENOMEM; | |
226 | ||
227 | board->irq_pin = of_get_gpio(pdev->dev.of_node, 0); | |
228 | board->det_pin = of_get_gpio(pdev->dev.of_node, 1); | |
229 | board->vcc_pin = of_get_gpio(pdev->dev.of_node, 2); | |
230 | board->rst_pin = of_get_gpio(pdev->dev.of_node, 3); | |
231 | ||
232 | pdev->dev.platform_data = board; | |
233 | ||
eaa9a21d | 234 | mc = syscon_regmap_lookup_by_compatible("atmel,at91rm9200-sdramc"); |
eaa9a21d | 235 | |
12038392 | 236 | return PTR_ERR_OR_ZERO(mc); |
ed9084ec JE |
237 | } |
238 | #else | |
239 | static int at91_cf_dt_init(struct platform_device *pdev) | |
240 | { | |
241 | return -ENODEV; | |
242 | } | |
243 | #endif | |
244 | ||
16a7c7cf | 245 | static int at91_cf_probe(struct platform_device *pdev) |
2c1f3b7a AV |
246 | { |
247 | struct at91_cf_socket *cf; | |
0db6095d | 248 | struct at91_cf_data *board = pdev->dev.platform_data; |
2c536200 | 249 | struct resource *io; |
2c1f3b7a AV |
250 | int status; |
251 | ||
ed9084ec JE |
252 | if (!board) { |
253 | status = at91_cf_dt_init(pdev); | |
254 | if (status) | |
255 | return status; | |
256 | ||
257 | board = pdev->dev.platform_data; | |
258 | } | |
259 | ||
260 | if (!gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin)) | |
2c1f3b7a AV |
261 | return -ENODEV; |
262 | ||
2c536200 DB |
263 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
264 | if (!io) | |
265 | return -ENODEV; | |
266 | ||
54fe1591 | 267 | cf = devm_kzalloc(&pdev->dev, sizeof(*cf), GFP_KERNEL); |
2c1f3b7a AV |
268 | if (!cf) |
269 | return -ENOMEM; | |
270 | ||
271 | cf->board = board; | |
272 | cf->pdev = pdev; | |
ebe5cfb3 | 273 | cf->phys_baseaddr = io->start; |
0db6095d | 274 | platform_set_drvdata(pdev, cf); |
2c1f3b7a | 275 | |
2c1f3b7a | 276 | /* must be a GPIO; ergo must trigger on both edges */ |
54fe1591 | 277 | status = devm_gpio_request(&pdev->dev, board->det_pin, "cf_det"); |
2c1f3b7a | 278 | if (status < 0) |
54fe1591 JE |
279 | return status; |
280 | ||
281 | status = devm_request_irq(&pdev->dev, gpio_to_irq(board->det_pin), | |
282 | at91_cf_irq, 0, "at91_cf detect", cf); | |
4c1fc445 | 283 | if (status < 0) |
54fe1591 JE |
284 | return status; |
285 | ||
0db6095d | 286 | device_init_wakeup(&pdev->dev, 1); |
2c1f3b7a | 287 | |
54fe1591 | 288 | status = devm_gpio_request(&pdev->dev, board->rst_pin, "cf_rst"); |
4c1fc445 DB |
289 | if (status < 0) |
290 | goto fail0a; | |
291 | ||
80af9e6d | 292 | if (gpio_is_valid(board->vcc_pin)) { |
54fe1591 | 293 | status = devm_gpio_request(&pdev->dev, board->vcc_pin, "cf_vcc"); |
4c1fc445 | 294 | if (status < 0) |
54fe1591 | 295 | goto fail0a; |
4c1fc445 DB |
296 | } |
297 | ||
2c1f3b7a AV |
298 | /* |
299 | * The card driver will request this irq later as needed. | |
300 | * but it causes lots of "irqNN: nobody cared" messages | |
301 | * unless we report that we handle everything (sigh). | |
302 | * (Note: DK board doesn't wire the IRQ pin...) | |
303 | */ | |
80af9e6d | 304 | if (gpio_is_valid(board->irq_pin)) { |
54fe1591 | 305 | status = devm_gpio_request(&pdev->dev, board->irq_pin, "cf_irq"); |
4c1fc445 | 306 | if (status < 0) |
54fe1591 JE |
307 | goto fail0a; |
308 | ||
309 | status = devm_request_irq(&pdev->dev, gpio_to_irq(board->irq_pin), | |
310 | at91_cf_irq, IRQF_SHARED, "at91_cf", cf); | |
2c1f3b7a | 311 | if (status < 0) |
54fe1591 | 312 | goto fail0a; |
80af9e6d | 313 | cf->socket.pci_irq = gpio_to_irq(board->irq_pin); |
2c536200 | 314 | } else |
9130adda | 315 | cf->socket.pci_irq = nr_irqs + 1; |
2c1f3b7a | 316 | |
1be27c62 AB |
317 | /* |
318 | * pcmcia layer only remaps "real" memory not iospace | |
319 | * io_offset is set to 0x10000 to avoid the check in static_find_io(). | |
320 | * */ | |
321 | cf->socket.io_offset = 0x10000; | |
322 | status = pci_ioremap_io(0x10000, cf->phys_baseaddr + CF_IO_PHYS); | |
323 | if (status) | |
54fe1591 | 324 | goto fail0a; |
2c1f3b7a | 325 | |
40a0017e | 326 | /* reserve chip-select regions */ |
54fe1591 | 327 | if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io), "at91_cf")) { |
ebe5cfb3 | 328 | status = -ENXIO; |
54fe1591 | 329 | goto fail0a; |
ebe5cfb3 | 330 | } |
2c1f3b7a | 331 | |
40ca0209 | 332 | dev_info(&pdev->dev, "irqs det #%d, io #%d\n", |
80af9e6d | 333 | gpio_to_irq(board->det_pin), gpio_to_irq(board->irq_pin)); |
2c1f3b7a AV |
334 | |
335 | cf->socket.owner = THIS_MODULE; | |
e4a3c3f0 | 336 | cf->socket.dev.parent = &pdev->dev; |
2c1f3b7a AV |
337 | cf->socket.ops = &at91_cf_ops; |
338 | cf->socket.resource_ops = &pccard_static_ops; | |
339 | cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | |
340 | | SS_CAP_MEM_ALIGN; | |
341 | cf->socket.map_size = SZ_2K; | |
2c536200 | 342 | cf->socket.io[0].res = io; |
2c1f3b7a AV |
343 | |
344 | status = pcmcia_register_socket(&cf->socket); | |
345 | if (status < 0) | |
54fe1591 | 346 | goto fail0a; |
2c1f3b7a AV |
347 | |
348 | return 0; | |
349 | ||
2c1f3b7a | 350 | fail0a: |
1fbece15 | 351 | device_init_wakeup(&pdev->dev, 0); |
2c1f3b7a AV |
352 | return status; |
353 | } | |
354 | ||
16a7c7cf | 355 | static int at91_cf_remove(struct platform_device *pdev) |
2c1f3b7a | 356 | { |
0db6095d | 357 | struct at91_cf_socket *cf = platform_get_drvdata(pdev); |
2c1f3b7a AV |
358 | |
359 | pcmcia_unregister_socket(&cf->socket); | |
0db6095d | 360 | device_init_wakeup(&pdev->dev, 0); |
54fe1591 | 361 | |
2c1f3b7a AV |
362 | return 0; |
363 | } | |
364 | ||
0db6095d DB |
365 | #ifdef CONFIG_PM |
366 | ||
367 | static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg) | |
368 | { | |
369 | struct at91_cf_socket *cf = platform_get_drvdata(pdev); | |
370 | struct at91_cf_data *board = cf->board; | |
371 | ||
1fbece15 | 372 | if (device_may_wakeup(&pdev->dev)) { |
80af9e6d JE |
373 | enable_irq_wake(gpio_to_irq(board->det_pin)); |
374 | if (gpio_is_valid(board->irq_pin)) | |
375 | enable_irq_wake(gpio_to_irq(board->irq_pin)); | |
0db6095d | 376 | } |
0db6095d DB |
377 | return 0; |
378 | } | |
379 | ||
380 | static int at91_cf_resume(struct platform_device *pdev) | |
381 | { | |
9af20376 MP |
382 | struct at91_cf_socket *cf = platform_get_drvdata(pdev); |
383 | struct at91_cf_data *board = cf->board; | |
384 | ||
385 | if (device_may_wakeup(&pdev->dev)) { | |
80af9e6d JE |
386 | disable_irq_wake(gpio_to_irq(board->det_pin)); |
387 | if (gpio_is_valid(board->irq_pin)) | |
388 | disable_irq_wake(gpio_to_irq(board->irq_pin)); | |
9af20376 MP |
389 | } |
390 | ||
0db6095d DB |
391 | return 0; |
392 | } | |
393 | ||
394 | #else | |
395 | #define at91_cf_suspend NULL | |
396 | #define at91_cf_resume NULL | |
397 | #endif | |
398 | ||
399 | static struct platform_driver at91_cf_driver = { | |
400 | .driver = { | |
40ca0209 | 401 | .name = "at91_cf", |
ed9084ec | 402 | .of_match_table = of_match_ptr(at91_cf_dt_ids), |
0db6095d | 403 | }, |
16a7c7cf JH |
404 | .probe = at91_cf_probe, |
405 | .remove = at91_cf_remove, | |
0db6095d DB |
406 | .suspend = at91_cf_suspend, |
407 | .resume = at91_cf_resume, | |
2c1f3b7a AV |
408 | }; |
409 | ||
16a7c7cf | 410 | module_platform_driver(at91_cf_driver); |
2c1f3b7a AV |
411 | |
412 | MODULE_DESCRIPTION("AT91 Compact Flash Driver"); | |
413 | MODULE_AUTHOR("David Brownell"); | |
414 | MODULE_LICENSE("GPL"); | |
12c2c019 | 415 | MODULE_ALIAS("platform:at91_cf"); |