code:
#	$OpenBSD: Makefile.zaurus,v 1.54 2015/01/13 01:12:50 deraadt Exp $

# For instructions on building kernels consult the config(8) and options(4)
# manual pages.
#
# N.B.: NO DEPENDENCIES ON FOLLOWING FLAGS ARE VISIBLE TO MAKEFILE
#	IF YOU CHANGE THE DEFINITION OF ANY OF THESE RECOMPILE EVERYTHING
# DEBUG is set to -g by config if debugging is requested (config -g).
# PROF is set to -pg by config if profiling is requested (config -p).

.include <bsd.own.mk>

SIZE?=	size
STRIP?=	strip

# source tree is located via $S relative to the compilation directory
.ifndef S
S!=	cd ../../../..; pwd
.endif

_machdir?=	$S/arch/${_mach}
_archdir?=	$S/arch/${_arch}

INCLUDES=	-nostdinc -I$S -I. -I$S/arch
CPPFLAGS=	${INCLUDES} ${IDENT} ${PARAM} -D_KERNEL -D__${_mach}__ -MD -MP
CWARNFLAGS=	-Werror -Wall -Wimplicit-function-declaration \
		-Wno-main -Wno-uninitialized \
		-Wframe-larger-than=2047

CMACHFLAGS=	-ffreestanding -msoft-float # -march=armv4 -mtune=strongarm
CMACHFLAGS+=	-fno-builtin-printf -fno-builtin-snprintf \
		-fno-builtin-vsnprintf -fno-builtin-log \
		-fno-builtin-log2 -fno-builtin-malloc ${NOPIE_FLAGS}
.if ${IDENT:M-DNO_PROPOLICE}
CMACHFLAGS+=	-fno-stack-protector
.endif

COPTS?=		-O2
CFLAGS=		${DEBUG} ${CWARNFLAGS} ${CMACHFLAGS} ${COPTS} ${PIPE}
AFLAGS=		-D_LOCORE -x assembler-with-cpp ${CWARNFLAGS} ${CMACHFLAGS}
LINKFLAGS=	-T ldscript --warn-common -nopie

.if ${IDENT:M-DDDB_STRUCT}
DB_STRUCTINFO=	db_structinfo.h
.else
DB_STRUCTINFO=
.endif

HOSTCC?=	${CC}
HOSTED_CPPFLAGS=${CPPFLAGS:S/^-nostdinc$//}
HOSTED_CFLAGS=	${CFLAGS}
HOSTED_C=	${HOSTCC} ${HOSTED_CFLAGS} ${HOSTED_CPPFLAGS} -c $<

NORMAL_C_NOP=	${CC} ${CFLAGS} ${CPPFLAGS} -c $<
NORMAL_C=	${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
NORMAL_S=	${CC} ${AFLAGS} ${CPPFLAGS} -c $<

%OBJS

%CFILES

%SFILES

# load lines for config "xxx" will be emitted as:
# xxx: ${SYSTEM_DEP} swapxxx.o
#	${SYSTEM_LD_HEAD}
#	${SYSTEM_LD} swapxxx.o
#	${SYSTEM_LD_TAIL}
SYSTEM_HEAD=	${_mach}_start.o locore.o param.o ioconf.o
SYSTEM_OBJ=	${SYSTEM_HEAD} ${OBJS}
SYSTEM_DEP=	Makefile ${SYSTEM_OBJ}
SYSTEM_LD_HEAD=	@rm -f $@
SYSTEM_LD_HEAD+=; \
		cat ${_archdir}/conf/ldscript.head ${_archdir}/conf/ldscript.tail | \
		    sed -e 's/@KERNEL_BASE_PHYS@/${KERNEL_BASE_PHYS}/' \
		    -e 's/@KERNEL_BASE_VIRT@/${KERNEL_BASE_VIRT}/' > ldscript

SYSTEM_LD=	@echo ${LD} ${LINKFLAGS} -o $@ '$${SYSTEM_HEAD} vers.o $${OBJS}'; \
		${LD} ${LINKFLAGS} -o $@ ${SYSTEM_HEAD} vers.o ${OBJS}
SYSTEM_LD_TAIL=	@${SIZE} $@; chmod 755 $@

DEBUG?=
.if ${DEBUG} == "-g"
LINKFLAGS+=	-X
STRIPFLAGS=	-g -x
SYSTEM_LD_TAIL+=; \
		echo mv $@ $@.gdb; rm -f $@.gdb; mv $@ $@.gdb; \
		echo ${STRIP} ${STRIPFLAGS} -o $@ $@.gdb; \
		${STRIP} ${STRIPFLAGS} -o $@ $@.gdb
.else
LINKFLAGS+=	-x
.endif

%LOAD

# cc's -MD puts the source and output paths in the dependency file;
# since those are temp files here we need to fix it up.  It also
# puts the file in /tmp, so we use -MF to put it in the current
# directory as assym.P and then generate assym.d from it with a
# good target name
assym.h: $S/kern/genassym.sh Makefile \
	 ${_archdir}/${_arch}/genassym.cf ${_machdir}/${_mach}/genassym.cf
	cat ${_archdir}/${_arch}/genassym.cf ${_machdir}/${_mach}/genassym.cf | \
	    sh $S/kern/genassym.sh ${CC} ${CFLAGS} ${CPPFLAGS} -MF assym.P > assym.h.tmp
	sed '1s/.*/assym.h: \\/' assym.P > assym.d
	sort -u assym.h.tmp > assym.h

param.c: $S/conf/param.c
	rm -f param.c
	cp $S/conf/param.c .

param.o: param.c Makefile
	${NORMAL_C}

mcount.o: $S/lib/libkern/mcount.c Makefile
	${NORMAL_C_NOP}

ioconf.o: ioconf.c
	${NORMAL_C}

vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP}
	sh $S/conf/newvers.sh
	${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c vers.c

clean::
	rm -f eddep *bsd *bsd.gdb tags *.[dio] [a-z]*.s \
	    [Ee]rrs linterrs assym.h ${DB_STRUCTINFO}

lint:
	@lint -hbxncez -Dvolatile= ${CPPFLAGS} -UKGDB \
	    ${CFILES} ioconf.c param.c | \
	    grep -v 'static function .* unused'

depend:
	@touch $@

tags:
	@echo "see $S/kern/Makefile for tags"

db_structinfo.h: $S/ddb/db_structinfo.c $S/ddb/parse_structinfo.pl
	${CC} ${CFLAGS} ${CPPFLAGS} -MT $@ -gstabs -c $S/ddb/db_structinfo.c
	objdump -g db_structinfo.o | perl $S/ddb/parse_structinfo.pl > $@
	rm -f db_structinfo.o

${_mach}_start.o: ${_machdir}/${_mach}/${_mach}_start.S
locore.o: ${_archdir}/${_arch}/locore.S assym.h
in_cksum_arm.o fiq_subr.o bcopyinout.o copystr.o sigcode.o: assym.h
vectors.o cpuswitch.o exception.o bcopy_page.o irq_dispatch.o: assym.h
zaurus_start.o: assym.h

# The install target can be redefined by putting a
# install-kernel-${MACHINE_NAME} target into /etc/mk.conf
MACHINE_NAME!=  uname -n
install: install-kernel-${MACHINE_NAME}
.if !target(install-kernel-${MACHINE_NAME}})
install-kernel-${MACHINE_NAME}:
	cmp -s bsd /bsd || ln -f /bsd /obsd
	cp bsd /nbsd
	mv /nbsd /bsd
.endif

# pull in the dependency information
.if !empty(DB_STRUCTINFO) && !exists(${DB_STRUCTINFO})
 ${SYSTEM_OBJ}: ${DB_STRUCTINFO}
.endif
.ifnmake clean
. for o in ${SYSTEM_OBJ} assym.h ${DB_STRUCTINFO}
.  if exists(${o:R}.d)
.   include "${o:R}.d"
.  elif exists($o)
    .PHONY: $o
.  endif
. endfor
.endif

%RULES


code:
#	$OpenBSD: RAMDISK,v 1.53 2015/07/17 22:52:29 tedu Exp $

machine		zaurus arm
maxusers	4

option		TIMEZONE=0
option		DST=0
option		SMALL_KERNEL
option		NO_PROPOLICE
option		BOOT_CONFIG

option		MINIROOTSIZE=5120
option		RAMDISK_HOOKS

option		FFS
option		CD9660
option		MSDOSFS
option		INET6
option		EXT2FS
option		CRYPTO

option		ARM32
option		PXAGPIO_HAS_GPION_INTRS

makeoptions	KERNEL_BASE_PHYS="0xa0200000"
makeoptions	KERNEL_BASE_VIRT="0xc0200000"

option		CPU_XSCALE_PXA2X0
#option		XSCALE_CACHE_WRITE_THROUGH
option		XSCALE_CACHE_WRITE_BACK
makeoptions	CPUFLAGS="-mcpu=xscale"

config		bsd root on rd0a swap on rd0b

mainbus0	at root
cpu0		at mainbus?
pxaip0		at mainbus?
pxaintc0	at pxaip?		# interrupt controller
pxagpio0	at pxaip?
scoop0		at pxaip?
scoop1		at pxaip?
pxaost0		at pxaip? addr 0x40a00000 size 0x20

lcd0		at pxaip?
wsdisplay*	at lcd? console ?
zssp0		at pxaip?		# ADC, touch screen, backlight
zts0		at pxaip?		# touch screen
wsmouse*	at zts? mux 0
zkbd0		at pxaip?
wskbd*		at zkbd? mux 1
pxaudc0		at pxaip?		# USB Device Controller

ohci0		at pxaip?
usbf*		at pxaudc?		# USB logical device
cdcef*		at usbf?		# CDC ethernet function
usb*		at ohci?

uhub*		at usb?
uhub*		at uhub?
uhidev*		at uhub?
uhid*		at uhidev?		# USB Generic HID devices
ukbd*		at uhidev?		# USB HID Keyboard devices
wskbd*		at ukbd? mux 1
aue*		at uhub?		# ADMtek AN986 Pegasus Ethernet
axe*		at uhub?		# ASIX Electronics AX88172 USB Ethernet
axen*		at uhub?		# ASIX Electronics AX88179 USB Ethernet
cdce*		at uhub?		# CDC Ethernet
cue*		at uhub?		# CATC USB-EL1201A based Ethernet
kue*		at uhub?		# Kawasaki KL5KUSB101B based Ethernet
smsc*		at uhub?		# SMSC LAN95xx Ethernet
udav*		at uhub?		# Davicom DM9601 based Ethernet
ural*		at uhub?		# Ralink RT2500
url*		at uhub?		# Realtek RTL8150L based adapters
wi*		at uhub?		# WaveLAN IEEE 802.11DS
upl*		at uhub?		# Prolific PL2301/PL2302 host-to-host
ugl*		at uhub?		# Genesys Logic GL620USB-A host-to-host
umass*		at uhub?

pxammc0		at pxaip?		# MMC/SD/SDIO controller
sdmmc*		at pxammc?		# SD/MMC bus
softraid0	at root

scsibus*	at scsi?
sd*		at scsibus?
cd*		at scsibus?

option		COM_PXA2X0
com0		at pxaip? addr 0x40100000 intr 22	# Full Function UART
com1		at pxaip? addr 0x40200000 intr 21	# BlueTooth UART
com2		at pxaip? addr 0x40700000 intr 20	# Standard UART (for IrDA)

pxapcic0	at pxaip?
pcmcia*		at pxapcic?
wi*		at pcmcia?
ne*		at pcmcia?
wdc*		at pcmcia?
wd*		at wdc? flags 0x0000

exphy*		at mii?			# 3Com internal PHYs
inphy*		at mii?			# Intel 82555 PHYs
iophy*		at mii?			# Intel 82553 PHYs
icsphy*		at mii?			# ICS 1890 PHYs
lxtphy*		at mii?			# Level1 LXT970 PHYs
nsphy*		at mii?			# NS and compatible PHYs
nsphyter*	at mii?			# NS and compatible PHYs
qsphy*		at mii?			# Quality Semi QS6612 PHYs
sqphy*		at mii?			# Seeq 8x220 PHYs
rlphy*		at mii?			# Realtek 8139 internal PHYs
mtdphy*		at mii?			# Myson MTD972 PHYs
acphy*		at mii?			# Altima AC101 PHYs
amphy*		at mii?			# AMD 79C873 PHYs
tqphy*		at mii?			# TDK 78Q212x PHYs
bmtphy*		at mii?			# Broadcom 10/100 PHYs
eephy*		at mii?			# Marvell 88E1000 series PHY
nsgphy*		at mii?			# NS gigabit PHYs
urlphy*		at mii?			# Realtek RTL8150L internal PHY
rgephy*		at mii?			# Realtek 8169S/8110S PHY
ukphy*		at mii?			# "unknown" PHYs

apm0		at pxaip?		# APM emulation

pseudo-device	wsmux 2
pseudo-device	loop 1
pseudo-device	bpfilter 1
pseudo-device	rd 1
pseudo-device	bio 1

path: sys/arch/zaurus/conf/RAMDISK

license: unknown


code:
#	$OpenBSD: files.zaurus,v 1.31 2015/05/11 06:46:21 ratchov Exp $
#
# First try for arm-specific configuration info
#

maxpartitions	16
maxusers	2 8 64

major	{wd = 16}
major	{sd = 24}
major	{cd = 26}
major	{rd = 18}

file	arch/arm/arm/conf.c

file	arch/zaurus/zaurus/zaurus_machdep.c

#
# Machine-independent SCSI drivers
#
include "scsi/files.scsi"
include "dev/atapiscsi/files.atapiscsi"

# CPU support and integrated peripherals
include "arch/arm/xscale/files.pxa2x0"

# LCD frame buffer
attach lcd at pxaip with lcd_pxaip
file arch/zaurus/zaurus/zaurus_lcd.c		lcd_pxaip

# GPIO keyboard
device zkbd: wskbddev
attach zkbd at pxaip
file arch/zaurus/dev/zaurus_kbd.c		zkbd

# 16-bit GPIO controller
device scoop
attach scoop at pxaip
file arch/zaurus/dev/zaurus_scoop.c		scoop

# PCMCIA/CF socket controller
attach pxapcic at pxaip with pxapcic_scoop
file arch/zaurus/dev/scoop_pcic.c		pxapcic_scoop

# SD/MMC socket controller
attach pxammc at pxaip with pxammc_scoop
file arch/zaurus/dev/scoop_mmc.c		pxammc_scoop

# Dedicated SSP unit for ADC, touch screen, and backlight
device zssp
attach zssp at pxaip
file arch/zaurus/dev/zaurus_ssp.c		zssp

# Power manager and APM emulation
attach apm at pxaip with apm_pxaip
file arch/zaurus/dev/zaurus_apm.c		apm_pxaip

# Zaurus touchpad
device zts: wsmousedev
attach zts at pxaip
file arch/zaurus/dev/zts.c			zts

# Zaurus sound
device	zaudio: audio, i2s, i2c
attach	zaudio at pxaip
file	arch/zaurus/dev/zaurus_audio.c		zaudio

# Zaurus remote control
device	zrc: wskbddev
attach	zrc at pxaip
file	arch/zaurus/dev/zaurus_remote.c		zrc

# NAND flash pseudo-disk device (Xilinx NAND flash controller)
device	flash: disk
attach	flash at pxaip with flash_pxaip
file	arch/zaurus/dev/zaurus_flash.c		flash_pxaip

#
# Machine-independent ATA drivers
#
include "dev/ata/files.ata"

# Generic MD files
file	arch/zaurus/zaurus/autoconf.c

file	arch/arm/arm/disksubr.c			disk

# ARM FPE
file	arch/arm/fpe-arm/armfpe_glue.S		armfpe
file	arch/arm/fpe-arm/armfpe_init.c		armfpe
file	arch/arm/fpe-arm/armfpe.s		armfpe

# Include WSCONS stuff
include "dev/wscons/files.wscons"
include "dev/rasops/files.rasops"
include "dev/wsfont/files.wsfont"

# Include USB stuff
include "dev/usb/files.usb"

attach pxaudc at pxaip with pxaudc_zaurus
file arch/zaurus/dev/zaurus_udc.c		pxaudc_zaurus

# Media Independent Interface (mii)
include "dev/mii/files.mii"

#
# Machine-independent SD/MMC drivers
#
include "dev/sdmmc/files.sdmmc"

#
# Machine-independent 1-Wire drivers
#
include "dev/onewire/files.onewire"


code:
/scoop_mmc.c/1.2/Thu Sep  3 21:40:29 2009//TOPENBSD_5_8_BASE
/scoop_pcic.c/1.5/Thu May  8 21:17:01 2014//TOPENBSD_5_8_BASE
/zaurus_apm.c/1.33/Wed Feb 11 07:05:39 2015//TOPENBSD_5_8_BASE
/zaurus_apm.h/1.1/Mon Apr 11 03:21:03 2005//TOPENBSD_5_8_BASE
/zaurus_audio.c/1.19/Mon May 11 06:46:21 2015//TOPENBSD_5_8_BASE
/zaurus_flash.c/1.13/Sat Jul 12 18:44:43 2014//TOPENBSD_5_8_BASE
/zaurus_kbd.c/1.36/Fri Jul 11 08:18:31 2014//TOPENBSD_5_8_BASE
/zaurus_kbdmap.h/1.19/Tue May 10 08:26:12 2005//TOPENBSD_5_8_BASE
/zaurus_remote.c/1.2/Sun Jan 26 17:48:08 2014//TOPENBSD_5_8_BASE
/zaurus_scoop.c/1.21/Tue Sep  7 16:21:41 2010//TOPENBSD_5_8_BASE
/zaurus_scoopreg.h/1.8/Sun Mar 18 20:50:23 2007//TOPENBSD_5_8_BASE
/zaurus_scoopvar.h/1.11/Sun Mar 18 20:50:23 2007//TOPENBSD_5_8_BASE
/zaurus_ssp.c/1.8/Tue Sep  7 16:21:41 2010//TOPENBSD_5_8_BASE
/zaurus_sspvar.h/1.3/Fri Apr  8 21:58:49 2005//TOPENBSD_5_8_BASE
/zaurus_udc.c/1.3/Thu Oct 24 22:40:10 2013//TOPENBSD_5_8_BASE
/zts.c/1.16/Sat Mar 29 18:09:30 2014//TOPENBSD_5_8_BASE
D

path: sys/arch/zaurus/dev/CVS/Entries


code: src/sys/arch/zaurus/dev

path: sys/arch/zaurus/dev/CVS/Repository


code:
/*	$OpenBSD: scoop_mmc.c,v 1.2 2009/09/03 21:40:29 marex Exp $	*/

/*
 * Copyright (c) 2007 Uwe Stuehler <uwe@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* Attachment driver for pxammc(4) on Zaurus */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <dev/sdmmc/sdmmcreg.h>

#include <machine/machine_reg.h>

#include <arch/arm/xscale/pxammcvar.h>
#include <arch/zaurus/dev/zaurus_scoopvar.h>

int	scoop_mmc_match(struct device *, void *, void *);
void	scoop_mmc_attach(struct device *, struct device *, void *);

struct cfattach pxammc_scoop_ca = {
	sizeof(struct pxammc_softc), scoop_mmc_match,
	scoop_mmc_attach
};

u_int32_t scoop_mmc_get_ocr(void *);
int	  scoop_mmc_set_power(void *, u_int32_t);

int
scoop_mmc_match(struct device *parent, void *match, void *aux)
{
	return pxammc_match();
}

void
scoop_mmc_attach(struct device *parent, struct device *self, void *aux)
{
	struct pxammc_softc *sc = (struct pxammc_softc *)self;

	sc->tag.cookie = (void *)sc;
	sc->tag.get_ocr = scoop_mmc_get_ocr;
	sc->tag.set_power = scoop_mmc_set_power;

	sc->sc_gpio_detect = GPIO_MMC_DETECT;

	pxammc_attach(sc, aux);
}

u_int32_t
scoop_mmc_get_ocr(void *cookie)
{
	return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V;
}

int
scoop_mmc_set_power(void *cookie, u_int32_t ocr)
{
	if (ISSET(ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V)) {
		scoop_set_sdmmc_power(1);
		return 0;
	} else if (ocr != 0) {
		printf("scoop_mmc_set_power: unsupported OCR (%#x)\n", ocr);
		return EINVAL;
	} else {
		scoop_set_sdmmc_power(0);
		return 0;
	}
}


code:
/*	$OpenBSD: scoop_pcic.c,v 1.5 2014/05/08 21:17:01 miod Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <arch/arm/xscale/pxa2x0var.h>
#include <arch/arm/xscale/pxapcicvar.h>

#include <machine/machine_reg.h>
#include <machine/zaurus_var.h>

#include <zaurus/dev/zaurus_scoopreg.h>

int	scoop_pcic_match(struct device *, void *, void *);
void	scoop_pcic_attach(struct device *, struct device *, void *);
void	scoop_pcic_socket_setup(struct pxapcic_socket *);

struct cfattach pxapcic_scoop_ca = {
	sizeof(struct pxapcic_softc), scoop_pcic_match,
	scoop_pcic_attach
};

u_int	scoop_pcic_read(struct pxapcic_socket *, int);
void	scoop_pcic_write(struct pxapcic_socket *, int, u_int);
void	scoop_pcic_set_power(struct pxapcic_socket *, int);
void	scoop_pcic_clear_intr(struct pxapcic_socket *);

struct pxapcic_tag scoop_pcic_functions = {
	scoop_pcic_read,
	scoop_pcic_write,
	scoop_pcic_set_power,
	scoop_pcic_clear_intr,
	0,			/* intr_establish */
	0,			/* intr_disestablish */
	0			/* intr_string */
};

int
scoop_pcic_match(struct device *parent, void *cf, void *aux)
{
	return (ZAURUS_ISC860 || ZAURUS_ISC3000);
}

void
scoop_pcic_attach(struct device *parent, struct device *self, void *aux)
{
	struct pxapcic_softc *sc = (struct pxapcic_softc *)self;
	struct pxaip_attach_args *pxa = aux;

	sc->sc_iot = pxa->pxa_iot;

	if (ZAURUS_ISC860) {
		sc->sc_nslots = 1;
		sc->sc_irqpin[0] = C860_CF0_IRQ;
		sc->sc_irqcfpin[0] = C860_CF0_IRQ_PIN;
	} else if (ZAURUS_ISC3000) {
		sc->sc_nslots = 2;
		sc->sc_irqpin[0] = C3000_CF0_IRQ;
		sc->sc_irqcfpin[0] = C3000_CF0_IRQ_PIN;
		sc->sc_irqpin[1] = C3000_CF1_IRQ;
		sc->sc_irqcfpin[1] = C3000_CF1_IRQ_PIN;
	}

	pxapcic_attach(sc, &scoop_pcic_socket_setup);
}

void
scoop_pcic_socket_setup(struct pxapcic_socket *so)
{
	struct pxapcic_softc *sc;
	bus_addr_t pa;
	bus_size_t size = SCOOP_SIZE;
	bus_space_tag_t iot;
	bus_space_handle_t scooph;
	int error;

	sc = so->sc;
	iot = sc->sc_iot;

	if (so->socket == 0)
		pa = C3000_SCOOP0_BASE;
	else if (so->socket == 1)
		pa = C3000_SCOOP1_BASE;
	else
		panic("%s: invalid CF slot %d", sc->sc_dev.dv_xname,
		    so->socket);
	error = bus_space_map(iot, trunc_page(pa), round_page(size),
	    0, &scooph);
	if (error)
		panic("%s: can't map memory %lx for scoop",
		    sc->sc_dev.dv_xname, pa);
	scooph += pa - trunc_page(pa);

	bus_space_write_2(iot, scooph, SCOOP_IMR,
	    SCP_IMR_UNKN0 | SCP_IMR_UNKN1);

	/* setup */
	bus_space_write_2(iot, scooph, SCOOP_MCR, 0x0100);
	bus_space_write_2(iot, scooph, SCOOP_CDR, 0x0000);
	bus_space_write_2(iot, scooph, SCOOP_CPR, 0x0000);
	bus_space_write_2(iot, scooph, SCOOP_IMR, 0x0000);
	bus_space_write_2(iot, scooph, SCOOP_IRM, 0x00ff);
	bus_space_write_2(iot, scooph, SCOOP_ISR, 0x0000);
	bus_space_write_2(iot, scooph, SCOOP_IRM, 0x0000);

	/* C3000 */
	if (so->socket == 1) {
		bus_space_write_2(iot, scooph, SCOOP_CPR, 0x80c1);
		bus_space_write_2(iot, scooph, SCOOP_IMR, 0x00c4);
		bus_space_write_2(iot, scooph, SCOOP_MCR, 0x0111);
	} else {
		bus_space_write_2(iot, scooph, SCOOP_CPR,
		    SCP_CPR_PWR|SCP_CPR_5V);
	}

	bus_space_write_2(iot, scooph, SCOOP_IMR, 0x00ce);
	bus_space_write_2(iot, scooph, SCOOP_MCR, 0x0111);

	/* C3000 */
	so->power_capability = PXAPCIC_POWER_3V;
	if (so->socket == 0)
		so->power_capability |= PXAPCIC_POWER_5V;

	so->pcictag_cookie = (void *)scooph;
	so->pcictag = &scoop_pcic_functions;
}

u_int
scoop_pcic_read(struct pxapcic_socket *so, int reg)
{
	bus_space_tag_t iot = so->sc->sc_iot;
	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
	u_int16_t csr;

	csr = bus_space_read_2(iot, ioh, SCOOP_CSR);

	switch (reg) {
	case PXAPCIC_CARD_STATUS:
		if (csr & SCP_CSR_MISSING)
			return (PXAPCIC_CARD_INVALID);
		else
			return (PXAPCIC_CARD_VALID);

	case PXAPCIC_CARD_READY:
		return ((bus_space_read_2(iot, ioh, SCOOP_CSR) &
		    SCP_CSR_READY) != 0);

	default:
		panic("scoop_pcic_read: bogus register");
	}
}

void
scoop_pcic_write(struct pxapcic_socket *so, int reg, u_int val)
{
	bus_space_tag_t iot = so->sc->sc_iot;
	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
	u_int16_t newval;
	int s;

	s = splhigh();

	switch (reg) {
	case PXAPCIC_CARD_POWER:
		newval = bus_space_read_2(iot, ioh, SCOOP_CPR);
		newval &= ~(SCP_CPR_PWR | SCP_CPR_3V | SCP_CPR_5V);

		if (val == PXAPCIC_POWER_3V)
			newval |= (SCP_CPR_PWR | SCP_CPR_3V);
		else if (val == PXAPCIC_POWER_5V)
			newval |= (SCP_CPR_PWR | SCP_CPR_5V);

		bus_space_write_2(iot, ioh, SCOOP_CPR, newval);
		break;

	case PXAPCIC_CARD_RESET:
		bus_space_write_2(iot, ioh, SCOOP_CCR,
		    val ? SCP_CCR_RESET : 0);
		break;

	default:
		panic("scoop_pcic_write: bogus register");
	}

	splx(s);
}

void
scoop_pcic_set_power(struct pxapcic_socket *so, int pwr)
{
	bus_space_tag_t iot = so->sc->sc_iot;
	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;
	u_int16_t reg;
	int s;

	s = splhigh();

	switch (pwr) {
	case PXAPCIC_POWER_OFF:
#if 0
		/* XXX does this disable power to both sockets? */
		reg = bus_space_read_2(iot, ioh, SCOOP_GPWR);
		bus_space_write_2(iot, ioh, SCOOP_GPWR,
		    reg & ~(1 << SCOOP0_CF_POWER_C3000));
#endif
		break;

	case PXAPCIC_POWER_3V:
	case PXAPCIC_POWER_5V:
		/* XXX */
		if (so->socket == 0) {
			reg = bus_space_read_2(iot, ioh, SCOOP_GPWR);
			bus_space_write_2(iot, ioh, SCOOP_GPWR,
			    reg | (1 << SCOOP0_CF_POWER_C3000));
		}
		break;

	default:
		splx(s);
		panic("scoop_pcic_set_power: bogus power state");
	}

	splx(s);
}

void
scoop_pcic_clear_intr(struct pxapcic_socket *so)
{
	bus_space_tag_t iot = so->sc->sc_iot;
	bus_space_handle_t ioh = (bus_space_handle_t)so->pcictag_cookie;

	bus_space_write_2(iot, ioh, SCOOP_IRM, 0x00ff);
	bus_space_write_2(iot, ioh, SCOOP_ISR, 0x0000);
	bus_space_write_2(iot, ioh, SCOOP_IRM, 0x0000);
}


code:
/*	$OpenBSD: zaurus_apm.c,v 1.33 2015/02/11 07:05:39 dlg Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/sysctl.h>
#include <sys/mutex.h>
#include <sys/reboot.h>
#include <sys/rwlock.h>
#include <dev/rndvar.h>

#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_apm.h>
#include <arm/xscale/pxa2x0_gpio.h>

#include <zaurus/dev/zaurus_scoopvar.h>
#include <zaurus/dev/zaurus_sspvar.h>
void zssp_init(void);	/* XXX */

#include <zaurus/dev/zaurus_apm.h>

#include <dev/wscons/wsdisplayvar.h>

#include "wsdisplay.h"

#if defined(APMDEBUG)
#define DPRINTF(x)	printf x
#else
#define	DPRINTF(x)	/**/
#endif

struct zapm_softc {
	struct pxa2x0_apm_softc sc;
	struct timeout sc_poll;
	struct timeval sc_lastbattchk;
	int	sc_suspended;
	int	sc_ac_on;
	int	sc_charging;
	int	sc_discharging;
	int	sc_batt_full;
	int	sc_batt_volt;
	u_int	sc_event;
};

int	apm_match(struct device *, void *, void *);
void	apm_attach(struct device *, struct device *, void *);
int	apm_activate(struct device *, int);

struct cfattach apm_pxaip_ca = {
        sizeof (struct zapm_softc), apm_match, apm_attach,
	NULL, apm_activate
};
extern struct cfdriver apm_cd;

/* MAX1111 command word */
#define MAXCTRL_PD0		(1<<0)
#define MAXCTRL_PD1		(1<<1)
#define MAXCTRL_SGL		(1<<2)
#define MAXCTRL_UNI		(1<<3)
#define MAXCTRL_SEL_SHIFT	4
#define MAXCTRL_STR		(1<<7)

/* MAX1111 ADC channels */
#define	BATT_THM		2
#define	BATT_AD			4
#define JK_VAD			6

/* battery-related GPIO pins */
#define GPIO_AC_IN_C3000	115	/* 0=AC connected */
#define GPIO_CHRG_CO_C3000	101	/* 1=battery full */
#define GPIO_BATT_COVER_C3000	90	/* 0=unlocked */

/*
 * Battery-specific information
 */

struct battery_threshold {
	int	bt_volt;
	int	bt_life;
	int	bt_state;
};

struct battery_info {
	int	bi_minutes;		/* 100% life time */
	const	struct battery_threshold *bi_thres;
};

const struct battery_threshold zaurus_battery_life_c3000[] = {
#if 0
	{224,	125,	APM_BATT_HIGH}, /* XXX unverified */
#endif
	{194,	100,	APM_BATT_HIGH},
	{188,	75,	APM_BATT_HIGH},
	{184,	50,	APM_BATT_HIGH},
	{180,	25,	APM_BATT_LOW},
	{178,	5,	APM_BATT_LOW},
	{0,	0,	APM_BATT_CRITICAL},
};

const struct battery_info zaurus_battery_c3000 = {
	180 /* minutes; pessimistic estimate */,
	zaurus_battery_life_c3000
};

const struct battery_info *zaurus_main_battery = &zaurus_battery_c3000;

/* Restart charging this many times before accepting BATT_FULL. */
#define MIN_BATT_FULL 2

/* Discharge 100 ms before reading the voltage if AC is connected. */
#define DISCHARGE_TIMEOUT (hz / 10)

/* Check battery voltage and "kick charging" every minute. */
const	struct timeval zapm_battchkrate = { 60, 0 };

/* Prototypes */

int	zapm_acintr(void *);
int	zapm_bcintr(void *);
int	zapm_ac_on(void);
int	max1111_adc_value(int);
int	max1111_adc_value_avg(int, int);
#if 0
int	zapm_jkvad_voltage(void);
int	zapm_batt_temp(void);
#endif
int	zapm_batt_volt(void);
int	zapm_batt_state(int);
int	zapm_batt_life(int);
int	zapm_batt_minutes(int);
void	zapm_enable_charging(struct zapm_softc *, int);
int	zapm_charge_complete(struct zapm_softc *);
void	zapm_poll(void *);
int	zapm_get_event(struct pxa2x0_apm_softc *, u_int *);
void	zapm_power_info(struct pxa2x0_apm_softc *, struct apm_power_info *);
void	zapm_suspend(struct pxa2x0_apm_softc *);
int	zapm_resume(struct pxa2x0_apm_softc *);
void	pxa2x0_setperf(int);
int	pxa2x0_cpuspeed(int *);

int
apm_match(struct device *parent, void *match, void *aux)
{
	return (1);
}

void
apm_attach(struct device *parent, struct device *self, void *aux)
{
	struct zapm_softc *sc = (struct zapm_softc *)self;

	pxa2x0_gpio_set_function(GPIO_AC_IN_C3000, GPIO_IN);
	pxa2x0_gpio_set_function(GPIO_CHRG_CO_C3000, GPIO_IN);
	pxa2x0_gpio_set_function(GPIO_BATT_COVER_C3000, GPIO_IN);

	(void)pxa2x0_gpio_intr_establish(GPIO_AC_IN_C3000,
	    IST_EDGE_BOTH, IPL_BIO, zapm_acintr, sc, "apm_ac");
	(void)pxa2x0_gpio_intr_establish(GPIO_BATT_COVER_C3000,
	    IST_EDGE_BOTH, IPL_BIO, zapm_bcintr, sc, "apm_bc");

	sc->sc_event = APM_NOEVENT;
	sc->sc.sc_get_event = zapm_get_event;
	sc->sc.sc_power_info = zapm_power_info;
	sc->sc.sc_suspend = zapm_suspend;
	sc->sc.sc_resume = zapm_resume;

	timeout_set(&sc->sc_poll, &zapm_poll, sc);

	/* Get initial battery voltage. */
	zapm_enable_charging(sc, 0);
	if (zapm_ac_on()) {
		/* C3000: discharge 100 ms when AC is on. */
		scoop_discharge_battery(1);
		delay(100000);
	}
	sc->sc_batt_volt = zapm_batt_volt();
	scoop_discharge_battery(0);

	pxa2x0_apm_attach_sub(&sc->sc);

	cpu_setperf = pxa2x0_setperf;
	cpu_cpuspeed = pxa2x0_cpuspeed;
}

int
apm_activate(struct device *self, int act)
{
	struct zapm_softc *sc = (struct zapm_softc *)self;
	int ret = 0;

	switch (act) {
	case DVACT_POWERDOWN:
		zapm_enable_charging(sc, 0);
		break;
	}

	return (ret);
}

int
zapm_acintr(void *v)
{
	zapm_poll(v);
	return (1);
}

int
zapm_bcintr(void *v)
{
	zapm_poll(v);
	return (1);
}

int
zapm_ac_on(void)
{
	return (!pxa2x0_gpio_get_bit(GPIO_AC_IN_C3000));
}

int
max1111_adc_value(int chan)
{

	return ((int)zssp_ic_send(ZSSP_IC_MAX1111, MAXCTRL_PD0 |
	    MAXCTRL_PD1 | MAXCTRL_SGL | MAXCTRL_UNI |
	    (chan << MAXCTRL_SEL_SHIFT) | MAXCTRL_STR));
}

/* XXX simplify */
int
max1111_adc_value_avg(int chan, int pause)
{
	int val[5];
	int i, j, k, x;
	int sum = 0;

	for (i = 0; i < 5; i++) {
		val[i] = max1111_adc_value(chan);
		if (i != 4)
			delay(pause * 1000);
	}

	x = val[0];
	j = 0;
	for (i = 1; i < 5; i++) {
		if (x < val[i]) {
			x = val[i];
			j = i;
		}
	}

	x = val[4];
	k = 4;
	for (i = 3; i >= 0; i--) {
		if (x > val[i]) {
			x = val[i];
			k = i;
		}
	}

	for (i = 0; i < 5; i++) {
		if (i == j || i == k)
			continue;
		sum += val[i];
	}

	return (sum / 3);
}

#if 0
/*
 * Return the voltage available for charging.  This will be zero,
 * unless A/C power is connected.
 */
int
zapm_jkvad_voltage(void)
{

	return (max1111_adc_value_avg(JK_VAD, 10));
}

int
zapm_batt_temp(void)
{
	int temp;

	scoop_battery_temp_adc(1);
	delay(10000);
	temp = max1111_adc_value_avg(BATT_THM, 1);
	scoop_battery_temp_adc(0);

	return (temp);
}
#endif

int
zapm_batt_volt(void)
{

	return (max1111_adc_value_avg(BATT_AD, 10));
}

int
zapm_batt_state(int volt)
{
	const struct battery_threshold *bthr;
	int i;

	bthr = zaurus_main_battery->bi_thres;

	for (i = 0; bthr[i].bt_volt > 0; i++)
		if (bthr[i].bt_volt <= volt)
			break;

	return (bthr[i].bt_state);
}

int
zapm_batt_life(int volt)
{
	const struct battery_threshold *bthr;
	int i;

	bthr = zaurus_main_battery->bi_thres;

	for (i = 0; bthr[i].bt_volt > 0; i++)
		if (bthr[i].bt_volt <= volt)
			break;

	if (i == 0)
		return (bthr[i].bt_life);

	return (bthr[i].bt_life +
	    ((volt - bthr[i].bt_volt) * 100) /
	    (bthr[i-1].bt_volt - bthr[i].bt_volt) *
	    (bthr[i-1].bt_life - bthr[i].bt_life) / 100);
}

int
zapm_batt_minutes(int life)
{

	return (zaurus_main_battery->bi_minutes * life / 100);
}

void
zapm_enable_charging(struct zapm_softc *sc, int enable)
{

	scoop_discharge_battery(0);
	scoop_charge_battery(enable, 0);
	scoop_led_set(SCOOP_LED_ORANGE, enable);
}

/*
 * Return non-zero if the charge complete signal indicates that the
 * battery is fully charged.  Restart charging to clear this signal.
 */
int
zapm_charge_complete(struct zapm_softc *sc)
{

	if (sc->sc_charging && sc->sc_batt_full < MIN_BATT_FULL) {
		if (pxa2x0_gpio_get_bit(GPIO_CHRG_CO_C3000) != 0) {
			if (++sc->sc_batt_full < MIN_BATT_FULL) {
				DPRINTF(("battery almost full\n"));
				zapm_enable_charging(sc, 0);
				delay(15000);
				zapm_enable_charging(sc, 1);
			}
		} else if (sc->sc_batt_full > 0) {
			/* false alarm */
			sc->sc_batt_full = 0;
			zapm_enable_charging(sc, 0);
			delay(15000);
			zapm_enable_charging(sc, 1);
		}
	}

	return (sc->sc_batt_full >= MIN_BATT_FULL);
}

/*
 * Poll power-management related GPIO inputs, update battery life
 * in softc, and/or control battery charging.
 */
void
zapm_poll(void *v)
{
	struct zapm_softc *sc = v;
	int ac_on;
	int bc_lock;
	int charging;
	int volt;
	int s;

	s = splhigh();

	/* Check positition of battery compartment lock switch. */
	bc_lock = pxa2x0_gpio_get_bit(GPIO_BATT_COVER_C3000) ? 1 : 0;

	/* Stop discharging. */
	if (sc->sc_discharging) {
		sc->sc_discharging = 0;
		volt = zapm_batt_volt();
		ac_on = zapm_ac_on();
		charging = 0;
		DPRINTF(("zapm_poll: discharge off volt %d\n", volt));
	} else {
		ac_on = zapm_ac_on();
		charging = sc->sc_charging;
		volt = sc->sc_batt_volt;
	}

	/* Start or stop charging as necessary. */
	if (ac_on && bc_lock) {
		if (charging) {
			if (zapm_charge_complete(sc)) {
				DPRINTF(("zapm_poll: batt full\n"));
				charging = 0;
				zapm_enable_charging(sc, 0);
			}
		} else if (!zapm_charge_complete(sc)) {
			charging = 1;
			volt = zapm_batt_volt();
			zapm_enable_charging(sc, 1);
			DPRINTF(("zapm_poll: start charging volt %d\n", volt));
		}
	} else {
		if (charging) {
			charging = 0;
			zapm_enable_charging(sc, 0);
			timerclear(&sc->sc_lastbattchk);
			DPRINTF(("zapm_poll: stop charging\n"));
		}
		sc->sc_batt_full = 0;
	}

	/*
	 * Restart charging once in a while.  Discharge a few milliseconds
	 * before updating the voltage in our softc if A/C is connected.
	 */
	if (bc_lock && ratecheck(&sc->sc_lastbattchk, &zapm_battchkrate)) {
		if (sc->sc_suspended) {
			DPRINTF(("zapm_poll: suspended %lu %lu\n",
			    sc->sc_lastbattchk.tv_sec,
			    pxa2x0_rtc_getsecs()));
			if (charging) {
				zapm_enable_charging(sc, 0);
				delay(15000);
				zapm_enable_charging(sc, 1);
				pxa2x0_rtc_setalarm(pxa2x0_rtc_getsecs() +
				    zapm_battchkrate.tv_sec + 1);
			}
		} else if (ac_on && sc->sc_batt_full == 0) {
			DPRINTF(("zapm_poll: discharge on\n"));
			if (charging)
				zapm_enable_charging(sc, 0);
			sc->sc_discharging = 1;
			scoop_discharge_battery(1);
			timeout_add(&sc->sc_poll, DISCHARGE_TIMEOUT);
		} else if (!ac_on) {
			volt = zapm_batt_volt();
			DPRINTF(("zapm_poll: volt %d\n", volt));
		}
	}

	/* Update the cached power state in our softc. */
	if (ac_on != sc->sc_ac_on || charging != sc->sc_charging ||
	    volt != sc->sc_batt_volt) {
		sc->sc_ac_on = ac_on;
		sc->sc_charging = charging;
		sc->sc_batt_volt = volt;
		if (sc->sc_event == APM_NOEVENT)
			sc->sc_event = APM_POWER_CHANGE;
	}

	/* Detect battery low conditions. */
	if (!ac_on) {
		if (zapm_batt_life(volt) < 5)
			sc->sc_event = APM_BATTERY_LOW;
		if (zapm_batt_state(volt) == APM_BATT_CRITICAL)
			sc->sc_event = APM_CRIT_SUSPEND_REQ;
	}

#ifdef APMDEBUG
	if (sc->sc_event != APM_NOEVENT)
		DPRINTF(("zapm_poll: power event %d\n", sc->sc_event));
#endif
	splx(s);
}

/*
 * apm_thread() calls this routine approximately once per second.
 */
int
zapm_get_event(struct pxa2x0_apm_softc *pxa_sc, u_int *typep)
{
	struct zapm_softc *sc = (struct zapm_softc *)pxa_sc;
	int s;

	s = splsoftclock();

	/* Don't interfere with discharging. */
	if (sc->sc_discharging)
		*typep = sc->sc_event;
	else if (sc->sc_event == APM_NOEVENT) {
		zapm_poll(sc);
		*typep = sc->sc_event;
	}
	sc->sc_event = APM_NOEVENT;

	splx(s);
	return (*typep == APM_NOEVENT);
}

/*
 * Return power status to the generic APM driver.
 */
void
zapm_power_info(struct pxa2x0_apm_softc *pxa_sc, struct apm_power_info *power)
{
	struct zapm_softc *sc = (struct zapm_softc *)pxa_sc;
	int s;
	int ac_on;
	int volt;
	int charging;

	s = splsoftclock();
	ac_on = sc->sc_ac_on;
	volt = sc->sc_batt_volt;
	charging = sc->sc_charging;
	splx(s);

	power->ac_state = ac_on ? APM_AC_ON : APM_AC_OFF;
	if (charging)
		power->battery_state = APM_BATT_CHARGING;
	else
		power->battery_state = zapm_batt_state(volt);

	power->battery_life = zapm_batt_life(volt);
	power->minutes_left = zapm_batt_minutes(power->battery_life);
}

/*
 * Called before suspending when all ca_activate functions are done.
 */
void
zapm_suspend(struct pxa2x0_apm_softc *pxa_sc)
{
	struct zapm_softc *sc = (struct zapm_softc *)pxa_sc;

	config_suspend_all(DVACT_QUIESCE);
	bufq_quiesce();

	/* Poll in suspended mode and forget the discharge timeout. */
	sc->sc_suspended = 1;
	timeout_del(&sc->sc_poll);

	/* Make sure charging is enabled and RTC alarm is set. */
	timerclear(&sc->sc_lastbattchk);

	zapm_poll(sc);

#if 0
	pxa2x0_rtc_setalarm(pxa2x0_rtc_getsecs() + 5);
#endif
	pxa2x0_wakeup_config(PXA2X0_WAKEUP_ALL, 1);
}

/*
 * Called after wake-up from suspend with interrupts still disabled,
 * before any ca_activate functions are done.
 */
int
zapm_resume(struct pxa2x0_apm_softc *pxa_sc)
{
	struct zapm_softc *sc = (struct zapm_softc *)pxa_sc;
	int	a, b;
	u_int	wsrc;
	int	wakeup = 0;

	/* C3000 */
	a = pxa2x0_gpio_get_bit(97) ? 1 : 0;
	b = pxa2x0_gpio_get_bit(96) ? 2 : 0;

	wsrc = pxa2x0_wakeup_status();

	/* Resume only if the lid is not closed. */
	if ((a | b) != 3 && (wsrc & PXA2X0_WAKEUP_POWERON) != 0) {
		int timeout = 100; /* 10 ms */
		/* C3000 */
		while (timeout-- > 0 && pxa2x0_gpio_get_bit(95) != 0) {
			if (timeout == 0) {
				wakeup = 1;
				break;
			}
			delay(100);
		}
	}

	/* Initialize the SSP unit before using the MAX1111 again. */
	zssp_init();

	zapm_poll(sc);

	if (wakeup) {
		/* Resume normal polling. */
		sc->sc_suspended = 0;

		pxa2x0_rtc_setalarm(0);
	} else {
#if 0
		DPRINTF(("zapm_resume: suspended %lu %lu\n",
		    sc->sc_lastbattchk.tv_sec, pxa2x0_rtc_getsecs()));
		pxa2x0_rtc_setalarm(pxa2x0_rtc_getsecs() + 5);
#endif
	}

	return (wakeup);
}

void
zapm_poweroff(void)
{
	struct pxa2x0_apm_softc *sc;
	int s;

	KASSERT(apm_cd.cd_ndevs > 0 && apm_cd.cd_devs[0] != NULL);
	sc = apm_cd.cd_devs[0];

#if NWSDISPLAY > 0
	wsdisplay_suspend();
#endif /* NWSDISPLAY > 0 */

	s = splhigh();
	config_suspend_all(DVACT_SUSPEND);

	suspend_randomness();

	/* XXX
	 * Flag to disk drivers that they should "power down" the disk
	 * when we get to DVACT_POWERDOWN.
	 */
	boothowto |= RB_POWERDOWN;
	config_suspend_all(DVACT_POWERDOWN);
	boothowto &= ~RB_POWERDOWN;

	/* XXX enable charging during suspend */

	/* XXX keep power LED state during suspend */

	/* XXX do the same thing for GPIO 43 (BTTXD) */

	/* XXX scoop power down */

	/* XXX set PGSRn and GPDRn */

	pxa2x0_wakeup_config(PXA2X0_WAKEUP_ALL, 1);

	do {
		pxa2x0_apm_sleep(sc);
	} while (!zapm_resume(sc));

	zapm_restart();

	/* NOTREACHED */
	config_suspend_all(DVACT_RESUME);
	splx(s);

	resume_randomness(NULL, 0);	/* force RNG upper level reseed */
	bufq_restart();

	config_suspend_all(DVACT_WAKEUP);

#if NWSDISPLAY > 0
	wsdisplay_resume();
#endif /* NWSDISPLAY > 0 */
}

/*
 * Do a GPIO reset, immediately causing the processor to begin the normal
 * boot sequence.  See 2.7 Reset in the PXA27x Developer's Manual for the
 * summary of effects of this kind of reset.
 */
void
zapm_restart(void)
{
	if (apm_cd.cd_ndevs > 0 && apm_cd.cd_devs[0] != NULL) {
		struct pxa2x0_apm_softc *sc = apm_cd.cd_devs[0];
		int rv;

		/*
		 * Reduce the ROM Delay Next Access and ROM Delay First
		 * Access times for synchronous flash connected to nCS1.
		 */
		rv = bus_space_read_4(sc->sc_iot, sc->sc_memctl_ioh,
		    MEMCTL_MSC0);
		if ((rv & 0xffff0000) == 0x7ff00000)
			bus_space_write_4(sc->sc_iot, sc->sc_memctl_ioh,
			    MEMCTL_MSC0, (rv & 0xffff) | 0x7ee00000);
	}

	/* External reset circuit presumably asserts nRESET_GPIO. */
	pxa2x0_gpio_set_function(89, GPIO_OUT | GPIO_SET);
	delay(1000000);
}


code:
/*	$OpenBSD: zaurus_apm.h,v 1.1 2005/04/11 03:21:03 uwe Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

void	zapm_poweroff(void);
void	zapm_restart(void);


code:
/*	$OpenBSD: zaurus_audio.c,v 1.19 2015/05/11 06:46:21 ratchov Exp $	*/

/*
 * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * TODO:
 *	- record support
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/audioio.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_i2c.h>
#include <arm/xscale/pxa2x0_i2s.h>
#include <arm/xscale/pxa2x0_dmac.h>
#include <arm/xscale/pxa2x0_gpio.h>

#include <zaurus/dev/zaurus_scoopvar.h>
#include <dev/i2c/wm8750reg.h>
#include <dev/audio_if.h>

#define WM8750_ADDRESS  0x1B
#define SPKR_VOLUME	112

#define wm8750_write(sc, reg, val)	pxa2x0_i2c_write_2(&sc->sc_i2c, \
    WM8750_ADDRESS, (((reg) << 9) | ((val) & 0x1ff)))

int	zaudio_match(struct device *, void *, void *);
void	zaudio_attach(struct device *, struct device *, void *);
int	zaudio_detach(struct device *, int);
int	zaudio_activate(struct device *, int);
void    zaudio_pintr(void *);
void    zaudio_rintr(void *);

#define ZAUDIO_OP_SPKR	0
#define ZAUDIO_OP_HP	1

#define ZAUDIO_JACK_STATE_OUT	0
#define ZAUDIO_JACK_STATE_IN	1
#define ZAUDIO_JACK_STATE_INS	2
#define ZAUDIO_JACK_STATE_REM	3

/* GPIO pins */
#define GPIO_HP_IN_C3000	116

struct zaudio_volume {
	u_int8_t		left;
	u_int8_t		right;
};

struct zaudio_softc {
	struct device		sc_dev;

	/* i2s device softc */
	/* NB: pxa2x0_i2s requires this to be the second struct member */
	struct pxa2x0_i2s_softc	sc_i2s;

	/* i2c device softc */
	struct pxa2x0_i2c_softc	sc_i2c;

	int			sc_playing;

	struct zaudio_volume	sc_volume[2];
	char			sc_unmute[2];

	int			sc_state;
	int			sc_icount;
	struct timeout		sc_to;
	void		      (*sc_pintr)(void *);
	void		       *sc_parg;
};

struct cfattach zaudio_ca = {
	sizeof(struct zaudio_softc), zaudio_match, zaudio_attach,
	zaudio_detach, zaudio_activate
};

struct cfdriver zaudio_cd = {
	NULL, "zaudio", DV_DULL
};

struct audio_device wm8750_device = {
	"WM8750",
	"1.0",
	"wm"
};

void zaudio_init(struct zaudio_softc *);
int zaudio_jack_intr(void *);
void zaudio_jack(void *);
void zaudio_standby(struct zaudio_softc *);
void zaudio_update_volume(struct zaudio_softc *, int);
void zaudio_update_mutes(struct zaudio_softc *);
void zaudio_play_setup(struct zaudio_softc *);
int zaudio_open(void *, int);
void zaudio_close(void *);
int zaudio_query_encoding(void *, struct audio_encoding *);
int zaudio_set_params(void *, int, int, struct audio_params *,
    struct audio_params *);
int zaudio_halt_output(void *);
int zaudio_halt_input(void *);
int zaudio_getdev(void *, struct audio_device *);
int zaudio_set_port(void *, struct mixer_ctrl *);
int zaudio_get_port(void *, struct mixer_ctrl *);
int zaudio_query_devinfo(void *, struct mixer_devinfo *);
int zaudio_get_props(void *);
int zaudio_start_output(void *, void *, int, void (*)(void *), void *);
int zaudio_start_input(void *, void *, int, void (*)(void *), void *);

struct audio_hw_if wm8750_hw_if = {
	zaudio_open,
	zaudio_close,
	NULL /* zaudio_drain */,
	zaudio_query_encoding,
	zaudio_set_params,
	pxa2x0_i2s_round_blocksize,
	NULL /* zaudio_commit_settings */,
	NULL /* zaudio_init_output */,
	NULL /* zaudio_init_input */,
	zaudio_start_output,
	zaudio_start_input,
	zaudio_halt_output,
	zaudio_halt_input,
	NULL /* zaudio_speaker_ctl */,
	zaudio_getdev,
	NULL /* zaudio_setfd */,
	zaudio_set_port,
	zaudio_get_port,
	zaudio_query_devinfo,
	pxa2x0_i2s_allocm,
	pxa2x0_i2s_freem,
	pxa2x0_i2s_round_buffersize,
	pxa2x0_i2s_mappage,
	zaudio_get_props,
	NULL /* zaudio_trigger_output */,
	NULL /* zaudio_trigger_input */,
	NULL
};

static const unsigned short playback_registers[][2] = {
	/* Unmute DAC */
	{ ADCDACCTL_REG, 0x000 },

	/* 16 bit audio words */
	{ AUDINT_REG, AUDINT_SET_FORMAT(2) },

	/* Enable thermal protection, power */
	{ ADCTL1_REG, ADCTL1_TSDEN | ADCTL1_SET_VSEL(3) },

	/* Enable speaker driver, DAC oversampling */
	{ ADCTL2_REG, ADCTL2_ROUT2INV | ADCTL2_DACOSR },

	/* Set DAC voltage references */
	{ PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(1) | PWRMGMT1_VREF },

	/* Direct DACs to output mixers */
	{ LOUTMIX1_REG, LOUTMIX1_LD2LO },
	{ ROUTMIX2_REG, ROUTMIX2_RD2RO },

	/* End of list */
	{ 0xffff, 0xffff }
};

int
zaudio_match(struct device *parent, void *match, void *aux)
{
	return (1);
}

void
zaudio_attach(struct device *parent, struct device *self, void *aux)
{
	struct zaudio_softc		*sc = (struct zaudio_softc *)self;
	struct pxaip_attach_args	*pxa = aux;
	int err;

	sc->sc_i2s.sc_iot = pxa->pxa_iot;
	sc->sc_i2s.sc_dmat = pxa->pxa_dmat;
	sc->sc_i2s.sc_size = PXA2X0_I2S_SIZE;
	if (pxa2x0_i2s_attach_sub(&sc->sc_i2s)) {
		printf(": unable to attach I2S\n");
		goto fail_i2s;
	}

	sc->sc_i2c.sc_iot = pxa->pxa_iot;
	sc->sc_i2c.sc_size = PXA2X0_I2C_SIZE;
	if (pxa2x0_i2c_attach_sub(&sc->sc_i2c)) {
		printf(": unable to attach I2C\n");
		goto fail_i2c;
	}

	/* Check for an I2C response from the wm8750 */
	pxa2x0_i2c_open(&sc->sc_i2c);
	err = wm8750_write(sc, RESET_REG, 0);
	pxa2x0_i2c_close(&sc->sc_i2c);

	if (err) {
		printf(": codec failed to respond\n");
		goto fail_probe;
	}
	delay(100);

	/* Speaker on, headphones off by default. */
	sc->sc_volume[ZAUDIO_OP_SPKR].left = 240;
	sc->sc_unmute[ZAUDIO_OP_SPKR] = 1;
	sc->sc_volume[ZAUDIO_OP_HP].left = 180;
	sc->sc_volume[ZAUDIO_OP_HP].right = 180;
	sc->sc_unmute[ZAUDIO_OP_HP] = 0;

	/* Configure headphone jack state change handling. */
	timeout_set(&sc->sc_to, zaudio_jack, sc);
	pxa2x0_gpio_set_function(GPIO_HP_IN_C3000, GPIO_IN);
	(void)pxa2x0_gpio_intr_establish(GPIO_HP_IN_C3000,
	    IST_EDGE_BOTH, IPL_BIO, zaudio_jack_intr, sc, "hpjk");

	zaudio_init(sc);

	printf(": I2C, I2S, WM8750 Audio\n");

	audio_attach_mi(&wm8750_hw_if, sc, &sc->sc_dev);

	return;

fail_probe:
	pxa2x0_i2c_detach_sub(&sc->sc_i2c);
fail_i2c:
	pxa2x0_i2s_detach_sub(&sc->sc_i2s);
fail_i2s:
	;
}

int
zaudio_detach(struct device *self, int flags)
{
	struct zaudio_softc *sc = (struct zaudio_softc *)self;

	pxa2x0_i2c_detach_sub(&sc->sc_i2c);
	pxa2x0_i2s_detach_sub(&sc->sc_i2s);

	return (0);
}

int
zaudio_activate(struct device *self, int act)
{
	struct zaudio_softc *sc = (struct zaudio_softc *)self;

	switch (act) {
	case DVACT_SUSPEND:
		timeout_del(&sc->sc_to);
		zaudio_standby(sc);
		break;
	case DVACT_RESUME:
		pxa2x0_i2s_init(&sc->sc_i2s);
		pxa2x0_i2c_init(&sc->sc_i2c);
		zaudio_init(sc);
		break;
	}
	return 0;
}

void
zaudio_init(struct zaudio_softc *sc)
{
	pxa2x0_i2c_open(&sc->sc_i2c);

	/* Reset the codec */
	wm8750_write(sc, RESET_REG, 0);
	delay(100);

	/* Switch to standby power only */
	wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2));
	wm8750_write(sc, PWRMGMT2_REG, 0);

	/* Configure digital interface for I2S */
	wm8750_write(sc, AUDINT_REG, AUDINT_SET_FORMAT(2));

	/* Initialise volume levels */
	zaudio_update_volume(sc, ZAUDIO_OP_SPKR);
	zaudio_update_volume(sc, ZAUDIO_OP_HP);
	scoop_set_headphone(0);

	pxa2x0_i2c_close(&sc->sc_i2c);

	/* Assume that the jack state has changed. */
	zaudio_jack(sc);

}

int
zaudio_jack_intr(void *v)
{
	struct zaudio_softc *sc = v;

	if (!timeout_triggered(&sc->sc_to))
		zaudio_jack(sc);

	return (1);
}

void
zaudio_jack(void *v)
{
	struct zaudio_softc *sc = v;

	switch (sc->sc_state) {
	case ZAUDIO_JACK_STATE_OUT:
		if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
			sc->sc_state = ZAUDIO_JACK_STATE_INS;
			sc->sc_icount = 0;
		}
		break;
	case ZAUDIO_JACK_STATE_INS:
		if (sc->sc_icount++ > 2) {
			if (pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
				sc->sc_state = ZAUDIO_JACK_STATE_IN;
				sc->sc_unmute[ZAUDIO_OP_SPKR] = 0;
				sc->sc_unmute[ZAUDIO_OP_HP] = 1;
				goto update_mutes;
			} else
				sc->sc_state = ZAUDIO_JACK_STATE_OUT;
		}
		break;
	case ZAUDIO_JACK_STATE_IN:
		if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
			sc->sc_state = ZAUDIO_JACK_STATE_REM;
			sc->sc_icount = 0;
		}
		break;
	case ZAUDIO_JACK_STATE_REM:
		if (sc->sc_icount++ > 2) {
			if (!pxa2x0_gpio_get_bit(GPIO_HP_IN_C3000)) {
				sc->sc_state = ZAUDIO_JACK_STATE_OUT;
				sc->sc_unmute[ZAUDIO_OP_SPKR] = 1;
				sc->sc_unmute[ZAUDIO_OP_HP] = 0;
				goto update_mutes;
			} else
				sc->sc_state = ZAUDIO_JACK_STATE_IN;
		}
		break;
	}

	timeout_add(&sc->sc_to, hz/4);
	return;

update_mutes:
	timeout_del(&sc->sc_to);

	if (sc->sc_playing) {
		pxa2x0_i2c_open(&sc->sc_i2c);
		zaudio_update_mutes(sc);
		pxa2x0_i2c_close(&sc->sc_i2c);
	}
}

void
zaudio_standby(struct zaudio_softc *sc)
{
	pxa2x0_i2c_open(&sc->sc_i2c);

	/* Switch codec to standby power only */
	wm8750_write(sc, PWRMGMT1_REG, PWRMGMT1_SET_VMIDSEL(2));
	wm8750_write(sc, PWRMGMT2_REG, 0);

	scoop_set_headphone(0);

	pxa2x0_i2c_close(&sc->sc_i2c);
}

void
zaudio_update_volume(struct zaudio_softc *sc, int output)
{
	switch(output) {
	case ZAUDIO_OP_SPKR:
		wm8750_write(sc, LOUT2VOL_REG, LOUT2VOL_LO2VU | LOUT2VOL_LO2ZC |
		    LOUT2VOL_SET_LOUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR
		    ].left >> 1));
		wm8750_write(sc, ROUT2VOL_REG, ROUT2VOL_RO2VU | ROUT2VOL_RO2ZC |
		    ROUT2VOL_SET_ROUT2VOL(sc->sc_volume[ZAUDIO_OP_SPKR
		    ].left >> 1));
		break;
	case ZAUDIO_OP_HP:
		wm8750_write(sc, LOUT1VOL_REG, LOUT1VOL_LO1VU | LOUT1VOL_LO1ZC |
		    LOUT1VOL_SET_LOUT1VOL(sc->sc_volume[ZAUDIO_OP_HP
		    ].left >> 1));
		wm8750_write(sc, ROUT1VOL_REG, ROUT1VOL_RO1VU | ROUT1VOL_RO1ZC |
		    ROUT1VOL_SET_ROUT1VOL(sc->sc_volume[ZAUDIO_OP_HP
		    ].right >> 1));
		break;
	}
}

void
zaudio_update_mutes(struct zaudio_softc *sc)
{
	unsigned short val;

	val = PWRMGMT2_DACL | PWRMGMT2_DACR;

	if (sc->sc_unmute[ZAUDIO_OP_SPKR])
		val |= PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2;

	if (sc->sc_unmute[ZAUDIO_OP_HP])
		val |= PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1;

	wm8750_write(sc, PWRMGMT2_REG, val);

	scoop_set_headphone(sc->sc_unmute[ZAUDIO_OP_HP]);
}

void
zaudio_play_setup(struct zaudio_softc *sc)
{
	int i = 0;

	pxa2x0_i2c_open(&sc->sc_i2c);

	/* Program the codec with playback settings */
	while (playback_registers[i][0] != 0xffff) {
		wm8750_write(sc, playback_registers[i][0],
		    playback_registers[i][1]);
		i++;
	}
	zaudio_update_mutes(sc);

	pxa2x0_i2c_close(&sc->sc_i2c);
}

int
zaudio_open(void *hdl, int flags)
{
	struct zaudio_softc *sc = hdl;

	/* can't record yet */
	if (flags & FREAD)
		return (ENXIO);

	/* Power on the I2S bus and codec */
	pxa2x0_i2s_open(&sc->sc_i2s);

	return 0;
}

void
zaudio_close(void *hdl)
{
	struct zaudio_softc *sc = hdl;

	/* Power off the I2S bus and codec */
	pxa2x0_i2s_close(&sc->sc_i2s);
}

int
zaudio_query_encoding(void *hdl, struct audio_encoding *aep)
{
	switch (aep->index) {
	case 0:
		strlcpy(aep->name, AudioEslinear_le, sizeof(aep->name));
		aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
		aep->precision = 16;
		aep->flags = 0;
		break;
	default:
		return (EINVAL);
	}
	aep->bps = AUDIO_BPS(aep->precision);
	aep->msb = 1;
	return (0);
}

int
zaudio_set_params(void *hdl, int setmode, int usemode,
    struct audio_params *play, struct audio_params *rec)
{
	struct zaudio_softc *sc = hdl;

	if (setmode & AUMODE_PLAY) {
		play->precision = 16;
		play->channels = 2;
		play->encoding = AUDIO_ENCODING_SLINEAR_LE;
		play->bps = AUDIO_BPS(play->precision);
		play->msb = 1;

		pxa2x0_i2s_setspeed(&sc->sc_i2s, &play->sample_rate);
	}

#if RECORD_XXX_NOT_YET
	if (setmode & AUMODE_RECORD) {
		rec->precision = 16;
		rec->channels = 2;
		rec->encoding = AUDIO_ENCODING_SLINEAR_LE;
		rec->bps = AUDIO_BPS(rec->precision);
		rec->msb = 1;

		pxa2x0_i2s_setspeed(sc, &rec->sample_rate);
	}
#endif

	return (0);
}

int
zaudio_halt_output(void *hdl)
{
	struct zaudio_softc *sc = hdl;

	/* XXX forcibly stop output DMA? */

	mtx_enter(&audio_lock);
	zaudio_standby(sc);
	sc->sc_playing = 0;
	mtx_leave(&audio_lock);

	return 0;
}

int
zaudio_halt_input(void *hdl)
{
	/* struct zaudio_softc *sc = hdl; */

	return 0;
}

int
zaudio_getdev(void *hdl, struct audio_device *ret)
{
	/* struct zaudio_softc *sc = hdl; */

	*ret = wm8750_device;
	return 0;
}

#define ZAUDIO_SPKR_LVL		0
#define ZAUDIO_SPKR_MUTE	1
#define ZAUDIO_HP_LVL		2
#define ZAUDIO_HP_MUTE		3
#define ZAUDIO_OUTPUT_CLASS	4

int
zaudio_set_port(void *hdl, struct mixer_ctrl *mc)
{
	struct zaudio_softc *sc = hdl;
	int error = EINVAL, s;

	s = splbio();
	pxa2x0_i2c_open(&sc->sc_i2c);

	switch (mc->dev) {
	case ZAUDIO_SPKR_LVL:
		if (mc->type != AUDIO_MIXER_VALUE)
			break;
		if (mc->un.value.num_channels == 1)
			sc->sc_volume[ZAUDIO_OP_SPKR].left =
			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
		else
			break;
		zaudio_update_volume(sc, ZAUDIO_OP_SPKR);
		error = 0;
		break;
	case ZAUDIO_SPKR_MUTE:
		if (mc->type != AUDIO_MIXER_ENUM)
			break;
		sc->sc_unmute[ZAUDIO_OP_SPKR] = mc->un.ord ? 1 : 0;
		zaudio_update_mutes(sc);
		error = 0;
		break;
	case ZAUDIO_HP_LVL:
		if (mc->type != AUDIO_MIXER_VALUE)
			break;
		if (mc->un.value.num_channels == 1) {
			sc->sc_volume[ZAUDIO_OP_HP].left =
			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
			sc->sc_volume[ZAUDIO_OP_HP].right =
			    mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
		} else if (mc->un.value.num_channels == 2) {
			sc->sc_volume[ZAUDIO_OP_HP].left =
			    mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
			sc->sc_volume[ZAUDIO_OP_HP].right =
			    mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
		}
		else
			break;
		zaudio_update_volume(sc, ZAUDIO_OP_HP);
		error = 0;
		break;
	case ZAUDIO_HP_MUTE:
		if (mc->type != AUDIO_MIXER_ENUM)
			break;
		sc->sc_unmute[ZAUDIO_OP_HP] = mc->un.ord ? 1 : 0;
		zaudio_update_mutes(sc);
		error = 0;
		break;
	}

	pxa2x0_i2c_close(&sc->sc_i2c);
	splx(s);

	return error;
}

int
zaudio_get_port(void *hdl, struct mixer_ctrl *mc)
{
	struct zaudio_softc *sc = hdl;
	int error = EINVAL;

	switch (mc->dev) {
	case ZAUDIO_SPKR_LVL:
		if (mc->type != AUDIO_MIXER_VALUE)
			break;
		if (mc->un.value.num_channels == 1)
			mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
			    sc->sc_volume[ZAUDIO_OP_SPKR].left;
		else
			break;
		error = 0;
		break;
	case ZAUDIO_SPKR_MUTE:
		if (mc->type != AUDIO_MIXER_ENUM)
			break;
		mc->un.ord = sc->sc_unmute[ZAUDIO_OP_SPKR] ? 1 : 0;
		error = 0;
		break;
	case ZAUDIO_HP_LVL:
		if (mc->type != AUDIO_MIXER_VALUE)
			break;
		if (mc->un.value.num_channels == 1)
			mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
			    sc->sc_volume[ZAUDIO_OP_HP].left;
		else if (mc->un.value.num_channels == 2) {
			mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
			    sc->sc_volume[ZAUDIO_OP_HP].left;
			mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
			    sc->sc_volume[ZAUDIO_OP_HP].right;
		}
		else
			break;
		error = 0;
		break;
	case ZAUDIO_HP_MUTE:
		if (mc->type != AUDIO_MIXER_ENUM)
			break;
		mc->un.ord = sc->sc_unmute[ZAUDIO_OP_HP] ? 1 : 0;
		error = 0;
		break;
	}

	return error;
}

int
zaudio_query_devinfo(void *hdl, struct mixer_devinfo *di)
{
	/* struct zaudio_softc *sc = hdl; */

	switch (di->index) {
	case ZAUDIO_SPKR_LVL:
		di->type = AUDIO_MIXER_VALUE;
		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
		di->prev = AUDIO_MIXER_LAST;
		di->next = ZAUDIO_SPKR_MUTE;
		strlcpy(di->label.name, AudioNspeaker,
		    sizeof(di->label.name));
		strlcpy(di->un.v.units.name, AudioNvolume,
		    sizeof(di->un.v.units.name));
		di->un.v.num_channels = 1;
		break;
	case ZAUDIO_SPKR_MUTE:
		di->type = AUDIO_MIXER_ENUM;
		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
		di->prev = ZAUDIO_SPKR_LVL;
		di->next = AUDIO_MIXER_LAST;
		goto mute;
	case ZAUDIO_HP_LVL:
		di->type = AUDIO_MIXER_VALUE;
		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
		di->prev = AUDIO_MIXER_LAST;
		di->next = ZAUDIO_HP_MUTE;
		strlcpy(di->label.name, AudioNheadphone,
		    sizeof(di->label.name));
		di->un.v.num_channels = 1;
		strlcpy(di->un.v.units.name, AudioNvolume,
		    sizeof(di->un.v.units.name));
		break;
	case ZAUDIO_HP_MUTE:
		di->type = AUDIO_MIXER_ENUM;
		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
		di->prev = ZAUDIO_HP_LVL;
		di->next = AUDIO_MIXER_LAST;
mute:
		strlcpy(di->label.name, AudioNmute, sizeof(di->label.name));
		di->un.e.num_mem = 2;
		strlcpy(di->un.e.member[0].label.name, AudioNon,
		    sizeof(di->un.e.member[0].label.name));
		di->un.e.member[0].ord = 0;
		strlcpy(di->un.e.member[1].label.name, AudioNoff,
		    sizeof(di->un.e.member[1].label.name));
		di->un.e.member[1].ord = 1;
		break;
	case ZAUDIO_OUTPUT_CLASS:
		di->type = AUDIO_MIXER_CLASS;
		di->mixer_class = ZAUDIO_OUTPUT_CLASS;
		di->prev = AUDIO_MIXER_LAST;
		di->next = AUDIO_MIXER_LAST;
		strlcpy(di->label.name, AudioCoutputs,
		    sizeof(di->label.name));
		break;
	default:
		return ENXIO;
	}

	return 0;
}

int
zaudio_get_props(void *hdl)
{
	return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
}

void
zaudio_pintr(void *hdl)
{
	struct zaudio_softc *sc = hdl;

	mtx_enter(&audio_lock);
	sc->sc_pintr(sc->sc_parg);
	mtx_leave(&audio_lock);
}

/*
 * called by interrupt code-path, don't lock
 */
int
zaudio_start_output(void *hdl, void *block, int bsize, void (*intr)(void *),
    void *intrarg)
{
	struct zaudio_softc *sc = hdl;
	int err;

	/* Power up codec if we are not already playing. */
	if (!sc->sc_playing) {
		sc->sc_playing = 1;
		zaudio_play_setup(sc);
	}

	/* Start DMA via I2S */
	sc->sc_pintr = intr;
	sc->sc_parg = intrarg;
	err = pxa2x0_i2s_start_output(&sc->sc_i2s, block, bsize, zaudio_pintr, sc);
	if (err) {
		zaudio_standby(sc);
		sc->sc_playing = 0;
	}
	return err;
}

/*
 * called by interrupt code-path, don't lock
 */
int
zaudio_start_input(void *hdl, void *block, int bsize, void (*intr)(void *),
    void *intrarg)
{
	return ENXIO;
}


code:
/*	$OpenBSD: zaurus_flash.c,v 1.13 2014/07/12 18:44:43 tedu Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Samsung NAND flash controlled by some unspecified CPLD device.
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>

#include <dev/flashvar.h>
#include <dev/rndvar.h>

#include <machine/zaurus_var.h>

#include <arch/arm/xscale/pxa2x0var.h>

#define DEBUG
#ifdef DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif

/* CPLD register definitions */
#define CPLD_REG_ECCLPLB	0x00
#define CPLD_REG_ECCLPUB	0x04
#define CPLD_REG_ECCCP		0x08
#define CPLD_REG_ECCCNTR	0x0c
#define CPLD_REG_ECCCLRR	0x10
#define CPLD_REG_FLASHIO	0x14
#define CPLD_REG_FLASHCTL	0x18
#define  FLASHCTL_NCE0		(1<<0)
#define  FLASHCTL_CLE		(1<<1)
#define  FLASHCTL_ALE		(1<<2)
#define  FLASHCTL_NWP		(1<<3)
#define  FLASHCTL_NCE1		(1<<4)
#define  FLASHCTL_RYBY		(1<<5)
#define  FLASHCTL_NCE		(FLASHCTL_NCE0|FLASHCTL_NCE1)

/* CPLD register accesses */
#define CPLD_READ(sc, r)						\
	bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
#define CPLD_WRITE(sc, r, v)						\
	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
#define CPLD_SET(sc, r, v)						\
	CPLD_WRITE((sc), (r), CPLD_READ((sc), (r)) | (v))
#define CPLD_CLR(sc, r, v)						\
	CPLD_WRITE((sc), (r), CPLD_READ((sc), (r)) & ~(v))
#define CPLD_SETORCLR(sc, r, m, v)					\
	((v) ? CPLD_SET((sc), (r), (m)) : CPLD_CLR((sc), (r), (m)))

/* Offsets into OOB data. */
#define OOB_JFFS2_ECC0		0
#define OOB_JFFS2_ECC1		1
#define OOB_JFFS2_ECC2		2
#define OOB_JFFS2_ECC3		3
#define OOB_JFFS2_ECC4		6
#define OOB_JFFS2_ECC5		7
#define OOB_LOGADDR_0_LO	8
#define OOB_LOGADDR_0_HI	9
#define OOB_LOGADDR_1_LO	10
#define OOB_LOGADDR_1_HI	11
#define OOB_LOGADDR_2_LO	12
#define OOB_LOGADDR_2_HI	13

/*
 * Structure for managing logical blocks in a partition; allocated on
 * first use of each partition on a "safe" flash device.
 */
struct zflash_safe {
	dev_t			 sp_dev;
	u_long			 sp_pblks;	/* physical block count */
	u_long			 sp_lblks;	/* logical block count */
	u_int16_t		*sp_phyuse;	/* physical block usage */
	u_int			*sp_logmap;	/* logical to physical */
	u_int			 sp_pnext;	/* next physical block */
};

struct zflash_softc {
	struct flash_softc	 sc_flash;
	bus_space_tag_t		 sc_iot;
	bus_space_handle_t	 sc_ioh;
	int			 sc_ioobbadblk;
	int			 sc_ioobpostbadblk;
	struct zflash_safe	*sc_safe[MAXPARTITIONS];
};

int	 zflashmatch(struct device *, void *, void *);
void	 zflashattach(struct device *, struct device *, void *);
int	 zflashdetach(struct device *, int);

u_int8_t zflash_reg8_read(void *, int);
int	 zflash_regx_read_page(void *, caddr_t, caddr_t);
void	 zflash_reg8_write(void *, int, u_int8_t);
int	 zflash_regx_write_page(void *, caddr_t, caddr_t);
void	 zflash_default_disklabel(void *, dev_t, struct disklabel *);
int	 zflash_safe_strategy(void *, struct buf *);

int	 zflash_safe_start(struct zflash_softc *, dev_t);
void	 zflash_safe_stop(struct zflash_softc *, dev_t);

struct cfattach flash_pxaip_ca = {
	sizeof(struct zflash_softc), zflashmatch, zflashattach,
	zflashdetach
};

struct flash_ctl_tag zflash_ctl_tag = {
	zflash_reg8_read,
	zflash_regx_read_page,
	zflash_reg8_write,
	zflash_regx_write_page,
	zflash_default_disklabel,
	zflash_safe_strategy
};

int
zflashmatch(struct device *parent, void *match, void *aux)
{
	/* XXX call flashprobe(), yet to be implemented */
	return ZAURUS_ISC3000;
}

void
zflashattach(struct device *parent, struct device *self, void *aux)
{
	struct zflash_softc *sc = (struct zflash_softc *)self;
	struct pxaip_attach_args *pxa = aux;
	bus_addr_t addr = pxa->pxa_addr;
	bus_size_t size = pxa->pxa_size;

	sc->sc_iot = pxa->pxa_iot;

	if ((int)addr == -1 || (int)size == 0) {
		addr = 0x0c000000;
		size = 0x00001000;
	}

	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
		printf(": failed to map controller\n");
		return;
	}

	/* Disable and write-protect the chip. */
	CPLD_WRITE(sc, CPLD_REG_FLASHCTL, FLASHCTL_NCE);

	flashattach(&sc->sc_flash, &zflash_ctl_tag, sc);

	switch (sc->sc_flash.sc_flashdev->id) {
	case FLASH_DEVICE_SAMSUNG_K9F2808U0C: /* C3000 */
		sc->sc_ioobpostbadblk = 4;
		sc->sc_ioobbadblk = 5;
		break;
	case FLASH_DEVICE_SAMSUNG_K9F1G08U0A: /* C3100 */
		sc->sc_ioobpostbadblk = 4;
		sc->sc_ioobbadblk = 0;
		break;
	}
}

int
zflashdetach(struct device *self, int flags)
{
	struct zflash_softc *sc = (struct zflash_softc *)self;
	int part;

	for (part = 0; part < MAXPARTITIONS; part++)
		zflash_safe_stop(sc, part);

	return (flashdetach(self, flags));
}

u_int8_t
zflash_reg8_read(void *arg, int reg)
{
	struct zflash_softc *sc = arg;
	u_int8_t value;

	switch (reg) {
	case FLASH_REG_DATA:
		value = CPLD_READ(sc, CPLD_REG_FLASHIO);
		break;
	case FLASH_REG_READY:
		value = (CPLD_READ(sc, CPLD_REG_FLASHCTL) &
		    FLASHCTL_RYBY) != 0;
		break;
	default:
#ifdef DIAGNOSTIC
		printf("%s: read from pseudo-register %02x\n",
		    sc->sc_flash.sc_dev.dv_xname, reg);
#endif
		value = 0;
		break;
	}
	return value;
}

void
zflash_reg8_write(void *arg, int reg, u_int8_t value)
{
	struct zflash_softc *sc = arg;

	switch (reg) {
	case FLASH_REG_DATA:
	case FLASH_REG_COL:
	case FLASH_REG_ROW:
	case FLASH_REG_CMD:
		CPLD_WRITE(sc, CPLD_REG_FLASHIO, value);
		break;
	case FLASH_REG_ALE:
		CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_ALE, value);
		break;
	case FLASH_REG_CLE:
		CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_CLE, value);
		break;
	case FLASH_REG_CE:
		CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_NCE, !value);
		break;
	case FLASH_REG_WP:
		CPLD_SETORCLR(sc, CPLD_REG_FLASHCTL, FLASHCTL_NWP, !value);
		break;
#ifdef DIAGNOSTIC
	default:
		printf("%s: write to pseudo-register %02x\n",
		    sc->sc_flash.sc_dev.dv_xname, reg);
#endif
	}
}

int
zflash_regx_read_page(void *arg, caddr_t data, caddr_t oob)
{
	struct zflash_softc *sc = arg;

	if (oob == NULL || sc->sc_flash.sc_flashdev->pagesize != 512) {
		flash_reg8_read_page(&sc->sc_flash, data, oob);
		return 0;
	}

	flash_reg8_read_page(&sc->sc_flash, data, oob);

	oob[OOB_JFFS2_ECC0] = 0xff;
	oob[OOB_JFFS2_ECC1] = 0xff;
	oob[OOB_JFFS2_ECC2] = 0xff;
	oob[OOB_JFFS2_ECC3] = 0xff;
	oob[OOB_JFFS2_ECC4] = 0xff;
	oob[OOB_JFFS2_ECC5] = 0xff;
	return 0;
}

int
zflash_regx_write_page(void *arg, caddr_t data, caddr_t oob)
{
	struct zflash_softc *sc = arg;
	int i;

	if (oob == NULL || sc->sc_flash.sc_flashdev->pagesize != 512) {
		flash_reg8_write_page(&sc->sc_flash, data, oob);
		return 0;
	}

	if (oob[OOB_JFFS2_ECC0] != 0xff || oob[OOB_JFFS2_ECC1] != 0xff ||
	    oob[OOB_JFFS2_ECC2] != 0xff || oob[OOB_JFFS2_ECC3] != 0xff ||
	    oob[OOB_JFFS2_ECC4] != 0xff || oob[OOB_JFFS2_ECC5] != 0xff) {
#ifdef DIAGNOSTIC
		printf("%s: non-FF ECC bytes in OOB data\n",
		    sc->sc_flash.sc_dev.dv_xname);
#endif
		return EINVAL;
	}

	CPLD_WRITE(sc, CPLD_REG_ECCCLRR, 0x00);
	for (i = 0; i < sc->sc_flash.sc_flashdev->pagesize / 2; i++)
		flash_reg8_write(&sc->sc_flash, FLASH_REG_DATA, data[i]);

	oob[OOB_JFFS2_ECC0] = ~CPLD_READ(sc, CPLD_REG_ECCLPUB);
	oob[OOB_JFFS2_ECC1] = ~CPLD_READ(sc, CPLD_REG_ECCLPLB);
	oob[OOB_JFFS2_ECC2] = (~CPLD_READ(sc, CPLD_REG_ECCCP) << 2) | 0x03;

	if (CPLD_READ(sc, CPLD_REG_ECCCNTR) != 0) {
		printf("%s: ECC failed\n", sc->sc_flash.sc_dev.dv_xname);
		oob[OOB_JFFS2_ECC0] = 0xff;
		oob[OOB_JFFS2_ECC1] = 0xff;
		oob[OOB_JFFS2_ECC2] = 0xff;
		return EIO;
	}

	CPLD_WRITE(sc, CPLD_REG_ECCCLRR, 0x00);
	for (; i < sc->sc_flash.sc_flashdev->pagesize; i++)
		flash_reg8_write(&sc->sc_flash, FLASH_REG_DATA, data[i]);

	oob[OOB_JFFS2_ECC3] = ~CPLD_READ(sc, CPLD_REG_ECCLPUB);
	oob[OOB_JFFS2_ECC4] = ~CPLD_READ(sc, CPLD_REG_ECCLPLB);
	oob[OOB_JFFS2_ECC5] = (~CPLD_READ(sc, CPLD_REG_ECCCP) << 2) | 0x03;

	if (CPLD_READ(sc, CPLD_REG_ECCCNTR) != 0) {
		printf("%s: ECC failed\n", sc->sc_flash.sc_dev.dv_xname);
		oob[OOB_JFFS2_ECC0] = 0xff;
		oob[OOB_JFFS2_ECC1] = 0xff;
		oob[OOB_JFFS2_ECC2] = 0xff;
		oob[OOB_JFFS2_ECC3] = 0xff;
		oob[OOB_JFFS2_ECC4] = 0xff;
		oob[OOB_JFFS2_ECC5] = 0xff;
		return EIO;
	}

	for (i = 0; i < sc->sc_flash.sc_flashdev->oobsize; i++)
		flash_reg8_write(&sc->sc_flash, FLASH_REG_DATA, oob[i]);

	oob[OOB_JFFS2_ECC0] = 0xff;
	oob[OOB_JFFS2_ECC1] = 0xff;
	oob[OOB_JFFS2_ECC2] = 0xff;
	oob[OOB_JFFS2_ECC3] = 0xff;
	oob[OOB_JFFS2_ECC4] = 0xff;
	oob[OOB_JFFS2_ECC5] = 0xff;
	return 0;
}

/*
 * A default disklabel with only one RAW_PART spanning the whole
 * device is passed to us. We add the partitions besides RAW_PART.
 */
void
zflash_default_disklabel(void *arg, dev_t dev, struct disklabel *lp)
{
	struct zflash_softc *sc = arg;
	long bsize = sc->sc_flash.sc_flashdev->pagesize;

	switch (sc->sc_flash.sc_flashdev->id) {
	case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
		DL_SETPSIZE(&lp->d_partitions[8], 7*1024*1024 / bsize);
		DL_SETPSIZE(&lp->d_partitions[9], 5*1024*1024 / bsize);
		DL_SETPSIZE(&lp->d_partitions[10], 4*1024*1024 / bsize);
		break;
	case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
		DL_SETPSIZE(&lp->d_partitions[8], 7*1024*1024 / bsize);
		DL_SETPSIZE(&lp->d_partitions[9], 32*1024*1024 / bsize);
		DL_SETPSIZE(&lp->d_partitions[10], 89*1024*1024 / bsize);
		break;
	default:
		return;
	}

	/* The "smf" partition uses logical addressing. */
	DL_SETPOFFSET(&lp->d_partitions[8], 0);
	lp->d_partitions[8].p_fstype = FS_OTHER;

	/* The "root" partition uses physical addressing. */
	DL_SETPSIZE(&lp->d_partitions[9], DL_GETPSIZE(&lp->d_partitions[8]));
	lp->d_partitions[9].p_fstype = FS_OTHER;

	/* The "home" partition uses physical addressing. */
	DL_SETPOFFSET(&lp->d_partitions[10],
	    DL_GETPOFFSET(&lp->d_partitions[9]) + DL_GETPSIZE(&lp->d_partitions[9]));
	lp->d_partitions[10].p_fstype = FS_OTHER;
	lp->d_npartitions = MAXPARTITIONS;

	lp->d_version = 1;
	/* Re-calculate the checksum. */
	lp->d_checksum = dkcksum(lp);
}

/*
 * Sharp's access strategy for bad blocks management and wear-leveling.
 */

#define PHYUSE_STATUS(v)	((v) & 0x00ff)
#define  P_BADBLOCK		0x0000
#define  P_POSTBADBLOCK		0x00f0
#define  P_NORMALBLOCK		0x00ff
#define PHYUSE_WRITTEN(v)	((v) & 0xff00)
#define  P_DUST			0x0000
#define  P_LOGICAL		0x0100
#define  P_JFFS2		0x0300

void	  zflash_write_strategy(struct zflash_softc *, struct buf *,
    struct zflash_safe *, u_int, u_int);
u_int	  zflash_safe_next_block(struct zflash_safe *);

u_char	  zflash_oob_status_decode(u_char);
u_int16_t zflash_oob_status(struct zflash_softc *, u_char *);
u_int	  zflash_oob_logno(struct zflash_softc *, u_char *);
void	  zflash_oob_set_status(struct zflash_softc *, u_char *, u_int16_t);
void	  zflash_oob_set_logno(struct zflash_softc *, u_char *, u_int);

int
zflash_safe_strategy(void *arg, struct buf *bp)
{
	struct zflash_softc *sc = arg;
	struct zflash_safe *sp;
	u_int logno;
	u_int blkofs;
	u_int blkno;
	int error;
	int part;
	int i;

	/* Initialize logical blocks management on the fly. */
	/* XXX toss everything when the disklabel has changed. */
	if ((error = zflash_safe_start(sc, bp->b_dev)) != 0) {
		bp->b_error = error;
		bp->b_flags |= B_ERROR;
		return 0;
	}

	part = flashpart(bp->b_dev);
	sp = sc->sc_safe[part];

	logno = bp->b_blkno / (sc->sc_flash.sc_flashdev->blkpages *
	    sc->sc_flash.sc_flashdev->pagesize / DEV_BSIZE);
	blkofs = bp->b_blkno % (sc->sc_flash.sc_flashdev->blkpages *
	    sc->sc_flash.sc_flashdev->pagesize / DEV_BSIZE);

	/* If exactly at end of logical flash, return EOF, else error. */
	if (logno == sp->sp_lblks && blkofs == 0) {
		bp->b_resid = bp->b_bcount;
		return 0;
	} else if (logno >= sp->sp_lblks) {
		bp->b_error = EINVAL;
		bp->b_flags |= B_ERROR;
		return 0;
	}

	/* Writing is more complicated, so handle it separately. */
	if ((bp->b_flags & B_READ) == 0) {
		flash_chip_enable(&sc->sc_flash);
		zflash_write_strategy(sc, bp, sp, logno, blkofs);
		flash_chip_disable(&sc->sc_flash);
		return 0;
	}

	/* Get the physical flash block number for this logical one. */
	blkno = sp->sp_logmap[logno];

	/* Unused logical blocks read as all 0xff. */
	if ((bp->b_flags & B_READ) != 0 && blkno == UINT_MAX) {
		for (i = 0; i < sc->sc_flash.sc_flashdev->pagesize; i++)
			((u_char *)bp->b_data)[i] = 0xff;
		bp->b_resid = bp->b_bcount -
		    sc->sc_flash.sc_flashdev->pagesize;
		return 0;
	}

	/* Update the block number in the buffer with the physical one. */
	bp->b_blkno = blkno * (sc->sc_flash.sc_flashdev->blkpages *
	    sc->sc_flash.sc_flashdev->pagesize / DEV_BSIZE) + blkofs;

	/* Process the modified transfer buffer normally. */
	return 1;
}

void
zflash_write_strategy(struct zflash_softc *sc, struct buf *bp,
    struct zflash_safe *sp, u_int logno, u_int logofs)
{
	size_t	bufsize;
	u_char *buf = NULL;
	size_t	oobsize;
	u_char *oob = NULL;
	u_int	oblkno;
	u_int	nblkno;
	int	error;

	/* Not efficient, but we always transfer one page for now. */
	if (bp->b_bcount < sc->sc_flash.sc_flashdev->pagesize) {
		bp->b_error = EINVAL;
		goto bad;
	}

	/* Allocate a temporary buffer for one flash block. */
	bufsize = sc->sc_flash.sc_flashdev->blkpages *
	    sc->sc_flash.sc_flashdev->pagesize;
	buf = (u_char *)malloc(bufsize, M_DEVBUF, M_NOWAIT);
	if (buf == NULL) {
		bp->b_error = ENOMEM;
		goto bad;
	}

	/* Allocate a temporary buffer for one spare area. */
	oobsize = sc->sc_flash.sc_flashdev->oobsize;
	oob = (u_char *)malloc(oobsize, M_DEVBUF, M_NOWAIT);
	if (oob == NULL) {
		bp->b_error = ENOMEM;
		goto bad;
	}

	/* Read the old logical block into the temporary buffer. */
	oblkno = sp->sp_logmap[logno];
	if (oblkno != UINT_MAX) {
		error = flash_chip_read_block(&sc->sc_flash, oblkno, buf);
		if (error != 0) {
			bp->b_error = error;
			goto bad;
		}
	} else
		/* Unused logical blocks read as all 0xff. */
		memset(buf, 0xff, bufsize);

	/* Transfer the page into the logical block buffer. */
	bcopy(bp->b_data, buf + logofs * sc->sc_flash.sc_flashdev->pagesize,
	    sc->sc_flash.sc_flashdev->pagesize);

	/* Generate OOB data for the spare area of this logical block. */
	memset(oob, 0xff, oobsize);
	zflash_oob_set_status(sc, oob, P_NORMALBLOCK);
	zflash_oob_set_logno(sc, oob, logno);

	while (1) {
		/* Search for a free physical block. */
		nblkno = zflash_safe_next_block(sp);
		if (nblkno == UINT_MAX) {
			printf("%s: no spare block, giving up on logical"
			    " block %u\n", sc->sc_flash.sc_dev.dv_xname,
			    logno);
			bp->b_error = ENOSPC;
			goto bad;
		}

#if 0
		DPRINTF(("%s: moving logical block %u from physical %u to %u\n",
		    sc->sc_flash.sc_dev.dv_xname, logno, oblkno, nblkno));
#endif

		/* Erase the free physical block. */
		if (flash_chip_erase_block(&sc->sc_flash, nblkno) != 0) {
			printf("%s: can't erase block %u, retrying\n",
			    sc->sc_flash.sc_dev.dv_xname, nblkno);
			sp->sp_phyuse[nblkno] = P_POSTBADBLOCK | P_DUST;
			continue;
		}

		/* Write the logical block to the free physical block. */
		if (flash_chip_write_block(&sc->sc_flash, nblkno, buf, oob)) {
			printf("%s: can't write block %u, retrying\n",
			    sc->sc_flash.sc_dev.dv_xname, nblkno);
			goto trynext;
		}

		/* Yeah, we re-wrote that logical block! */
		break;
	trynext:
		sp->sp_phyuse[nblkno] = P_POSTBADBLOCK | P_DUST;
		(void)flash_chip_erase_block(&sc->sc_flash, nblkno);
	}

	/* Map the new physical block. */
	sp->sp_logmap[logno] = nblkno;
	sp->sp_phyuse[nblkno] = PHYUSE_STATUS(sp->sp_phyuse[nblkno])
	    | P_LOGICAL;

	/* Erase the old physical block. */
	if (oblkno != UINT_MAX) {
		sp->sp_phyuse[oblkno] = PHYUSE_STATUS(sp->sp_phyuse[oblkno])
		    | P_DUST;
		error = flash_chip_erase_block(&sc->sc_flash, oblkno);
		if (error != 0) {
			printf("%s: can't erase old block %u\n",
			    sc->sc_flash.sc_dev.dv_xname, oblkno);
			bp->b_error = error;
			goto bad;
		}
	}

	bp->b_resid = bp->b_bcount - sc->sc_flash.sc_flashdev->pagesize;
	free(oob, M_DEVBUF, 0);
	free(buf, M_DEVBUF, 0);
	return;
bad:
	bp->b_flags |= B_ERROR;
	if (oob != NULL)
		free(oob, M_DEVBUF, 0);
	if (buf != NULL)
		free(buf, M_DEVBUF, 0);
}

int
zflash_safe_start(struct zflash_softc *sc, dev_t dev)
{
	u_char oob[FLASH_MAXOOBSIZE];
	struct disklabel *lp = sc->sc_flash.sc_dk.dk_label;
	struct zflash_safe *sp;
	u_int16_t *phyuse;
	u_int *logmap;
	u_int blksect;
	u_int blkno;
	u_int logno;
	u_int unusable;
	int part;

	part = flashpart(dev);
	if (sc->sc_safe[part] != NULL)
		return 0;

	/* We can only handle so much OOB data here. */
	if (sc->sc_flash.sc_flashdev->oobsize > FLASH_MAXOOBSIZE)
		return EIO;

	/* Safe partitions must start on a flash block boundary. */
	blksect = (sc->sc_flash.sc_flashdev->blkpages *
	    sc->sc_flash.sc_flashdev->pagesize) / lp->d_secsize;
	if (DL_GETPOFFSET(&lp->d_partitions[part]) % blksect)
		return EIO;

	sp = malloc(sizeof(*sp), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (sp == NULL)
		return ENOMEM;

	sp->sp_dev = dev;

	sp->sp_pblks = DL_GETPSIZE(&lp->d_partitions[part]) / blksect;
	sp->sp_lblks = sp->sp_pblks;

	/* Try to reserve a number of spare physical blocks. */
	switch (sc->sc_flash.sc_flashdev->id) {
	case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
		sp->sp_lblks -= 24; /* C3000 */
		break;
	case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
		sp->sp_lblks -= 4;  /* C3100 */
		break;
	}

	DPRINTF(("pblks %lu lblks %lu\n", sp->sp_pblks, sp->sp_lblks));

	/* Next physical block to use; randomize for wear-leveling. */
	sp->sp_pnext = arc4random_uniform(sp->sp_pblks);

	/* Allocate physical block usage map. */
	phyuse = (u_int16_t *)malloc(sp->sp_pblks * sizeof(u_int16_t),
	    M_DEVBUF, M_NOWAIT);
	if (phyuse == NULL) {
		free(sp, M_DEVBUF, 0);
		return ENOMEM;
	}
	sp->sp_phyuse = phyuse;

	/* Allocate logical to physical block map. */
	logmap = (u_int *)malloc(sp->sp_lblks * sizeof(u_int),
	    M_DEVBUF, M_NOWAIT);
	if (logmap == NULL) {
		free(phyuse, M_DEVBUF, 0);
		free(sp, M_DEVBUF, 0);
		return ENOMEM;
	}
	sp->sp_logmap = logmap;

	/* Initialize the physical and logical block maps. */
	for (blkno = 0; blkno < sp->sp_pblks; blkno++)
		phyuse[blkno] = P_BADBLOCK | P_DUST;
	for (blkno = 0; blkno < sp->sp_lblks; blkno++)
		logmap[blkno] = UINT_MAX;

	/* Update physical block usage map with real data. */
	unusable = 0;
	flash_chip_enable(&sc->sc_flash);
	for (blkno = 0; blkno < sp->sp_pblks; blkno++) {
		long pageno;

		pageno = blkno * sc->sc_flash.sc_flashdev->blkpages;
		if (flash_chip_read_oob(&sc->sc_flash, pageno, oob) != 0) {
			DPRINTF(("blkno %u: can't read oob data\n", blkno));
			phyuse[blkno] = P_POSTBADBLOCK | P_DUST;
			unusable++;
			continue;
		}

		phyuse[blkno] = zflash_oob_status(sc, oob);
		if (PHYUSE_STATUS(phyuse[blkno]) != P_NORMALBLOCK) {
			DPRINTF(("blkno %u: badblock status %x\n", blkno,
			    PHYUSE_STATUS(phyuse[blkno])));
			phyuse[blkno] |= P_DUST;
			unusable++;
			continue;
		}

		logno = zflash_oob_logno(sc, oob);
		if (logno == UINT_MAX) {
			DPRINTF(("blkno %u: can't read logno\n", blkno));
			phyuse[blkno] |= P_JFFS2;
			unusable++;
			continue;
		}

		if (logno == USHRT_MAX) {
			phyuse[blkno] |= P_DUST;
			/* Block is usable and available. */
			continue;
		}

		if (logno >= sp->sp_lblks) {
			DPRINTF(("blkno %u: logno %u too big\n", blkno,
			    logno));
			phyuse[blkno] |= P_JFFS2;
			unusable++;
			continue;
		}

		if (logmap[logno] == UINT_MAX) {
			phyuse[blkno] |= P_LOGICAL;
			logmap[logno] = blkno;
		} else {
			/* Duplicate logical block! */
			DPRINTF(("blkno %u: duplicate logno %u\n", blkno,
			    logno));
			phyuse[blkno] |= P_DUST;
		}
	}
	flash_chip_disable(&sc->sc_flash);

	if (unusable > 0)
		printf("%s: %u unusable blocks\n",
		    sc->sc_flash.sc_dev.dv_xname, unusable);

	sc->sc_safe[part] = sp;
	return 0;
}

void
zflash_safe_stop(struct zflash_softc *sc, dev_t dev)
{
	struct zflash_safe *sp;
	int part;

	part = flashpart(dev);
	if (sc->sc_safe[part] == NULL)
		return;

	sp = sc->sc_safe[part];
	free(sp->sp_phyuse, M_DEVBUF, 0);
	free(sp->sp_logmap, M_DEVBUF, 0);
	free(sp, M_DEVBUF, 0);
	sc->sc_safe[part] = NULL;
}

u_int
zflash_safe_next_block(struct zflash_safe *sp)
{
	u_int blkno;

	for (blkno = sp->sp_pnext; blkno < sp->sp_pblks; blkno++)
		if (sp->sp_phyuse[blkno] == (P_NORMALBLOCK|P_DUST)) {
			sp->sp_pnext = blkno + 1;
			return blkno;
		}

	for (blkno = 0; blkno < sp->sp_pnext; blkno++)
		if (sp->sp_phyuse[blkno] == (P_NORMALBLOCK|P_DUST)) {
			sp->sp_pnext = blkno + 1;
			return blkno;
		}

	return UINT_MAX;
}

/*
 * Correct single bit errors in the block's status byte.
 */
u_char
zflash_oob_status_decode(u_char status)
{
	u_char bit;
	int count;

	/* Speed-up. */
	if (status == 0xff)
		return 0xff;

	/* Count the number of bits set in the byte. */
	for (count = 0, bit = 0x01; bit != 0x00; bit <<= 1)
		if ((status & bit) != 0)
			count++;

	return (count > 6) ? 0xff : 0x00;
}

/*
 * Decode the block's status byte into a value for the phyuse map.
 */
u_int16_t
zflash_oob_status(struct zflash_softc *sc, u_char *oob)
{
	u_char status;

	status = zflash_oob_status_decode(oob[sc->sc_ioobbadblk]);
	if (status != 0xff)
		return P_BADBLOCK;

	status = zflash_oob_status_decode(oob[sc->sc_ioobpostbadblk]);
	if (status != 0xff)
		return P_POSTBADBLOCK;

	return P_NORMALBLOCK;
}

/*
 * Extract the 16-bit logical block number corresponding to a physical
 * block from the physical block's OOB data.
 */
u_int
zflash_oob_logno(struct zflash_softc *sc, u_char *oob)
{
	int idx_lo, idx_hi;
	u_int16_t word;
	u_int16_t bit;
	int parity;

	/* Find a matching pair of high and low bytes. */
	if (oob[OOB_LOGADDR_0_LO] == oob[OOB_LOGADDR_1_LO] &&
	    oob[OOB_LOGADDR_0_HI] == oob[OOB_LOGADDR_1_HI]) {
		idx_lo = OOB_LOGADDR_0_LO;
		idx_hi = OOB_LOGADDR_0_HI;
	} else if (oob[OOB_LOGADDR_1_LO] == oob[OOB_LOGADDR_2_LO] &&
	    oob[OOB_LOGADDR_1_HI] == oob[OOB_LOGADDR_2_HI]) {
		idx_lo = OOB_LOGADDR_1_LO;
		idx_hi = OOB_LOGADDR_1_HI;
	} else if (oob[OOB_LOGADDR_2_LO] == oob[OOB_LOGADDR_0_LO] &&
	    oob[OOB_LOGADDR_2_HI] == oob[OOB_LOGADDR_0_HI]) {
		idx_lo = OOB_LOGADDR_2_LO;
		idx_hi = OOB_LOGADDR_2_HI;
	} else
		/* Block's OOB data may be invalid. */
		return UINT_MAX;

	word = ((u_int16_t)oob[idx_lo] << 0) |
	    ((u_int16_t)oob[idx_hi] << 8);

	/* Check for parity error in the logical block number. */
	for (parity = 0, bit = 0x0001; bit != 0x0000; bit <<= 1)
		if ((word & bit) != 0)
			parity++;
	if ((parity & 1) != 0)
		return UINT_MAX;

	/* No logical block number assigned to this block? */
	if (word == USHRT_MAX)
		return word;

	/* Return the validated logical block number. */
	return (word & 0x07fe) >> 1;
}

void
zflash_oob_set_status(struct zflash_softc *sc, u_char *oob, u_int16_t phyuse)
{
	switch (PHYUSE_STATUS(phyuse)) {
	case P_NORMALBLOCK:
		oob[sc->sc_ioobbadblk] = 0xff;
		oob[sc->sc_ioobpostbadblk] = 0xff;
		break;
	case P_BADBLOCK:
		oob[sc->sc_ioobbadblk] = 0x00;
		oob[sc->sc_ioobpostbadblk] = 0x00;
		break;
	case P_POSTBADBLOCK:
		oob[sc->sc_ioobbadblk] = 0xff;
		oob[sc->sc_ioobpostbadblk] = 0x00;
		break;
	}
}

void
zflash_oob_set_logno(struct zflash_softc *sc, u_char *oob, u_int logno)
{
	u_int16_t word;
	u_int16_t bit;
	u_char lo;
	u_char hi;
	int parity = 0;

	/* Why do we set the most significant bit? */
	word = ((logno & 0x03ff) << 1) | 0x1000;

	/* Calculate the parity. */
	for (bit = 0x0001; bit != 0x0000; bit <<= 1)
		if ((word & bit) != 0)
			parity++;
	if ((parity & 1) != 0)
		word |= 0x0001;

	lo = word & 0x00ff;
	hi = (word & 0xff00) >> 8;

	oob[OOB_LOGADDR_0_LO] = lo;
	oob[OOB_LOGADDR_0_HI] = hi;
	oob[OOB_LOGADDR_1_LO] = lo;
	oob[OOB_LOGADDR_1_HI] = hi;
	oob[OOB_LOGADDR_2_LO] = lo;
	oob[OOB_LOGADDR_2_HI] = hi;
}


code:
/* $OpenBSD: zaurus_kbd.c,v 1.36 2014/07/11 08:18:31 guenther Exp $ */
/*
 * Copyright (c) 2005 Dale Rahn <drahn@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/signalvar.h>

#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0_gpio.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>

#include <zaurus/dev/zaurus_kbdmap.h>

#include "apm.h"

const int
gpio_sense_pins_c3000[] = {
	12,
	17,
	91,
	34,
	36,
	38,
	39,
	-1
};

const int
gpio_strobe_pins_c3000[] = {
	88,
	23,
	24,
	25,
	26,
	27,
	52,
	103,
	107,
	-1,
	108,
	114
};

const int stuck_keys[] = {
	7,
	15,
	23,
	31
};

#define REP_DELAY1 400
#define REP_DELAYN 100

struct zkbd_softc {
	struct device sc_dev;

	const int *sc_sense_array;
	const int *sc_strobe_array;
	int sc_nsense;
	int sc_nstrobe;

	short sc_onkey_pin;
	short sc_sync_pin;
	short sc_swa_pin;
	short sc_swb_pin;
	char *sc_okeystate;
	char *sc_keystate;
	char sc_hinge;		/* 0=open, 1=nonsense, 2=backwards, 3=closed */
	char sc_maxkbdcol;

	struct timeout sc_roll_to;

	/* console stuff */
	int sc_polling;
	int sc_pollUD;
	int sc_pollkey;

	/* wskbd bits */
	struct device   *sc_wskbddev;
	int sc_rawkbd;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	const char *sc_xt_keymap;
#define MAXKEYS 20
#endif
};

struct zkbd_softc *zkbd_dev; /* XXX */

int zkbd_match(struct device *, void *, void *);
void zkbd_attach(struct device *, struct device *, void *);
int zkbd_activate(struct device *, int);

int zkbd_irq(void *v);
void zkbd_poll(void *v);
int zkbd_on(void *v);
int zkbd_sync(void *v);
int zkbd_hinge(void *v);

int zkbd_modstate;

struct cfattach zkbd_ca = {
	sizeof(struct zkbd_softc), zkbd_match, zkbd_attach, NULL,
	zkbd_activate
};

struct cfdriver zkbd_cd = {
	NULL, "zkbd", DV_DULL
};

int zkbd_enable(void *, int);
void zkbd_set_leds(void *, int);
int zkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);

struct wskbd_accessops zkbd_accessops = {
	zkbd_enable,
	zkbd_set_leds,
	zkbd_ioctl,
};

void zkbd_cngetc(void *, u_int *, int *);
void zkbd_cnpollc(void *, int);

struct wskbd_consops zkbd_consops = {
        zkbd_cngetc,
        zkbd_cnpollc,
};

struct wskbd_mapdata zkbd_keymapdata = {
        zkbd_keydesctab,
        KB_US | KB_DEFAULT,
};

int
zkbd_match(struct device *parent, void *cf, void *aux)
{
	return 1;
}

void
zkbd_attach(struct device *parent, struct device *self, void *aux)
{
	struct zkbd_softc *sc = (struct zkbd_softc *)self;
	struct wskbddev_attach_args a;
	int pin, i;
	extern int glass_console;

	zkbd_dev = sc;
	sc->sc_polling = 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	sc->sc_rawkbd = 0;
#endif
	/* Determine which system we are - XXX */

	if (1 /* C3000 */) {
		sc->sc_sense_array = gpio_sense_pins_c3000;
		sc->sc_strobe_array = gpio_strobe_pins_c3000;
		sc->sc_nsense = sizeof(gpio_sense_pins_c3000)/sizeof(int);
		sc->sc_nstrobe = sizeof(gpio_strobe_pins_c3000)/sizeof(int);
		sc->sc_maxkbdcol = 10;
		sc->sc_onkey_pin = 95;
		sc->sc_sync_pin = 16;
		sc->sc_swa_pin = 97;
		sc->sc_swb_pin = 96;
#ifdef WSDISPLAY_COMPAT_RAWKBD
		sc->sc_xt_keymap = xt_keymap;
#endif
	} /* XXX */

	sc->sc_okeystate = malloc(sc->sc_nsense * sc->sc_nstrobe,
	    M_DEVBUF, M_NOWAIT | M_ZERO);

	sc->sc_keystate = malloc(sc->sc_nsense * sc->sc_nstrobe,
	    M_DEVBUF, M_NOWAIT | M_ZERO);

	/* set all the strobe bits */
	for (i = 0; i < sc->sc_nstrobe; i++) {
		pin = sc->sc_strobe_array[i];
		if (pin == -1) {
			continue;
		}
		pxa2x0_gpio_set_function(pin, GPIO_SET|GPIO_OUT);
	}
	/* set all the sense bits */
	for (i = 0; i < sc->sc_nsense; i++) {
		pin = sc->sc_sense_array[i];
		if (pin == -1) {
			continue;
		}
		pxa2x0_gpio_set_function(pin, GPIO_IN);
		pxa2x0_gpio_intr_establish(pin, IST_EDGE_BOTH, IPL_TTY,
		    zkbd_irq, sc, sc->sc_dev.dv_xname);
	}
	pxa2x0_gpio_intr_establish(sc->sc_onkey_pin, IST_EDGE_BOTH, IPL_TTY,
	    zkbd_on, sc, sc->sc_dev.dv_xname);
	pxa2x0_gpio_intr_establish(sc->sc_sync_pin, IST_EDGE_RISING, IPL_TTY,
	    zkbd_sync, sc, sc->sc_dev.dv_xname);
	pxa2x0_gpio_intr_establish(sc->sc_swa_pin, IST_EDGE_BOTH, IPL_TTY,
	    zkbd_hinge, sc, sc->sc_dev.dv_xname);
	pxa2x0_gpio_intr_establish(sc->sc_swb_pin, IST_EDGE_BOTH, IPL_TTY,
	    zkbd_hinge, sc, sc->sc_dev.dv_xname);

	if (glass_console) {
		wskbd_cnattach(&zkbd_consops, sc, &zkbd_keymapdata);
		a.console = 1;
	} else {
		a.console = 0;
	}

	a.keymap = &zkbd_keymapdata;
	a.accessops = &zkbd_accessops;
	a.accesscookie = sc;

	printf("\n");

	zkbd_hinge(sc);		/* to initialize sc_hinge */

	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);

	timeout_set(&(sc->sc_roll_to), zkbd_poll, sc);
}

/* XXX only deal with keys that can be pressed when display is open? */
/* XXX are some not in the array? */
/* handle keypress interrupt */
int
zkbd_irq(void *v)
{
	zkbd_poll(v);

	return 1;
}

void
zkbd_poll(void *v)
{
	struct zkbd_softc *sc = v;
	int i, j, col, pin, type, keysdown = 0, s;
	int stuck;
	int keystate;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	int ncbuf = 0, c;
	char cbuf[MAXKEYS * 2];
#endif

	s = spltty();

	/* discharge all */
	for (i = 0; i < sc->sc_nstrobe; i++) {
		pin = sc->sc_strobe_array[i];
		if (pin != -1) {
			pxa2x0_gpio_clear_bit(pin);
			pxa2x0_gpio_set_dir(pin, GPIO_IN);
		}
	}

	delay (10);
	for(col = 0; col < sc->sc_nstrobe; col++) {
		if (sc->sc_strobe_array[i] == -1)
			continue;

		pin = sc->sc_strobe_array[col];

		/* activate_col */
		pxa2x0_gpio_set_bit(pin);
		pxa2x0_gpio_set_dir(pin, GPIO_OUT);

		/* wait activate delay */
		delay(10);

		/* read row */
		for (i = 0; i < sc->sc_nsense; i++) {
			int bit;

			if (sc->sc_sense_array[i] == -1)
				continue;

			bit = pxa2x0_gpio_get_bit(sc->sc_sense_array[i]);
			if (bit && sc->sc_hinge && col < sc->sc_maxkbdcol)
				continue;
			sc->sc_keystate[i + (col * sc->sc_nsense)] = bit;
		}

		/* reset_col */
		pxa2x0_gpio_set_dir(pin, GPIO_IN);
		/* wait discharge delay */
		delay(10);
	}
	/* charge all */
	for (i = 0; i < sc->sc_nstrobe; i++) {
		pin = sc->sc_strobe_array[i];
		if (pin != -1) {
			pxa2x0_gpio_set_bit(pin);
			pxa2x0_gpio_set_dir(pin, GPIO_OUT);
		}
	}

	/* force the irqs to clear as we have just played with them. */
	for (i = 0; i < sc->sc_nsense; i++)
		if (sc->sc_sense_array[i] != -1)
			pxa2x0_gpio_clear_intr(sc->sc_sense_array[i]);

	/* process after resetting interrupt */

	zkbd_modstate = (
		(sc->sc_keystate[84] ? (1 << 0) : 0) | /* shift */
		(sc->sc_keystate[93] ? (1 << 1) : 0) | /* Fn */
		(sc->sc_keystate[14] ? (1 << 2) : 0)); /* 'alt' */

	for (i = 0; i < (sc->sc_nsense * sc->sc_nstrobe); i++) {
		stuck = 0;
		/* extend  xt_keymap to do this faster. */
		/* ignore 'stuck' keys' */
		for (j = 0; j < sizeof(stuck_keys)/sizeof(stuck_keys[0]); j++) {
			if (stuck_keys[j] == i) {
				stuck = 1 ;
				break;
			}
		}
		if (stuck)
			continue;
		keystate = sc->sc_keystate[i];

		keysdown |= keystate; /* if any keys held */

#ifdef WSDISPLAY_COMPAT_RAWKBD
		if (sc->sc_polling == 0 && sc->sc_rawkbd) {
			if ((keystate) || (sc->sc_okeystate[i] != keystate)) {
				c = sc->sc_xt_keymap[i];
				if (c & 0x80) {
					cbuf[ncbuf++] = 0xe0;
				}
				cbuf[ncbuf] = c & 0x7f;
				if (!keystate)
					cbuf[ncbuf] |= 0x80;
				ncbuf++;
				sc->sc_okeystate[i] = keystate;
			}
		}
#endif

		if ((!sc->sc_rawkbd) && (sc->sc_okeystate[i] != keystate)) {

			type = keystate ? WSCONS_EVENT_KEY_DOWN :
			    WSCONS_EVENT_KEY_UP;

			if (sc->sc_polling) {
				sc->sc_pollkey = i;
				sc->sc_pollUD = type;
			} else {
				wskbd_input(sc->sc_wskbddev, type, i);
			}

			sc->sc_okeystate[i] = keystate;
		}
	}

#ifdef WSDISPLAY_COMPAT_RAWKBD
	if (sc->sc_polling == 0 && sc->sc_rawkbd)
		wskbd_rawinput(sc->sc_wskbddev, cbuf, ncbuf);
#endif
	if (keysdown)
		timeout_add(&(sc->sc_roll_to), hz * REP_DELAYN / 1000 / 2);
	else
		timeout_del(&(sc->sc_roll_to)); /* always cancel? */

	splx(s);
}

#if NAPM > 0
extern	int allowpowerdown;
extern	int apm_suspends;
static	int zkbdondown;				/* on key is pressed */
static	struct timeval zkbdontv = { 0, 0 };	/* last on key event */
const	struct timeval zkbdhalttv = { 3, 0 };	/*  3s for safe shutdown */
const	struct timeval zkbdsleeptv = { 0, 250000 };	/* .25s for suspend */
extern	int lid_suspend;
#endif

int
zkbd_on(void *v)
{
#if NAPM > 0
	struct zkbd_softc *sc = v;
	int down = pxa2x0_gpio_get_bit(sc->sc_onkey_pin) ? 1 : 0;

	/*
	 * Change run mode depending on how long the key is held down.
	 * Ignore the key if it gets pressed while the lid is closed.
	 *
	 * Keys can bounce and we have to work around missed interrupts.
	 * Only the second edge is detected upon exit from sleep mode.
	 */
	if (down) {
		if (sc->sc_hinge == 3) {
			zkbdondown = 0;
		} else {
			microuptime(&zkbdontv);
			zkbdondown = 1;
		}
	} else if (zkbdondown) {
		if (ratecheck(&zkbdontv, &zkbdhalttv)) {
			if (allowpowerdown == 1) {
				allowpowerdown = 0;
				prsignal(initprocess, SIGUSR2);
			}
		} else if (ratecheck(&zkbdontv, &zkbdsleeptv)) {
			apm_suspends++;
		}
		zkbdondown = 0;
	}
#endif
	return 1;
}

int
zkbd_sync(void *v)
{
	return 1;
}

int
zkbd_hinge(void *v)
{
	struct zkbd_softc *sc = v;
	int a = pxa2x0_gpio_get_bit(sc->sc_swa_pin) ? 1 : 0;
	int b = pxa2x0_gpio_get_bit(sc->sc_swb_pin) ? 2 : 0;
	extern void lcd_blank(int);

	sc->sc_hinge = a | b;

	if (sc->sc_hinge == 3) {
#if NAPM > 0
		if (lid_suspend)
			apm_suspends++;
#endif
		lcd_blank(1);
	} else
		lcd_blank(0);

	return 1;
}

int
zkbd_enable(void *v, int on)
{
        return 0;
}

void
zkbd_set_leds(void *v, int on)
{
}

int
zkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
#ifdef WSDISPLAY_COMPAT_RAWKBD
	struct zkbd_softc *sc = v;
#endif

	switch (cmd) {

	case WSKBDIO_GTYPE:
		*(int *)data = WSKBD_TYPE_ZAURUS;
		return 0;
	case WSKBDIO_SETLEDS:
		return 0;
	case WSKBDIO_GETLEDS:
		*(int *)data = 0;
		return 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	case WSKBDIO_SETMODE:
		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
		return (0);
#endif

	}
	/* kbdioctl(...); */

	return -1;
}

/* implement polling for zaurus_kbd */
void
zkbd_cngetc(void *v, u_int *type, int *data)
{
	struct zkbd_softc *sc = zkbd_dev;
	sc->sc_pollkey = -1;
	sc->sc_pollUD = -1;
	sc->sc_polling = 1;
	while (sc->sc_pollkey == -1) {
		zkbd_poll(zkbd_dev);
		DELAY(10000);	/* XXX */
	}
	sc->sc_polling = 0;
	*data = sc->sc_pollkey;
	*type = sc->sc_pollUD;
}

void
zkbd_cnpollc(void *v, int on)
{
}

int
zkbd_activate(struct device *self, int act)
{
	switch (act) {
	case DVACT_SUSPEND:
		zkbd_hinge(self);
		break;
	case DVACT_RESUME:
		zkbd_hinge(self);
		break;
	}
	return 0;
}