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
ssb_host_pcmcia_probe(struct pcmcia_device * dev)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(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(dev, dev->resource[2], 250);
47 if (res != 0)
48 goto err_kfree_ssb;
49
50 res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
51 if (res != 0)
52 goto err_disable;
53
54 if (!dev->irq)
55 goto err_disable;
56
57 res = pcmcia_enable_device(dev);
58 if (res != 0)
59 goto err_disable;
60
61 err = ssb_bus_pcmciabus_register(ssb, dev, 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(dev);
70 err_kfree_ssb:
71 kfree(ssb);
72 out_error:
73 dev_err(&dev->dev, "Initialization failed (%d, %d)\n", res, err);
74 return err;
75 }
76
ssb_host_pcmcia_remove(struct pcmcia_device * dev)77 static void ssb_host_pcmcia_remove(struct pcmcia_device *dev)
78 {
79 struct ssb_bus *ssb = dev->priv;
80
81 ssb_bus_unregister(ssb);
82 pcmcia_disable_device(dev);
83 kfree(ssb);
84 dev->priv = NULL;
85 }
86
87 #ifdef CONFIG_PM
ssb_host_pcmcia_suspend(struct pcmcia_device * dev)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(ssb);
93 }
94
ssb_host_pcmcia_resume(struct pcmcia_device * dev)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(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 */
ssb_host_pcmcia_init(void)122 int ssb_host_pcmcia_init(void)
123 {
124 pcmcia_init_failed = pcmcia_register_driver(&ssb_host_pcmcia_driver);
125
126 return pcmcia_init_failed;
127 }
128
ssb_host_pcmcia_exit(void)129 void ssb_host_pcmcia_exit(void)
130 {
131 if (!pcmcia_init_failed)
132 pcmcia_unregister_driver(&ssb_host_pcmcia_driver);
133 }
134