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