1 | /* |
2 | * Broadcom 43xx PCMCIA-SSB bridge module |
3 | * |
4 | * Copyright (c) 2007 Michael Buesch <m@bues.ch> |
5 | * |
6 | * Licensed under the GNU/GPL. See COPYING for details. |
7 | */ |
8 | |
9 | #include "ssb_private.h" |
10 | |
11 | #include <linux/ssb/ssb.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/module.h> |
14 | |
15 | #include <pcmcia/cistpl.h> |
16 | #include <pcmcia/ciscode.h> |
17 | #include <pcmcia/ds.h> |
18 | #include <pcmcia/cisreg.h> |
19 | |
20 | static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = { |
21 | PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), |
22 | PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476), |
23 | PCMCIA_DEVICE_NULL, |
24 | }; |
25 | |
26 | MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl); |
27 | |
28 | static int ssb_host_pcmcia_probe(struct pcmcia_device *dev) |
29 | { |
30 | struct ssb_bus *ssb; |
31 | int err = -ENOMEM; |
32 | int res = 0; |
33 | |
34 | ssb = kzalloc(size: sizeof(*ssb), GFP_KERNEL); |
35 | if (!ssb) |
36 | goto out_error; |
37 | |
38 | err = -ENODEV; |
39 | |
40 | dev->config_flags |= CONF_ENABLE_IRQ; |
41 | |
42 | dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 | |
43 | WIN_USE_WAIT; |
44 | dev->resource[2]->start = 0; |
45 | dev->resource[2]->end = SSB_CORE_SIZE; |
46 | res = pcmcia_request_window(p_dev: dev, res: dev->resource[2], speed: 250); |
47 | if (res != 0) |
48 | goto err_kfree_ssb; |
49 | |
50 | res = pcmcia_map_mem_page(p_dev: dev, res: dev->resource[2], offset: 0); |
51 | if (res != 0) |
52 | goto err_disable; |
53 | |
54 | if (!dev->irq) |
55 | goto err_disable; |
56 | |
57 | res = pcmcia_enable_device(p_dev: dev); |
58 | if (res != 0) |
59 | goto err_disable; |
60 | |
61 | err = ssb_bus_pcmciabus_register(bus: ssb, pcmcia_dev: dev, baseaddr: dev->resource[2]->start); |
62 | if (err) |
63 | goto err_disable; |
64 | dev->priv = ssb; |
65 | |
66 | return 0; |
67 | |
68 | err_disable: |
69 | pcmcia_disable_device(p_dev: dev); |
70 | err_kfree_ssb: |
71 | kfree(objp: ssb); |
72 | out_error: |
73 | dev_err(&dev->dev, "Initialization failed (%d, %d)\n" , res, err); |
74 | return err; |
75 | } |
76 | |
77 | static void ssb_host_pcmcia_remove(struct pcmcia_device *dev) |
78 | { |
79 | struct ssb_bus *ssb = dev->priv; |
80 | |
81 | ssb_bus_unregister(bus: ssb); |
82 | pcmcia_disable_device(p_dev: dev); |
83 | kfree(objp: ssb); |
84 | dev->priv = NULL; |
85 | } |
86 | |
87 | #ifdef CONFIG_PM |
88 | static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev) |
89 | { |
90 | struct ssb_bus *ssb = dev->priv; |
91 | |
92 | return ssb_bus_suspend(bus: ssb); |
93 | } |
94 | |
95 | static int ssb_host_pcmcia_resume(struct pcmcia_device *dev) |
96 | { |
97 | struct ssb_bus *ssb = dev->priv; |
98 | |
99 | return ssb_bus_resume(bus: ssb); |
100 | } |
101 | #else /* CONFIG_PM */ |
102 | # define ssb_host_pcmcia_suspend NULL |
103 | # define ssb_host_pcmcia_resume NULL |
104 | #endif /* CONFIG_PM */ |
105 | |
106 | static struct pcmcia_driver ssb_host_pcmcia_driver = { |
107 | .owner = THIS_MODULE, |
108 | .name = "ssb-pcmcia" , |
109 | .id_table = ssb_host_pcmcia_tbl, |
110 | .probe = ssb_host_pcmcia_probe, |
111 | .remove = ssb_host_pcmcia_remove, |
112 | .suspend = ssb_host_pcmcia_suspend, |
113 | .resume = ssb_host_pcmcia_resume, |
114 | }; |
115 | |
116 | static int pcmcia_init_failed; |
117 | |
118 | /* |
119 | * These are not module init/exit functions! |
120 | * The module_pcmcia_driver() helper cannot be used here. |
121 | */ |
122 | int ssb_host_pcmcia_init(void) |
123 | { |
124 | pcmcia_init_failed = pcmcia_register_driver(driver: &ssb_host_pcmcia_driver); |
125 | |
126 | return pcmcia_init_failed; |
127 | } |
128 | |
129 | void ssb_host_pcmcia_exit(void) |
130 | { |
131 | if (!pcmcia_init_failed) |
132 | pcmcia_unregister_driver(driver: &ssb_host_pcmcia_driver); |
133 | } |
134 | |