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(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 
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
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 
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  */
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 
129 void ssb_host_pcmcia_exit(void)
130 {
131 	if (!pcmcia_init_failed)
132 		pcmcia_unregister_driver(&ssb_host_pcmcia_driver);
133 }
134