summaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia/pci_socket.c
blob: 51981e41e6d571b237c6dce52009e95cffa744e5 (plain)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
 * Generic PCI pccard driver interface.
 *
 * (C) Copyright 1999 Linus Torvalds
 *
 * This implements the common parts of PCI pccard drivers,
 * notably detection and infrastructure conversion (ie change
 * from socket index to "struct pci_dev" etc)
 *
 * This does NOT implement the actual low-level driver details,
 * and this has on purpose been left generic enough that it can
 * be used to set up a PCI PCMCIA controller (ie non-cardbus),
 * or to set up a controller.
 *
 * See for example the "yenta" driver for PCI cardbus controllers
 * conforming to the yenta cardbus specifications.
 */
#include <linux/module.h>

#include <linux/init.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/interrupt.h>

#include <pcmcia/ss.h>

#include <asm/io.h>

#include "pci_socket.h"

/*
 * Arbitrary define. This is the array of active cardbus
 * entries.
 */
#define MAX_SOCKETS (8)
static pci_socket_t pci_socket_array[MAX_SOCKETS];

static int pci_init_socket(unsigned int sock)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->init)
		return socket->op->init(socket);
	return -EINVAL;
}

static int pci_suspend_socket(unsigned int sock)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->suspend)
		return socket->op->suspend(socket);
	return -EINVAL;
}

static int pci_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void * info)
{
	pci_socket_t *socket = pci_socket_array + sock;

	socket->handler = handler;
	socket->info = info;
	if (handler)
		MOD_INC_USE_COUNT;
	else
		MOD_DEC_USE_COUNT;
	return 0;
}

static int pci_inquire_socket(unsigned int sock, socket_cap_t *cap)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->inquire)
		return socket->op->inquire(socket, cap);
	return -EINVAL;
}

static int pci_get_status(unsigned int sock, unsigned int *value)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->get_status)
		return socket->op->get_status(socket, value);
	*value = 0;
	return -EINVAL;
}

static int pci_get_socket(unsigned int sock, socket_state_t *state)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->get_socket)
		return socket->op->get_socket(socket, state);
	return -EINVAL;
}

static int pci_set_socket(unsigned int sock, socket_state_t *state)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->set_socket)
		return socket->op->set_socket(socket, state);
	return -EINVAL;
}

static int pci_get_io_map(unsigned int sock, struct pccard_io_map *io)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->get_io_map)
		return socket->op->get_io_map(socket, io);
	return -EINVAL;
}

static int pci_set_io_map(unsigned int sock, struct pccard_io_map *io)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->set_io_map)
		return socket->op->set_io_map(socket, io);
	return -EINVAL;
}

static int pci_get_mem_map(unsigned int sock, struct pccard_mem_map *mem)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->get_mem_map)
		return socket->op->get_mem_map(socket, mem);
	return -EINVAL;
}

static int pci_set_mem_map(unsigned int sock, struct pccard_mem_map *mem)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->set_mem_map)
		return socket->op->set_mem_map(socket, mem);
	return -EINVAL;
}

static void pci_proc_setup(unsigned int sock, struct proc_dir_entry *base)
{
	pci_socket_t *socket = pci_socket_array + sock;

	if (socket->op && socket->op->proc_setup)
		socket->op->proc_setup(socket, base);
}

static struct pccard_operations pci_socket_operations = {
	pci_init_socket,
	pci_suspend_socket,
	pci_register_callback,
	pci_inquire_socket,
	pci_get_status,
	pci_get_socket,
	pci_set_socket,
	pci_get_io_map,
	pci_set_io_map,
	pci_get_mem_map,
	pci_set_mem_map,
	pci_proc_setup
};

static int __init add_pci_socket(int nr, struct pci_dev *dev, struct pci_socket_ops *ops)
{
	pci_socket_t *socket = nr + pci_socket_array;

	socket->dev = dev;
	socket->op = ops;
	return socket->op->open(socket);
}

static int __init pci_socket_init(void)
{
	struct pci_dev *dev = NULL;
	int nr = 0;

	while ((dev = pci_find_class(PCI_CLASS_BRIDGE_CARDBUS << 8, dev)) != NULL) {
		printk("Adding cardbus controller %d: %s\n", nr, dev->name);
		add_pci_socket(nr, dev, &yenta_operations);
		nr++;
	}

	if (nr <= 0)
		return -1;
	register_ss_entry(nr, &pci_socket_operations);
	return 0;
}

static void __exit pci_socket_exit(void)
{
	int i;

	unregister_ss_entry(&pci_socket_operations);
	for (i = 0; i < MAX_SOCKETS; i++) {
		pci_socket_t *socket = pci_socket_array + i;

		if (socket->op && socket->op->close)
			socket->op->close(socket);
	}
}

module_init(pci_socket_init);
module_exit(pci_socket_exit);