1/*
2
3  Broadcom B43 wireless driver
4
5  Copyright (c) 2007 Michael Buesch <m@bues.ch>
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16
17  You should have received a copy of the GNU General Public License
18  along with this program; see the file COPYING.  If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21
22*/
23
24#include "pcmcia.h"
25
26#include <linux/ssb/ssb.h>
27#include <linux/slab.h>
28#include <linux/module.h>
29
30#include <pcmcia/cistpl.h>
31#include <pcmcia/ciscode.h>
32#include <pcmcia/ds.h>
33#include <pcmcia/cisreg.h>
34
35
36static const struct pcmcia_device_id b43_pcmcia_tbl[] = {
37	PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
38	PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
39	PCMCIA_DEVICE_NULL,
40};
41
42MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl);
43
44#ifdef CONFIG_PM
45static int b43_pcmcia_suspend(struct pcmcia_device *dev)
46{
47	struct ssb_bus *ssb = dev->priv;
48
49	return ssb_bus_suspend(ssb);
50}
51
52static int b43_pcmcia_resume(struct pcmcia_device *dev)
53{
54	struct ssb_bus *ssb = dev->priv;
55
56	return ssb_bus_resume(ssb);
57}
58#else /* CONFIG_PM */
59# define b43_pcmcia_suspend		NULL
60# define b43_pcmcia_resume		NULL
61#endif /* CONFIG_PM */
62
63static int b43_pcmcia_probe(struct pcmcia_device *dev)
64{
65	struct ssb_bus *ssb;
66	int err = -ENOMEM;
67	int res = 0;
68
69	ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
70	if (!ssb)
71		goto out_error;
72
73	err = -ENODEV;
74
75	dev->config_flags |= CONF_ENABLE_IRQ;
76
77	dev->resource[2]->flags |=  WIN_ENABLE | WIN_DATA_WIDTH_16 |
78			 WIN_USE_WAIT;
79	dev->resource[2]->start = 0;
80	dev->resource[2]->end = SSB_CORE_SIZE;
81	res = pcmcia_request_window(dev, dev->resource[2], 250);
82	if (res != 0)
83		goto err_kfree_ssb;
84
85	res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
86	if (res != 0)
87		goto err_disable;
88
89	if (!dev->irq)
90		goto err_disable;
91
92	res = pcmcia_enable_device(dev);
93	if (res != 0)
94		goto err_disable;
95
96	err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);
97	if (err)
98		goto err_disable;
99	dev->priv = ssb;
100
101	return 0;
102
103err_disable:
104	pcmcia_disable_device(dev);
105err_kfree_ssb:
106	kfree(ssb);
107out_error:
108	printk(KERN_ERR "b43-pcmcia: Initialization failed (%d, %d)\n",
109	       res, err);
110	return err;
111}
112
113static void b43_pcmcia_remove(struct pcmcia_device *dev)
114{
115	struct ssb_bus *ssb = dev->priv;
116
117	ssb_bus_unregister(ssb);
118	pcmcia_disable_device(dev);
119	kfree(ssb);
120	dev->priv = NULL;
121}
122
123static struct pcmcia_driver b43_pcmcia_driver = {
124	.owner		= THIS_MODULE,
125	.name		= "b43-pcmcia",
126	.id_table	= b43_pcmcia_tbl,
127	.probe		= b43_pcmcia_probe,
128	.remove		= b43_pcmcia_remove,
129	.suspend	= b43_pcmcia_suspend,
130	.resume		= b43_pcmcia_resume,
131};
132
133/*
134 * These are not module init/exit functions!
135 * The module_pcmcia_driver() helper cannot be used here.
136 */
137int b43_pcmcia_init(void)
138{
139	return pcmcia_register_driver(&b43_pcmcia_driver);
140}
141
142void b43_pcmcia_exit(void)
143{
144	pcmcia_unregister_driver(&b43_pcmcia_driver);
145}
146