1/*	$NetBSD: scoop_pcic.c,v 1.3 2009/01/29 12:28:15 nonaka Exp $	*/
2/*	$OpenBSD: scoop_pcic.c,v 1.1 2005/07/01 23:51:55 uwe Exp $	*/
3
4/*
5 * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/cdefs.h>
21__KERNEL_RCSID(0, "$NetBSD: scoop_pcic.c,v 1.3 2009/01/29 12:28:15 nonaka Exp $");
22
23#include <sys/param.h>
24#include <sys/systm.h>
25#include <sys/device.h>
26
27#include <uvm/uvm.h>
28
29#include <arch/arm/xscale/pxa2x0var.h>
30#include <arch/arm/xscale/pxa2x0_gpio.h>
31#include <arch/arm/xscale/pxa2x0_pcic.h>
32
33#include <zaurus/zaurus/zaurus_reg.h>
34#include <zaurus/zaurus/zaurus_var.h>
35
36#include <zaurus/dev/scoopreg.h>
37
38static int	scoop_pcic_match(device_t, cfdata_t, void *);
39static void	scoop_pcic_attach(device_t, device_t, void *);
40
41CFATTACH_DECL_NEW(pxapcic_scoop, sizeof(struct pxapcic_softc),
42    scoop_pcic_match, scoop_pcic_attach, NULL, NULL);
43
44static void	scoop_pcic_socket_setup(struct pxapcic_socket *);
45static u_int	scoop_pcic_read(struct pxapcic_socket *, int);
46static void	scoop_pcic_write(struct pxapcic_socket *, int, u_int);
47static void	scoop_pcic_set_power(struct pxapcic_socket *, int);
48static void	scoop_pcic_clear_intr(struct pxapcic_socket *);
49static void 	*scoop_pcic_intr_establish(struct pxapcic_socket *, int,
50		    int (*)(void *), void *);
51static void	scoop_pcic_intr_disestablish(struct pxapcic_socket *, void *);
52
53static struct pxapcic_tag scoop_pcic_functions = {
54	scoop_pcic_read,
55	scoop_pcic_write,
56	scoop_pcic_set_power,
57	scoop_pcic_clear_intr,
58	scoop_pcic_intr_establish,
59	scoop_pcic_intr_disestablish
60};
61
62static int
63scoop_pcic_match(device_t parent, cfdata_t cf, void *aux)
64{
65
66	return 1;
67}
68
69static void
70scoop_pcic_attach(device_t parent, device_t self, void *aux)
71{
72	struct pxapcic_softc *sc = device_private(self);
73	struct pxaip_attach_args *pxa = (struct pxaip_attach_args *)aux;
74
75	sc->sc_dev = self;
76	sc->sc_iot = pxa->pxa_iot;
77
78	if (ZAURUS_ISC860) {
79		sc->sc_nslots = 1;
80		sc->sc_irqpin[0] = C860_CF0_IRQ;
81		sc->sc_irqcfpin[0] = C860_CF0_IRQ_PIN;
82	} else if (ZAURUS_ISC1000) {
83		sc->sc_nslots = 1;
84		sc->sc_irqpin[0] = C3000_CF0_IRQ;
85		sc->sc_irqcfpin[0] = C3000_CF0_IRQ_PIN;
86	} else if (ZAURUS_ISC3000) {
87		sc->sc_nslots = 2;
88		sc->sc_irqpin[0] = C3000_CF0_IRQ;
89		sc->sc_irqcfpin[0] = C3000_CF0_IRQ_PIN;
90		sc->sc_irqpin[1] = C3000_CF1_IRQ;
91		sc->sc_irqcfpin[1] = C3000_CF1_IRQ_PIN;
92	}
93	sc->sc_flags |= PPF_REVERSE_ORDER;
94
95	pxapcic_attach_common(sc, &scoop_pcic_socket_setup);
96}
97
98static void
99scoop_pcic_socket_setup(struct pxapcic_socket *so)
100{
101	struct pxapcic_softc *sc;
102	bus_addr_t pa;
103	bus_size_t size = SCOOP_SIZE;
104	bus_space_tag_t iot;
105	bus_space_handle_t scooph;
106	int error;
107
108	sc = so->sc;
109	iot = sc->sc_iot;
110
111	if (so->socket == 0) {
112		pa = C3000_SCOOP0_BASE;
113	} else if (so->socket == 1) {
114		pa = C3000_SCOOP1_BASE;
115	} else {
116		panic("%s: invalid CF slot %d", device_xname(sc->sc_dev),
117		    so->socket);
118	}
119
120	error = bus_space_map(iot, trunc_page(pa), round_page(size),
121	    0, &scooph);
122	if (error) {
123		panic("%s: failed to map memory %x for scoop",
124		    device_xname(sc->sc_dev), (uint32_t)pa);
125	}
126	scooph += pa - trunc_page(pa);
127
128	bus_space_write_2(iot, scooph, SCOOP_IMR,
129	    SCP_IMR_UNKN0 | SCP_IMR_UNKN1);
130
131	/* setup */
132	bus_space_write_2(iot, scooph, SCOOP_MCR, 0x0100);
133	bus_space_write_2(iot, scooph, SCOOP_CDR, 0x0000);
134	bus_space_write_2(iot, scooph, SCOOP_CPR, 0x0000);
135	bus_space_write_2(iot, scooph, SCOOP_IMR, 0x0000);
136	bus_space_write_2(iot, scooph, SCOOP_IRM, 0x00ff);
137	bus_space_write_2(iot, scooph, SCOOP_ISR, 0x0000);
138	bus_space_write_2(iot, scooph, SCOOP_IRM, 0x0000);
139
140	/* C3000 */
141	if (so->socket == 1) {
142		bus_space_write_2(iot, scooph, SCOOP_CPR, 0x80c1);
143		bus_space_write_2(iot, scooph, SCOOP_IMR, 0x00c4);
144		bus_space_write_2(iot, scooph, SCOOP_MCR, 0x0111);
145	} else {
146		bus_space_write_2(iot, scooph, SCOOP_CPR,
147		    SCP_CPR_PWR|SCP_CPR_5V);
148	}
149
150	bus_space_write_2(iot, scooph, SCOOP_IMR, 0x00ce);
151	bus_space_write_2(iot, scooph, SCOOP_MCR, 0x0111);
152
153	/* C3000 */
154	so->power_capability = PXAPCIC_POWER_3V;
155	if (so->socket == 0)
156		so->power_capability |= PXAPCIC_POWER_5V;
157
158	so->pcictag_cookie = (void *)scooph;
159	so->pcictag = &scoop_pcic_functions;
160}
161
162static u_int
163scoop_pcic_read(struct pxapcic_socket *so, int reg)
164{
165	bus_space_tag_t iot = so->sc->sc_iot;
166	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
167	uint16_t csr;
168
169	csr = bus_space_read_2(iot, ioh, SCOOP_CSR);
170
171	switch (reg) {
172	case PXAPCIC_CARD_STATUS:
173		if (csr & SCP_CSR_MISSING)
174			return (PXAPCIC_CARD_INVALID);
175		else
176			return (PXAPCIC_CARD_VALID);
177
178	case PXAPCIC_CARD_READY:
179		return ((bus_space_read_2(iot, ioh, SCOOP_CSR) &
180		    SCP_CSR_READY) != 0);
181
182	default:
183		panic("scoop_pcic_read: bogus register");
184	}
185	/*NOTREACHED*/
186}
187
188static void
189scoop_pcic_write(struct pxapcic_socket *so, int reg, u_int val)
190{
191	bus_space_tag_t iot = so->sc->sc_iot;
192	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
193	uint16_t newval;
194	int s;
195
196	s = splhigh();
197
198	switch (reg) {
199	case PXAPCIC_CARD_POWER:
200		newval = bus_space_read_2(iot, ioh, SCOOP_CPR);
201		newval &= ~(SCP_CPR_PWR | SCP_CPR_3V | SCP_CPR_5V);
202
203		if (val == PXAPCIC_POWER_3V)
204			newval |= (SCP_CPR_PWR | SCP_CPR_3V);
205		else if (val == PXAPCIC_POWER_5V)
206			newval |= (SCP_CPR_PWR | SCP_CPR_5V);
207
208		bus_space_write_2(iot, ioh, SCOOP_CPR, newval);
209		break;
210
211	case PXAPCIC_CARD_RESET:
212		bus_space_write_2(iot, ioh, SCOOP_CCR,
213		    val ? SCP_CCR_RESET : 0);
214		break;
215
216	default:
217		panic("scoop_pcic_write: bogus register");
218	}
219
220	splx(s);
221}
222
223static void
224scoop_pcic_set_power(struct pxapcic_socket *so, int pwr)
225{
226	bus_space_tag_t iot = so->sc->sc_iot;
227	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
228	u_int16_t reg;
229	int s;
230
231	s = splhigh();
232
233	switch (pwr) {
234	case PXAPCIC_POWER_OFF:
235#if 0
236		/* XXX does this disable power to both sockets? */
237		reg = bus_space_read_2(iot, ioh, SCOOP_GPWR);
238		bus_space_write_2(iot, ioh, SCOOP_GPWR,
239		    reg & ~(1 << SCOOP0_CF_POWER_C3000));
240#endif
241		break;
242
243	case PXAPCIC_POWER_3V:
244	case PXAPCIC_POWER_5V:
245		/* XXX */
246		if (so->socket == 0) {
247			reg = bus_space_read_2(iot, ioh, SCOOP_GPWR);
248			bus_space_write_2(iot, ioh, SCOOP_GPWR,
249			    reg | (1 << SCOOP0_CF_POWER_C3000));
250		}
251		break;
252
253	default:
254		splx(s);
255		panic("scoop_pcic_set_power: bogus power state");
256	}
257
258	splx(s);
259}
260
261static void
262scoop_pcic_clear_intr(struct pxapcic_socket *so)
263{
264	bus_space_tag_t iot = so->sc->sc_iot;
265	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
266
267	bus_space_write_2(iot, ioh, SCOOP_IRM, 0x00ff);
268	bus_space_write_2(iot, ioh, SCOOP_ISR, 0x0000);
269	bus_space_write_2(iot, ioh, SCOOP_IRM, 0x0000);
270}
271
272static void *
273scoop_pcic_intr_establish(struct pxapcic_socket *so, int ipl,
274    int (*func)(void *), void *arg)
275{
276
277	return (pxa2x0_gpio_intr_establish(so->irqpin, IST_EDGE_FALLING,
278	    ipl, func, arg));
279}
280
281static void
282scoop_pcic_intr_disestablish(struct pxapcic_socket *so, void *ih)
283{
284
285	pxa2x0_gpio_intr_disestablish(ih);
286}
287