aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ssb/bridge_pcmcia_80211.c
blob: ffa379efff83c7609ecf2be2516e5edd34bc250e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Broadcom 43xx PCMCIA-SSB bridge module
 *
 * Copyright (c) 2007 Michael Buesch <m@bues.ch>
 *
 * Licensed under the GNU/GPL. See COPYING for details.
 */

#include "ssb_private.h"

#include <linux/ssb/ssb.h>
#include <linux/slab.h>
#include <linux/module.h>

#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>

static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = {
	PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
	PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
	PCMCIA_DEVICE_NULL,
};

MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl);

static int ssb_host_pcmcia_probe(struct pcmcia_device *dev)
{
	struct ssb_bus *ssb;
	int err = -ENOMEM;
	int res = 0;

	ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
	if (!ssb)
		goto out_error;

	err = -ENODEV;

	dev->config_flags |= CONF_ENABLE_IRQ;

	dev->resource[2]->flags |=  WIN_ENABLE | WIN_DATA_WIDTH_16 |
			 WIN_USE_WAIT;
	dev->resource[2]->start = 0;
	dev->resource[2]->end = SSB_CORE_SIZE;
	res = pcmcia_request_window(dev, dev->resource[2], 250);
	if (res != 0)
		goto err_kfree_ssb;

	res = pcmcia_map_mem_page(dev, dev->resource[2], 0);
	if (res != 0)
		goto err_disable;

	if (!dev->irq)
		goto err_disable;

	res = pcmcia_enable_device(dev);
	if (res != 0)
		goto err_disable;

	err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);
	if (err)
		goto err_disable;
	dev->priv = ssb;

	return 0;

err_disable:
	pcmcia_disable_device(dev);
err_kfree_ssb:
	kfree(ssb);
out_error:
	dev_err(&dev->dev, "Initialization failed (%d, %d)\n", res, err);
	return err;
}

static void ssb_host_pcmcia_remove(struct pcmcia_device *dev)
{
	struct ssb_bus *ssb = dev->priv;

	ssb_bus_unregister(ssb);
	pcmcia_disable_device(dev);
	kfree(ssb);
	dev->priv = NULL;
}

#ifdef CONFIG_PM
static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev)
{
	struct ssb_bus *ssb = dev->priv;

	return ssb_bus_suspend(ssb);
}

static int ssb_host_pcmcia_resume(struct pcmcia_device *dev)
{
	struct ssb_bus *ssb = dev->priv;

	return ssb_bus_resume(ssb);
}
#else /* CONFIG_PM */
# define ssb_host_pcmcia_suspend		NULL
# define ssb_host_pcmcia_resume		NULL
#endif /* CONFIG_PM */

static struct pcmcia_driver ssb_host_pcmcia_driver = {
	.owner		= THIS_MODULE,
	.name		= "ssb-pcmcia",
	.id_table	= ssb_host_pcmcia_tbl,
	.probe		= ssb_host_pcmcia_probe,
	.remove		= ssb_host_pcmcia_remove,
	.suspend	= ssb_host_pcmcia_suspend,
	.resume		= ssb_host_pcmcia_resume,
};

static int pcmcia_init_failed;

/*
 * These are not module init/exit functions!
 * The module_pcmcia_driver() helper cannot be used here.
 */
int ssb_host_pcmcia_init(void)
{
	pcmcia_init_failed = pcmcia_register_driver(&ssb_host_pcmcia_driver);

	return pcmcia_init_failed;
}

void ssb_host_pcmcia_exit(void)
{
	if (!pcmcia_init_failed)
		pcmcia_unregister_driver(&ssb_host_pcmcia_driver);
}