xref: /netbsd-src/sys/arch/zaurus/dev/scoop.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: scoop.c,v 1.9 2011/07/19 15:11:49 dyoung Exp $	*/
2 /*	$OpenBSD: zaurus_scoop.c,v 1.12 2005/11/17 05:26:31 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.c,v 1.9 2011/07/19 15:11:49 dyoung Exp $");
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/conf.h>
27 #include <sys/gpio.h>
28 #include <sys/bus.h>
29 
30 #include <arm/xscale/pxa2x0var.h>
31 
32 #include <zaurus/zaurus/zaurus_reg.h>
33 #include <zaurus/zaurus/zaurus_var.h>
34 
35 #include <zaurus/dev/scoopreg.h>
36 #include <zaurus/dev/scoopvar.h>
37 
38 #include "ioconf.h"
39 
40 struct scoop_softc {
41 	device_t		sc_dev;
42 
43 	bus_space_tag_t		sc_iot;
44 	bus_space_handle_t	sc_ioh;
45 
46 	uint16_t		sc_gpwr;	/* GPIO state before suspend */
47 };
48 
49 static int	scoopmatch(device_t, cfdata_t, void *);
50 static void	scoopattach(device_t, device_t, void *);
51 
52 CFATTACH_DECL_NEW(scoop, sizeof(struct scoop_softc),
53     scoopmatch, scoopattach, NULL, NULL);
54 
55 #if 0
56 static int	scoop_gpio_pin_read(struct scoop_softc *, int);
57 #endif
58 static void	scoop_gpio_pin_write(struct scoop_softc *, int, int);
59 static void	scoop_gpio_pin_ctl(struct scoop_softc *, int, int);
60 
61 enum scoop_card {
62 	SD_CARD,
63 	CF_CARD		/* socket 0 (external) */
64 };
65 
66 static void	scoop0_set_card_power(enum scoop_card card, int new_cpr);
67 
68 static int
69 scoopmatch(device_t parent, cfdata_t cf, void *aux)
70 {
71 
72 	/*
73 	 * Only C3000-like models are known to have two SCOOPs.
74 	 */
75         if (ZAURUS_ISC3000)
76 		return (cf->cf_unit < 2);
77 	return (cf->cf_unit == 0);
78 }
79 
80 static void
81 scoopattach(device_t parent, device_t self, void *aux)
82 {
83 	struct scoop_softc *sc = device_private(self);
84 	struct pxaip_attach_args *pxa = (struct pxaip_attach_args *)aux;
85 	bus_addr_t addr;
86 	bus_size_t size;
87 
88 	sc->sc_dev = self;
89 	sc->sc_iot = pxa->pxa_iot;
90 
91 	aprint_normal(": PCMCIA/GPIO controller\n");
92 	aprint_naive("\n");
93 
94 	if (pxa->pxa_addr != -1)
95 		addr = pxa->pxa_addr;
96 	else if (sc->sc_dev->dv_unit == 0)
97 		addr = C3000_SCOOP0_BASE;
98 	else
99 		addr = C3000_SCOOP1_BASE;
100 
101 	size = pxa->pxa_size < SCOOP_SIZE ? SCOOP_SIZE : pxa->pxa_size;
102 
103 	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
104 		aprint_error_dev(sc->sc_dev, "couldn't map registers\n");
105 		return;
106 	}
107 
108 	if (ZAURUS_ISC3000 && sc->sc_dev->dv_unit == 1) {
109 		scoop_gpio_pin_ctl(sc, SCOOP1_AKIN_PULLUP, GPIO_PIN_OUTPUT);
110 		scoop_gpio_pin_write(sc, SCOOP1_AKIN_PULLUP, GPIO_PIN_LOW);
111 	} else if (ZAURUS_ISC860) {
112 		scoop_gpio_pin_ctl(sc, SCOOP0_AKIN_PULLUP, GPIO_PIN_OUTPUT);
113 		scoop_gpio_pin_write(sc, SCOOP0_AKIN_PULLUP, GPIO_PIN_LOW);
114 	}
115 }
116 
117 #if 0
118 static int
119 scoop_gpio_pin_read(struct scoop_softc *sc, int pin)
120 {
121 	uint16_t bit = (1 << pin);
122 	uint16_t rv;
123 
124 	rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR);
125 	return (rv & bit) ? 1 : 0;
126 }
127 #endif
128 
129 static void
130 scoop_gpio_pin_write(struct scoop_softc *sc, int pin, int level)
131 {
132 	uint16_t bit = (1 << pin);
133 	uint16_t rv;
134 
135 	rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR);
136 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
137 	    (level == GPIO_PIN_LOW) ? (rv & ~bit) : (rv | bit));
138 }
139 
140 static void
141 scoop_gpio_pin_ctl(struct scoop_softc *sc, int pin, int flags)
142 {
143 	uint16_t bit = (1 << pin);
144 	uint16_t rv;
145 
146 	rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPCR);
147 	switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
148 	case GPIO_PIN_INPUT:
149 		rv &= ~bit;
150 		break;
151 	case GPIO_PIN_OUTPUT:
152 		rv |= bit;
153 		break;
154 	}
155 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPCR, rv);
156 }
157 
158 /*
159  * Turn the LCD background light and contrast signal on or off.
160  */
161 void
162 scoop_set_backlight(int on, int cont)
163 {
164 	struct scoop_softc *sc;
165 #if 0
166 	struct scoop_softc *sc0;
167 
168 	sc0 = device_lookup_private(&scoop_cd, 0);
169 #endif
170 
171 	sc = device_lookup_private(&scoop_cd, 1);
172 	if (sc != NULL) {
173 		/* C3000 */
174 		scoop_gpio_pin_write(sc, SCOOP1_BACKLIGHT_CONT, !cont);
175 		scoop_gpio_pin_write(sc, SCOOP1_BACKLIGHT_ON, on);
176 	}
177 #if 0
178 	else if (sc0 != NULL) {
179 		scoop_gpio_pin_write(sc0,
180 		    SCOOP0_BACKLIGHT_CONT, cont);
181 	}
182 #endif
183 }
184 
185 /*
186  * Turn the infrared LED on or off (must be on while transmitting).
187  */
188 void
189 scoop_set_irled(int on)
190 {
191 	struct scoop_softc *sc;
192 
193 	sc = device_lookup_private(&scoop_cd, 1);
194 	if (sc != NULL) {
195 		/* IR_ON is inverted */
196 		scoop_gpio_pin_write(sc, SCOOP1_IR_ON, !on);
197 	}
198 }
199 
200 /*
201  * Turn the green and orange LEDs on or off.  If the orange LED is on,
202  * then it is wired to indicate if A/C is connected.  The green LED has
203  * no such predefined function.
204  */
205 void
206 scoop_led_set(int led, int on)
207 {
208 	struct scoop_softc *sc;
209 
210 	sc = device_lookup_private(&scoop_cd, 0);
211 	if (sc != NULL) {
212 		if ((led & SCOOP_LED_GREEN) != 0) {
213 			scoop_gpio_pin_write(sc, SCOOP0_LED_GREEN, on);
214 		}
215 		if (scoop_cd.cd_ndevs > 1 && (led & SCOOP_LED_ORANGE) != 0) {
216 			scoop_gpio_pin_write(sc, SCOOP0_LED_ORANGE_C3000, on);
217 		}
218 	}
219 }
220 
221 /*
222  * Enable or disable the headphone output connection.
223  */
224 void
225 scoop_set_headphone(int on)
226 {
227 	struct scoop_softc *sc;
228 
229 	sc = device_lookup_private(&scoop_cd, 0);
230 	if (sc == NULL)
231 		return;
232 
233 	scoop_gpio_pin_ctl(sc, SCOOP0_MUTE_L, GPIO_PIN_OUTPUT);
234 	scoop_gpio_pin_ctl(sc, SCOOP0_MUTE_R, GPIO_PIN_OUTPUT);
235 
236 	if (on) {
237 		scoop_gpio_pin_write(sc, SCOOP0_MUTE_L, GPIO_PIN_HIGH);
238 		scoop_gpio_pin_write(sc, SCOOP0_MUTE_R, GPIO_PIN_HIGH);
239 	} else {
240 		scoop_gpio_pin_write(sc, SCOOP0_MUTE_L, GPIO_PIN_LOW);
241 		scoop_gpio_pin_write(sc, SCOOP0_MUTE_R, GPIO_PIN_LOW);
242 	}
243 }
244 
245 /*
246  * Enable or disable the mic bias
247  */
248 void
249 scoop_set_mic_bias(int onoff)
250 {
251 	struct scoop_softc *sc1;
252 
253 	sc1 = device_lookup_private(&scoop_cd, 1);
254 	if (sc1 != NULL)
255 		scoop_gpio_pin_write(sc1, SCOOP1_MIC_BIAS, onoff);
256 }
257 
258 /*
259  * Turn on pullup resistor while not reading the remote control.
260  */
261 void
262 scoop_akin_pullup(int enable)
263 {
264 	struct scoop_softc *sc0;
265 	struct scoop_softc *sc1;
266 
267 	sc0 = device_lookup_private(&scoop_cd, 0);
268 	sc1 = device_lookup_private(&scoop_cd, 1);
269 
270 	if (sc1 != NULL) {
271 		scoop_gpio_pin_write(sc1, SCOOP1_AKIN_PULLUP, enable);
272 	} else if (sc0 != NULL) {
273 		scoop_gpio_pin_write(sc0, SCOOP0_AKIN_PULLUP, enable);
274 	}
275 }
276 
277 void
278 scoop_battery_temp_adc(int enable)
279 {
280 	struct scoop_softc *sc;
281 
282 	sc = device_lookup_private(&scoop_cd, 0);
283 
284 	if (sc != NULL) {
285 		scoop_gpio_pin_write(sc, SCOOP0_ADC_TEMP_ON_C3000, enable);
286 	}
287 }
288 
289 void
290 scoop_charge_battery(int enable, int voltage_high)
291 {
292 	struct scoop_softc *sc;
293 
294 	sc = device_lookup_private(&scoop_cd, 0);
295 
296 	if (sc != NULL) {
297 		scoop_gpio_pin_write(sc, SCOOP0_JK_B_C3000, voltage_high);
298 		scoop_gpio_pin_write(sc, SCOOP0_CHARGE_OFF_C3000, !enable);
299 	}
300 }
301 
302 void
303 scoop_discharge_battery(int enable)
304 {
305 	struct scoop_softc *sc;
306 
307 	sc = device_lookup_private(&scoop_cd, 0);
308 
309 	if (sc != NULL) {
310 		scoop_gpio_pin_write(sc, SCOOP0_JK_A_C3000, enable);
311 	}
312 }
313 
314 /*
315  * Enable or disable 3.3V power to the SD/MMC card slot.
316  */
317 void
318 scoop_set_sdmmc_power(int on)
319 {
320 
321 	scoop0_set_card_power(SD_CARD, on ? SCP_CPR_SD_3V : SCP_CPR_OFF);
322 }
323 
324 /*
325  * The Card Power Register of the first SCOOP unit controls the power
326  * for the first CompactFlash slot and the SD/MMC card slot as well.
327  */
328 void
329 scoop0_set_card_power(enum scoop_card card, int new_cpr)
330 {
331 	struct scoop_softc *sc;
332 	bus_space_tag_t iot;
333 	bus_space_handle_t ioh;
334 	uint16_t cpr;
335 
336 	sc = device_lookup_private(&scoop_cd, 0);
337 	if (sc == NULL)
338 		return;
339 
340 	iot = sc->sc_iot;
341 	ioh = sc->sc_ioh;
342 
343 	cpr = bus_space_read_2(iot, ioh, SCOOP_CPR);
344 	if (new_cpr & SCP_CPR_VOLTAGE_MSK) {
345 		if (card == CF_CARD)
346 			cpr |= SCP_CPR_5V;
347 		else if (card == SD_CARD)
348 			cpr |= SCP_CPR_SD_3V;
349 
350 		scoop_gpio_pin_write(sc, SCOOP0_CF_POWER_C3000, 1);
351 		if (!ISSET(cpr, SCP_CPR_5V) && !ISSET(cpr, SCP_CPR_SD_3V))
352 			delay(5000);
353 		bus_space_write_2(iot, ioh, SCOOP_CPR, cpr | new_cpr);
354 	} else {
355 		if (card == CF_CARD)
356 			cpr &= ~SCP_CPR_5V;
357 		else if (card == SD_CARD)
358 			cpr &= ~SCP_CPR_SD_3V;
359 
360 		if (!ISSET(cpr, SCP_CPR_5V) && !ISSET(cpr, SCP_CPR_SD_3V)) {
361 			bus_space_write_2(iot, ioh, SCOOP_CPR, SCP_CPR_OFF);
362 			delay(1000);
363 			scoop_gpio_pin_write(sc, SCOOP0_CF_POWER_C3000, 0);
364 		} else
365 			bus_space_write_2(iot, ioh, SCOOP_CPR, cpr | new_cpr);
366 	}
367 }
368 
369 void
370 scoop_check_mcr(void)
371 {
372 	struct scoop_softc *sc0, *sc1, *sc;
373 	uint16_t v;
374 
375 	sc0 = device_lookup_private(&scoop_cd, 0);
376 	sc1 = device_lookup_private(&scoop_cd, 1);
377 
378 	/* C3000 */
379 	if (sc1 != NULL) {
380 		sc = sc0;
381 		v = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR);
382 		if ((v & 0x100) == 0) {
383 			bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR,
384 			    0x0101);
385 		}
386 
387 		sc = sc1;
388 		v = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR);
389 		if ((v & 0x100) == 0) {
390 			bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR,
391 			    0x0101);
392 		}
393 	}
394 }
395 
396 void
397 scoop_suspend(void)
398 {
399 	struct scoop_softc *sc, *sc0, *sc1;
400 	uint32_t rv;
401 
402 	sc0 = device_lookup_private(&scoop_cd, 0);
403 	sc1 = device_lookup_private(&scoop_cd, 1);
404 
405 	if (sc0 != NULL) {
406 		sc = sc0;
407 		sc->sc_gpwr = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
408 		    SCOOP_GPWR);
409 		/* C3000 */
410 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
411 		    sc->sc_gpwr & ~((1<<SCOOP0_MUTE_L) | (1<<SCOOP0_MUTE_R) |
412 		    (1<<SCOOP0_JK_A_C3000) | (1<<SCOOP0_ADC_TEMP_ON_C3000) |
413 		    (1<<SCOOP0_LED_GREEN)));
414 	}
415 
416 	/* C3000 */
417 	if (sc1 != NULL) {
418 		sc = sc1;
419 		sc->sc_gpwr = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
420 		    SCOOP_GPWR);
421 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
422 		    sc->sc_gpwr & ~((1<<SCOOP1_RESERVED_4) |
423 		    (1<<SCOOP1_RESERVED_5) | (1<<SCOOP1_RESERVED_6) |
424 		    (1<<SCOOP1_BACKLIGHT_CONT) | (1<<SCOOP1_BACKLIGHT_ON) |
425 		    (1<<SCOOP1_MIC_BIAS)));
426 		rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR);
427 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
428 		    rv | ((1<<SCOOP1_IR_ON) | (1<<SCOOP1_RESERVED_3)));
429 	}
430 }
431 
432 void
433 scoop_resume(void)
434 {
435 	struct scoop_softc *sc, *sc0, *sc1;
436 
437 	sc0 = device_lookup_private(&scoop_cd, 0);
438 	sc1 = device_lookup_private(&scoop_cd, 1);
439 
440 	if (sc0 != NULL) {
441 		sc = sc0;
442 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
443 		    sc->sc_gpwr);
444 	}
445 
446 	if (sc1 != NULL) {
447 		sc = sc1;
448 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
449 		    sc->sc_gpwr);
450 	}
451 }
452