code:
/*	$OpenBSD: a1xintc.c,v 1.6 2015/05/20 03:49:23 jsg Exp $	*/
/*
 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
 * Copyright (c) 2013 Artturi Alm
 *
 * 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/queue.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/evcount.h>

#include <machine/bus.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>
#include <armv7/sunxi/a1xintc.h>

#ifdef DEBUG_INTC
#define DPRINTF(x)	do { if (a1xintcdebug) printf x; } while (0)
#define DPRINTFN(n,x)	do { if (a1xintcdebug>(n)) printf x; } while (0)
int	a1xintcdebug = 10;
char *ipl_strtbl[NIPL] = {
	"IPL_NONE",
	"IPL_SOFT",
	"IPL_SOFTCLOCK",
	"IPL_SOFTNET",
	"IPL_SOFTTTY",
	"IPL_BIO|IPL_USB",
	"IPL_NET",
	"IPL_TTY",
	"IPL_VM",
	"IPL_AUDIO",
	"IPL_CLOCK",
	"IPL_STATCLOCK",
	"IPL_SCHED|IPL_HIGH"
};
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif

#define NIRQ			96
#define NBANKS			3
#define NIRQPRIOREGS		5

/* registers */
#define INTC_VECTOR_REG		0x00
#define INTC_BASE_ADR_REG	0x04
#define INTC_PROTECTION_REG	0x08
#define INTC_NMI_CTRL_REG	0x0c

#define INTC_IRQ_PENDING_REG0	0x10
#define INTC_IRQ_PENDING_REG1	0x14
#define INTC_IRQ_PENDING_REG2	0x18

#define INTC_SELECT_REG0	0x30
#define INTC_SELECT_REG1	0x34
#define INTC_SELECT_REG2	0x38

#define INTC_ENABLE_REG0	0x40
#define INTC_ENABLE_REG1	0x44
#define INTC_ENABLE_REG2	0x48

#define INTC_MASK_REG0		0x50
#define INTC_MASK_REG1		0x54
#define INTC_MASK_REG2		0x58

#define INTC_RESP_REG0		0x60
#define INTC_RESP_REG1		0x64
#define INTC_RESP_REG2		0x68

#define INTC_PRIO_REG0		0x80
#define INTC_PRIO_REG1		0x84
#define INTC_PRIO_REG2		0x88
#define INTC_PRIO_REG3		0x8c
#define INTC_PRIO_REG4		0x90

#define INTC_IRQ_PENDING_REG(_b)	(0x10 + ((_b) * 4))
#define INTC_FIQ_PENDING_REG(_b)	(0x20 + ((_b) * 4))
#define INTC_SELECT_REG(_b)		(0x30 + ((_b) * 4))
#define INTC_ENABLE_REG(_b)		(0x40 + ((_b) * 4))
#define INTC_MASK_REG(_b)		(0x50 + ((_b) * 4))
#define INTC_RESP_REG(_b)		(0x60 + ((_b) * 4))
#define INTC_PRIO_REG(_b)		(0x80 + ((_b) * 4))

#define IRQ2REG32(i)		(((i) >> 5) & 0x3)
#define IRQ2BIT32(i)		((i) & 0x1f)

#define IRQ2REG16(i)		(((i) >> 4) & 0x5)
#define IRQ2BIT16(i)		(((i) & 0x0f) * 2)

#define INTC_IRQ_HIPRIO		0x3
#define INTC_IRQ_ENABLED	0x2
#define INTC_IRQ_DISABLED	0x1
#define INTC_IRQ_LOWPRIO	0x0
#define INTC_PRIOCLEAR(i)	(~(INTC_IRQ_HIPRIO << IRQ2BIT16((i))))
#define INTC_PRIOENABLE(i)	(INTC_IRQ_ENABLED << IRQ2BIT16((i)))
#define INTC_PRIOHI(i)		(INTC_IRQ_HIPRIO << IRQ2BIT16((i)))

struct intrhand {
	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
	int (*ih_func)(void *);		/* handler */
	void *ih_arg;			/* arg for handler */
	int ih_ipl;			/* IPL_* */
	int ih_irq;			/* IRQ number */
	struct evcount	ih_count;
	char *ih_name;
};

struct intrq {
	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
	int iq_irq;			/* IRQ to mask while handling */
	int iq_levels;			/* IPL_*'s this IRQ has */
	int iq_ist;			/* share type */
};

volatile int a1xsoftint_pending;

struct intrq a1xintc_handler[NIRQ];
u_int32_t a1xintc_smask[NIPL];
u_int32_t a1xintc_imask[NBANKS][NIPL];

bus_space_tag_t		a1xintc_iot;
bus_space_handle_t	a1xintc_ioh;
int			a1xintc_nirq;

void	a1xintc_attach(struct device *, struct device *, void *);
int	a1xintc_spllower(int);
int	a1xintc_splraise(int);
void	a1xintc_setipl(int);
void	a1xintc_calc_masks(void);

struct cfattach	a1xintc_ca = {
	sizeof (struct device), NULL, a1xintc_attach
};

struct cfdriver a1xintc_cd = {
	NULL, "a1xintc", DV_DULL
};

int a1xintc_attached = 0;

void
a1xintc_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	int i, j;

	a1xintc_iot = aa->aa_iot;
	if (bus_space_map(a1xintc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &a1xintc_ioh))
		panic("a1xintc_attach: bus_space_map failed!");

	/* disable/mask/clear all interrupts */
	for (i = 0; i < NBANKS; i++) {
		bus_space_write_4(a1xintc_iot, a1xintc_ioh, INTC_ENABLE_REG(i), 0);
		bus_space_write_4(a1xintc_iot, a1xintc_ioh, INTC_MASK_REG(i), 0);
		bus_space_write_4(a1xintc_iot, a1xintc_ioh, INTC_IRQ_PENDING_REG(i),
		    0xffffffff);
		for (j = 0; j < NIPL; j++)
			a1xintc_imask[i][j] = 0;
	}

	/* XXX */
	bus_space_write_4(a1xintc_iot, a1xintc_ioh, INTC_PROTECTION_REG, 1);
	bus_space_write_4(a1xintc_iot, a1xintc_ioh, INTC_NMI_CTRL_REG, 0);

	for (i = 0; i < NIRQ; i++)
		TAILQ_INIT(&a1xintc_handler[i].iq_list);

	a1xintc_calc_masks();

	arm_init_smask();
	a1xintc_attached = 1;

	/* insert self as interrupt handler */
	arm_set_intr_handler(a1xintc_splraise, a1xintc_spllower, a1xintc_splx,
	    a1xintc_setipl,
	    a1xintc_intr_establish, a1xintc_intr_disestablish, a1xintc_intr_string,
	    a1xintc_irq_handler);
	a1xintc_setipl(IPL_HIGH);  /* XXX ??? */
	enable_interrupts(I32_bit);
	printf("\n");
}

void
a1xintc_calc_masks(void)
{
	struct cpu_info *ci = curcpu();
	int irq;
	struct intrhand *ih;
	int i;

	for (irq = 0; irq < NIRQ; irq++) {
		int max = IPL_NONE;
		int min = IPL_HIGH;
		TAILQ_FOREACH(ih, &a1xintc_handler[irq].iq_list, ih_list) {
			if (ih->ih_ipl > max)
				max = ih->ih_ipl;
			if (ih->ih_ipl < min)
				min = ih->ih_ipl;
		}

		a1xintc_handler[irq].iq_irq = max;

		if (max == IPL_NONE)
			min = IPL_NONE;

#ifdef DEBUG_INTC
		if (min != IPL_NONE) {
			printf("irq %d to block at %d %d reg %d bit %d\n",
			    irq, max, min, IRQ2REG32(irq),
			    IRQ2BIT32(irq));
		}
#endif
		/* Enable interrupts at lower levels, clear -> enable */
		for (i = 0; i < min; i++)
			a1xintc_imask[IRQ2REG32(irq)][i] &=
			    ~(1 << IRQ2BIT32(irq));
		for (; i < NIPL; i++)
			a1xintc_imask[IRQ2REG32(irq)][i] |=
			    (1 << IRQ2BIT32(irq));
		/* XXX - set enable/disable, priority */
	}

	a1xintc_setipl(ci->ci_cpl);
}

void
a1xintc_splx(int new)
{
	struct cpu_info *ci = curcpu();
	a1xintc_setipl(new);

	if (ci->ci_ipending & arm_smask[ci->ci_cpl])
		arm_do_pending_intr(ci->ci_cpl);
}

int
a1xintc_spllower(int new)
{
	struct cpu_info *ci = curcpu();
	int old = ci->ci_cpl;
	a1xintc_splx(new);
	return (old);
}

int
a1xintc_splraise(int new)
{
	struct cpu_info *ci = curcpu();
	int old;
	old = ci->ci_cpl;

	/*
	 * setipl must always be called because there is a race window
	 * where the variable is updated before the mask is set
	 * an interrupt occurs in that window without the mask always
	 * being set, the hardware might not get updated on the next
	 * splraise completely messing up spl protection.
	 */
	if (old > new)
		new = old;

	a1xintc_setipl(new);

	return (old);
}

void
a1xintc_setipl(int new)
{
	struct cpu_info *ci = curcpu();
	int i, psw;
#if 1
	/*
	 * XXX not needed, because all interrupts are disabled
	 * by default, so touching maskregs has no effect, i hope.
	 */
	if (a1xintc_attached == 0) {
		ci->ci_cpl = new;
		return;
	}
#endif
	psw = disable_interrupts(I32_bit);
	ci->ci_cpl = new;
	for (i = 0; i < NBANKS; i++)
		bus_space_write_4(a1xintc_iot, a1xintc_ioh,
		    INTC_MASK_REG(i), a1xintc_imask[i][new]);
	restore_interrupts(psw);
}

void
a1xintc_irq_handler(void *frame)
{
	struct intrhand *ih;
	void *arg;
	uint32_t pr;
	int irq, prio, s;

	irq = bus_space_read_4(a1xintc_iot, a1xintc_ioh, INTC_VECTOR_REG) >> 2;
	if (irq == 0)
		return;

	prio = a1xintc_handler[irq].iq_irq;
	s = a1xintc_splraise(prio);
	splassert(prio);

	pr = bus_space_read_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)));
	bus_space_write_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)),
	    pr & ~(1 << IRQ2BIT32(irq)));

	/* clear pending */
	pr = bus_space_read_4(a1xintc_iot, a1xintc_ioh,
	    INTC_IRQ_PENDING_REG(IRQ2REG32(irq)));
	bus_space_write_4(a1xintc_iot, a1xintc_ioh,
	    INTC_IRQ_PENDING_REG(IRQ2REG32(irq)),
	    pr | (1 << IRQ2BIT32(irq)));

	pr = bus_space_read_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)));
	bus_space_write_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)),
	    pr | (1 << IRQ2BIT32(irq)));

	TAILQ_FOREACH(ih, &a1xintc_handler[irq].iq_list, ih_list) {
		if (ih->ih_arg != 0)
			arg = ih->ih_arg;
		else
			arg = frame;

		if (ih->ih_func(arg))
			ih->ih_count.ec_count++;
	}
	a1xintc_splx(s);
}

void *
a1xintc_intr_establish(int irq, int lvl, int (*f)(void *), void *arg, char *name)
{
	int psw;
	struct intrhand *ih;
	uint32_t er;

	if (irq <= 0 || irq >= NIRQ)
		panic("intr_establish: bogus irq %d %s\n", irq, name);

	DPRINTF(("intr_establish: irq %d level %d [%s]\n", irq, lvl,
	    name != NULL ? name : "NULL"));

	psw = disable_interrupts(I32_bit);

	/* no point in sleeping unless someone can free memory. */
	ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF,
	    cold ? M_NOWAIT : M_WAITOK);
	if (ih == NULL)
		panic("intr_establish: can't malloc handler info\n");
	ih->ih_func = f;
	ih->ih_arg = arg;
	ih->ih_ipl = lvl;
	ih->ih_irq = irq;
	ih->ih_name = name;

	TAILQ_INSERT_TAIL(&a1xintc_handler[irq].iq_list, ih, ih_list);

	if (name != NULL)
		evcount_attach(&ih->ih_count, name, &ih->ih_irq);

	er = bus_space_read_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)));
	bus_space_write_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)),
	    er | (1 << IRQ2BIT32(irq)));

	a1xintc_calc_masks();

	restore_interrupts(psw);
	return (ih);
}

void
a1xintc_intr_disestablish(void *cookie)
{
	struct intrhand *ih = cookie;
	int irq = ih->ih_irq;
	int psw;
	uint32_t er;

	psw = disable_interrupts(I32_bit);

	TAILQ_REMOVE(&a1xintc_handler[irq].iq_list, ih, ih_list);

	if (ih->ih_name != NULL)
		evcount_detach(&ih->ih_count);

	free(ih, M_DEVBUF, 0);

	er = bus_space_read_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)));
	bus_space_write_4(a1xintc_iot, a1xintc_ioh,
	    INTC_ENABLE_REG(IRQ2REG32(irq)),
	    er & ~(1 << IRQ2BIT32(irq)));

	a1xintc_calc_masks();

	restore_interrupts(psw);
}

const char *
a1xintc_intr_string(void *cookie)
{
	return "asd?";
}


code:
/*	$OpenBSD: a1xintc.h,v 1.3 2015/05/19 06:09:35 jsg Exp $ */
/*
 * Copyright (c) 2007,2009 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.
 */

#ifndef _A1XINTC_H_
#define _A1XINTC_H_

#ifndef _LOCORE

#include <arm/armreg.h>
#include <arm/cpufunc.h>
#include <machine/intr.h>
#include <arm/softintr.h>

extern volatile int current_spl_level;
extern volatile int softint_pending;
void a1xintc_do_pending(void);

#define SI_TO_IRQBIT(si)  (1U<<(si))
void a1xintc_setipl(int);
void a1xintc_splx(int);
int a1xintc_splraise(int);
int a1xintc_spllower(int);
void a1xintc_setsoftintr(int);

void a1xintc_irq_handler(void *);
void *a1xintc_intr_establish(int, int, int (*)(void *), void *, char *);
void a1xintc_intr_disestablish(void *);
const char *a1xintc_intr_string(void *);

#endif /* ! _LOCORE */

#endif /* _A1XINTC_H_ */

license: unknown


code:
#	$OpenBSD: files.sunxi,v 1.3 2015/05/15 15:35:43 jsg Exp $

define	sunxi {}
device	sunxi: sunxi
attach	sunxi at mainbus
file	arch/armv7/sunxi/sunxi_machdep.c	sunxi	needs-flag
file	arch/armv7/sunxi/sunxi.c		sunxi
file	arch/armv7/sunxi/sun4i.c
file	arch/armv7/sunxi/sun7i.c

device	sxiccmu
attach	sxiccmu at sunxi
file	arch/armv7/sunxi/sxiccmu.c		sxiccmu

device	sxipio {}: gpiobus
attach	sxipio at sunxi
file	arch/armv7/sunxi/sxipio.c		sxipio

device	a1xintc
attach	a1xintc at sunxi
file	arch/armv7/sunxi/a1xintc.c		a1xintc

device	sxitimer
attach	sxitimer at sunxi
file	arch/armv7/sunxi/sxitimer.c		sxitimer

device	sxidog
attach	sxidog at sunxi
file	arch/armv7/sunxi/sxidog.c		sxidog

device	sxirtc
attach	sxirtc at sunxi
file	arch/armv7/sunxi/sxirtc.c		sxirtc

attach	ahci at sunxi with sxiahci
file	arch/armv7/sunxi/sxiahci.c		sxiahci

#attach ohci at sunxi with sxiohci
#file	arch/armv7/sunxi/sxiohci.c		sxiohci

attach	ehci at sunxi with sxiehci
file	arch/armv7/sunxi/sxiehci.c		sxiehci

device	sxiuart
attach	sxiuart at sunxi
file	arch/armv7/sunxi/sxiuart.c		sxiuart

device	sxie: ether, ifnet, mii, ifmedia
attach	sxie at sunxi
file	arch/armv7/sunxi/sxie.c			sxie


code:
/*	$OpenBSD: sun4i.c,v 1.3 2013/11/06 19:03:07 syl Exp $	*/
/*
 * Copyright (c) 2011 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.
 */

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

#include <machine/bus.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>

struct armv7_dev sxia1x_devs[] = {

	/* 'Port IO' */
	{ .name = "sxipio",
	  .unit = 0,
	  .mem = { { PIO_ADDR, PIOx_SIZE } },
	  .irq = { PIO_IRQ }
	},

	/* Clock Control Module/Unit */
	{ .name = "sxiccmu",
	  .unit = 0,
	  .mem = { { CCMU_ADDR, CCMU_SIZE } },
	},

	/* Interrupt Controller */
	{ .name = "a1xintc",
	  .unit = 0,
	  .mem = { { INTC_ADDR, INTC_SIZE } },
	},

	/* Timers/Counters, resources mapped on first unit */
	{ .name = "sxitimer",
	  .unit = 0,
	  .mem = {	{ TIMER_ADDR, TIMERx_SIZE },
			{ CPUCNTRS_ADDR, CPUCNTRS_ADDR } }
	},
	{ .name = "sxitimer",
	  .unit = 1,
	},
	{ .name = "sxitimer",
	  .unit = 2,
	},

	/* Watchdog Timer */
	{ .name = "sxidog",
	  .unit = 0,
	  .mem = { { WDOG_ADDR, WDOG_SIZE } }
	},

	/* Real Time Clock */
	{ .name = "sxirtc",
	  .unit = 0,
	  .mem = { { RTC_ADDR, RTC_SIZE } }
	},

	/* DMA Controller */
	{ .name = "sxidmac",
	  .unit = 0,
	  .mem = { { DMAC_ADDR, DMAC_SIZE } },
	  .irq = { DMAC_IRQ }
	},

	/* UART */
	{ .name = "sxiuart",
	  .unit = 0,
	  .mem = { { UART0_ADDR, UARTx_SIZE } },
	  .irq = { UART0_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 1,
	  .mem = { { UART1_ADDR, UARTx_SIZE } },
	  .irq = { UART1_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 2,
	  .mem = { { UART2_ADDR, UARTx_SIZE } },
	  .irq = { UART2_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 3,
	  .mem = { { UART3_ADDR, UARTx_SIZE } },
	  .irq = { UART3_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 4,
	  .mem = { { UART4_ADDR, UARTx_SIZE } },
	  .irq = { UART4_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 5,
	  .mem = { { UART5_ADDR, UARTx_SIZE } },
	  .irq = { UART5_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 6,
	  .mem = { { UART6_ADDR, UARTx_SIZE } },
	  .irq = { UART6_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 7,
	  .mem = { { UART7_ADDR, UARTx_SIZE } },
	  .irq = { UART7_IRQ }
	},

	/* EMAC */
	{ .name = "sxie",
	  .unit = 0,
	  .mem = {	{ EMAC_ADDR, EMAC_SIZE },
			{ SXIESRAM_ADDR, SXIESRAM_SIZE } },
	  .irq = { EMAC_IRQ}
	},

	/* SATA/AHCI */
	{ .name = "ahci",
	  .unit = 0,
	  .mem = { { SATA_ADDR, SATA_SIZE } },
	  .irq = { SATA_IRQ }
	},

	/* USB */
	{ .name = "ehci",
	  .unit = 0,
	  .mem = { { USB1_ADDR, USBx_SIZE } },
	  .irq = { USB1_IRQ }
	},
	{ .name = "ehci",
	  .unit = 1,
	  .mem = { { USB2_ADDR, USBx_SIZE } },
	  .irq = { USB2_IRQ }
	},
	{ .name = "ohci",
	  .unit = 0,
	  .mem = { { USB1_ADDR, USBx_SIZE } },
	  .irq = { USB0_IRQ }
	},
	{ .name = "ohci",
	  .unit = 1,
	  .mem = { { USB2_ADDR, USBx_SIZE } },
	  .irq = { USB1_IRQ }
	},

	/* Terminator */
	{ .name = NULL,
	  .unit = 0,
	}
};

void
sxia1x_init(void)
{
	armv7_set_devs(sxia1x_devs);
}


code:
/* $OpenBSD: sun7i.c,v 1.3 2013/11/06 19:03:07 syl Exp $ */

/*
 * Copyright (c) 2011 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.
 */

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

#include <machine/bus.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>

struct armv7_dev sxia20_devs[] = {

	/* 'Port IO' */
	{ .name = "sxipio",
	  .unit = 0,
	  .mem = { { PIO_ADDR, PIOx_SIZE } },
	  .irq = { PIO_IRQ }
	},

	/* Clock Control Module/Unit */
	{ .name = "sxiccmu",
	  .unit = 0,
	  .mem = { { CCMU_ADDR, CCMU_SIZE } },
	},

	/* Timers/Counters, resources mapped on first unit */
	{ .name = "sxitimer",
	  .unit = 0,
	  .mem = {	{ TIMER_ADDR, TIMERx_SIZE },
			{ CPUCNTRS_ADDR, CPUCNTRS_ADDR } }
	},
	{ .name = "sxitimer",
	  .unit = 1,
	},
	{ .name = "sxitimer",
	  .unit = 2,
	},

	/* Watchdog Timer */
	{ .name = "sxidog",
	  .unit = 0,
	  .mem = { { WDOG_ADDR, WDOG_SIZE } }
	},

	/* Real Time Clock */
	{ .name = "sxirtc",
	  .unit = 0,
	  .mem = { { RTC_ADDR, RTC_SIZE } }
	},

	/* DMA Controller */
	{ .name = "sxidmac",
	  .unit = 0,
	  .mem = { { DMAC_ADDR, DMAC_SIZE } },
	  .irq = { DMAC_IRQ }
	},

	/* UART */
	{ .name = "sxiuart",
	  .unit = 0,
	  .mem = { { UART0_ADDR, UARTx_SIZE } },
	  .irq = { UART0_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 1,
	  .mem = { { UART1_ADDR, UARTx_SIZE } },
	  .irq = { UART1_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 2,
	  .mem = { { UART2_ADDR, UARTx_SIZE } },
	  .irq = { UART2_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 3,
	  .mem = { { UART3_ADDR, UARTx_SIZE } },
	  .irq = { UART3_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 4,
	  .mem = { { UART4_ADDR, UARTx_SIZE } },
	  .irq = { UART4_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 5,
	  .mem = { { UART5_ADDR, UARTx_SIZE } },
	  .irq = { UART5_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 6,
	  .mem = { { UART6_ADDR, UARTx_SIZE } },
	  .irq = { UART6_IRQ }
	},
	{ .name = "sxiuart",
	  .unit = 7,
	  .mem = { { UART7_ADDR, UARTx_SIZE } },
	  .irq = { UART7_IRQ }
	},

	/* EMAC */
	{ .name = "sxie",
	  .unit = 0,
	  .mem = {	{ EMAC_ADDR, EMAC_SIZE },
			{ SXIESRAM_ADDR, SXIESRAM_SIZE } },
	  .irq = { EMAC_IRQ}
	},

	/* SATA/AHCI */
	{ .name = "ahci",
	  .unit = 0,
	  .mem = { { SATA_ADDR, SATA_SIZE } },
	  .irq = { SATA_IRQ }
	},

	/* USB */
	{ .name = "ehci",
	  .unit = 0,
	  .mem = { { USB1_ADDR, USBx_SIZE } },
	  .irq = { USB1_IRQ }
	},
	{ .name = "ehci",
	  .unit = 1,
	  .mem = { { USB2_ADDR, USBx_SIZE } },
	  .irq = { USB2_IRQ }
	},
	{ .name = "ohci",
	  .unit = 0,
	  .mem = { { USB1_ADDR, USBx_SIZE } },
	  .irq = { USB0_IRQ }
	},
	{ .name = "ohci",
	  .unit = 1,
	  .mem = { { USB2_ADDR, USBx_SIZE } },
	  .irq = { USB1_IRQ }
	},

	/* Terminator */
	{ .name = NULL,
	  .unit = 0,
	}
};

void
sxia20_init(void)
{
	armv7_set_devs(sxia20_devs);
}


code:
/* $OpenBSD: sunxi.c,v 1.6 2015/05/20 00:14:56 jsg Exp $ */
/*
 * Copyright (c) 2005,2008 Dale Rahn <drahn@openbsd.com>
 *
 * 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 <machine/bus.h>

#include <arm/armv7/armv7var.h>
#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>

int	sunxi_match(struct device *, void *, void *);
void	sxia1x_init();
void	sxia20_init();

struct cfattach sunxi_ca = {
	sizeof(struct armv7_softc), sunxi_match, armv7_attach
};

struct cfdriver sunxi_cd = {
	NULL, "sunxi", DV_DULL
};

struct board_dev sun4i_devs[] = {
	{ "sxipio",	0 },
	{ "sxiccmu",	0 },
	{ "a1xintc",	0 },
	{ "sxitimer",	0 },
	{ "sxitimer",	1 },
	{ "sxitimer",	2 },
	{ "sxidog",	0 },
	{ "sxirtc",	0 },
	{ "sxiuart",	0 },
	{ "sxiuart",	1 },
	{ "sxiuart",	2 },
	{ "sxiuart",	3 },
	{ "sxiuart",	4 },
	{ "sxiuart",	5 },
	{ "sxiuart",	6 },
	{ "sxiuart",	7 },
	{ "sxie",	0 },
	{ "ahci",	0 },
	{ "ehci",	0 },
	{ "ehci",	1 },
#if 0
	{ "ohci",	0 },
	{ "ohci",	1 },
#endif
	{ NULL,		0 }
};

struct board_dev sun7i_devs[] = {
	{ "sxipio",	0 },
	{ "sxiccmu",	0 },
	{ "sxitimer",	0 },
	{ "sxitimer",	1 },
	{ "sxitimer",	2 },
	{ "sxidog",	0 },
	{ "sxirtc",	0 },
	{ "sxiuart",	0 },
	{ "sxiuart",	1 },
	{ "sxiuart",	2 },
	{ "sxiuart",	3 },
	{ "sxiuart",	4 },
	{ "sxiuart",	5 },
	{ "sxiuart",	6 },
	{ "sxiuart",	7 },
	{ "sxie",	0 },
	{ "ahci",	0 },
	{ "ehci",	0 },
	{ "ehci",	1 },
#if 0
	{ "ohci",	0 },
	{ "ohci",	1 },
#endif
	{ NULL,		0 }
};

struct armv7_board sunxi_boards[] = {
	{
		BOARD_ID_SUN4I_A10,
		"Allwinner A1x",
		sun4i_devs,
		sxia1x_init,
	},
	{
		BOARD_ID_SUN7I_A20,
		"Allwinner A20",
		sun7i_devs,
		sxia20_init,
	},
	{ 0, NULL, NULL, NULL },
};

struct board_dev *
sunxi_board_devs(void)
{
	int i;

	for (i = 0; sunxi_boards[i].name != NULL; i++) {
		if (sunxi_boards[i].board_id == board_id)
			return (sunxi_boards[i].devs);
	}
	return (NULL);
}

void
sunxi_board_init(void)
{
	bus_space_handle_t ioh;
	int i, match = 0;

	for (i = 0; sunxi_boards[i].name != NULL; i++) {
		if (sunxi_boards[i].board_id == board_id) {
			sunxi_boards[i].init();
			match = 1;
			break;
		}
	}

	if (match) {
		if (bus_space_map(&armv7_bs_tag, SYSCTRL_ADDR, SYSCTRL_SIZE, 0,
		    &ioh))
			panic("sunxi_attach: bus_space_map failed!");
		/* map the part of SRAM dedicated to EMAC to EMAC */
		bus_space_write_4(&armv7_bs_tag, ioh, 4,
		    bus_space_read_4(&armv7_bs_tag, ioh, 4) | (5 << 2));
	}
}

const char *
sunxi_board_name(void)
{
	int i;

	for (i = 0; sunxi_boards[i].name != NULL; i++) {
		if (sunxi_boards[i].board_id == board_id) {
			return (sunxi_boards[i].name);
			break;
		}
	}
	return (NULL);
}

int
sunxi_match(struct device *parent, void *cfdata, void *aux)
{
	return (sunxi_board_devs() != NULL);
}


code:
/*	$OpenBSD: sunxi_machdep.c,v 1.8 2015/05/19 03:30:54 jsg Exp $	*/
/*
 * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
 *
 * 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/types.h>
#include <sys/systm.h>
#include <sys/termios.h>

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

#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>

#include <arm/cortex/smc.h>
#include <arm/armv7/armv7var.h>
#include <armv7/armv7/armv7var.h>
#include <armv7/armv7/armv7_machdep.h>

extern int sxiuartcnattach(bus_space_tag_t, bus_addr_t, int, long, tcflag_t);
extern void sxidog_reset(void);
extern char *sunxi_board_name(void);
extern struct board_dev *sunxi_board_devs(void);
extern void sunxi_board_init(void);
extern int comcnspeed;
extern int comcnmode;

void
sunxi_platform_smc_write(bus_space_tag_t iot, bus_space_handle_t ioh,
    bus_size_t off, uint32_t op, uint32_t val)
{

}

void
sunxi_platform_init_cons(void)
{
	paddr_t paddr;

	switch (board_id) {
	case BOARD_ID_SUN4I_A10:
	case BOARD_ID_SUN7I_A20:
		paddr = 0x01c28000;	/* UART0 */
		break;
	default:
		sxiuartcnattach(&armv7_a4x_bs_tag, paddr, comcnspeed,
		    24000000, comcnmode);
		panic("board type %x unknown", board_id);
	}

	sxiuartcnattach(&armv7_a4x_bs_tag, paddr, comcnspeed, 24000000,
	    comcnmode);
}

void
sunxi_platform_watchdog_reset(void)
{
	sxidog_reset();
}

void
sunxi_platform_powerdown(void)
{

}

const char *
sunxi_platform_board_name(void)
{
	return (sunxi_board_name());
}

void
sunxi_platform_disable_l2_if_needed(void)
{

}

void
sunxi_platform_board_init(void)
{
	sunxi_board_init();
}

struct armv7_platform sunxi_platform = {
	.boot_name = "OpenBSD/sunxi",
	.board_name = sunxi_platform_board_name,
	.board_init = sunxi_platform_board_init,
	.smc_write = sunxi_platform_smc_write,
	.init_cons = sunxi_platform_init_cons,
	.watchdog_reset = sunxi_platform_watchdog_reset,
	.powerdown = sunxi_platform_powerdown,
	.disable_l2_if_needed = sunxi_platform_disable_l2_if_needed,
};

struct armv7_platform *
sunxi_platform_match(void)
{
	struct board_dev *devs;

	devs = sunxi_board_devs();
	if (devs == NULL)
		return (NULL);

	sunxi_platform.devs = devs;
	return (&sunxi_platform);
}


code:
/*	$OpenBSD: sunxireg.h,v 1.6 2013/11/06 19:03:07 syl Exp $	*/
/*
 * Copyright (c) 2013 Artturi Alm
 *
 * 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.
 */

#define SXIREAD1(sc, reg)						\
	(bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define SXIWRITE1(sc, reg, val)						\
	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define SXISET1(sc, reg, bits)						\
	SXIWRITE1((sc), (reg), SXIREAD1((sc), (reg)) | (bits))
#define SXICLR1(sc, reg, bits)						\
	SXIWRITE1((sc), (reg), SXIREAD1((sc), (reg)) & ~(bits))
#define	SXICMS1(sc, reg, mask, bits)					\
	SXIWRITE1((sc), (reg), (SXIREAD1((sc), (reg)) & ~(mask)) | (bits))

#define SXIREAD4(sc, reg)						\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define SXIWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define SXISET4(sc, reg, bits)						\
	SXIWRITE4((sc), (reg), SXIREAD4((sc), (reg)) | (bits))
#define SXICLR4(sc, reg, bits)						\
	SXIWRITE4((sc), (reg), SXIREAD4((sc), (reg)) & ~(bits))
#define	SXICMS4(sc, reg, mask, bits)					\
	SXIWRITE4((sc), (reg), (SXIREAD4((sc), (reg)) & ~(mask)) | (bits))

#define	TIMER0_FREQUENCY	(32768)
#define	TIMER1_FREQUENCY	(32768)
#define	TIMER2_FREQUENCY	(32768)
#define	COUNTER_FREQUENCY	(24000000)

/* SRAM Controller / System Control */
#define	SYSCTRL_ADDR		0x01c00000
#define	SYSCTRL_SIZE		0x1000

#define	DMAC_ADDR		0x01c02000
#define	DMAC_SIZE		0x1000
#define	DMAC_IRQ		27

#define	SDMMC0_ADDR		0x01c0f000
#define	SDMMCx_SIZE		0x1000
#define	SDMMC0_IRQ		32

#define	SATA_ADDR		0x01c18000
#define	SATA_SIZE		0x1000
#define	SATA_IRQ		56

#define	TIMER_ADDR		0x01c20c00
#define	TIMERx_SIZE		0x200
#define	TIMER0_IRQ		22
#define	TIMER1_IRQ		23
#define	TIMER2_IRQ		24
#define	STATTIMER_IRQ		TIMER1_IRQ /* XXX */

#define	WDOG_ADDR		0x90
#define	WDOG_SIZE		0x08
#define	WDOG_IRQ		24

#define	RTC_ADDR		0x0104
#define	RTC_SIZE		0x08

/* Clock Control Module/Unit */
#define	CCMU_ADDR		0x01c20000
#define	CCMU_SIZE		0x400

#define	PIO_ADDR		0x01c20800
#define	PIOx_SIZE		0x400
#define	PIO_IRQ			28

/* Secure ID */
#define SID_ADDR		0x01c23800
#define SID_SIZE		0x400

#define	UARTx_SIZE		0x400
#define	UART0_ADDR		0x01c28000
#define	UART1_ADDR		0x01c28400
#define	UART2_ADDR		0x01c28800
#define	UART3_ADDR		0x01c28c00
#define	UART4_ADDR		0x01c29000
#define	UART5_ADDR		0x01c29400
#define	UART6_ADDR		0x01c29800
#define	UART7_ADDR		0x01c29c00
#define	UART0_IRQ		1
#define	UART1_IRQ		2
#define	UART2_IRQ		3
#define	UART3_IRQ		4
#define	UART4_IRQ		17
#define	UART5_IRQ		18
#define	UART6_IRQ		19
#define	UART7_IRQ		20

#define	USB0_ADDR		0x01c13000 /* usb otg */
#define	USB1_ADDR		0x01c14000 /* first port up from pcb */
#define	USB2_ADDR		0x01c1c000 /* 'top port' == above USB1 */
#define	USBx_SIZE		0x1000
#define	USB0_IRQ		38
#define	USB1_IRQ		39
#define	USB2_IRQ		40

/* Ethernet MAC Controller */
#define	EMAC_ADDR		0x01c0b000
#define	EMAC_SIZE		0x1000
#define	EMAC_IRQ		55
#define	SXIESRAM_ADDR		0x00008000 /* combined area for EMAC fifos */
#define	SXIESRAM_SIZE		0x4000

/* Security System */
#define	SS_ADDR			0x01c15000
#define	SS_SIZE			0x1000
#define	SS_IRQ			54

/* GMAC */
#define	GMAC_ADDR		0x01c50000
#define	GMAC_SIZE		0x10000
#define	GMAC_IRQ		85

/* A1x / Cortex-A8 */
#define	INTC_ADDR		0x01c20400
#define	INTC_SIZE		0x400

/* A20 / Cortex-A7 */
#define	GIC_ADDR		0x01c80000 /* = periphbase */
#define	GIC_SIZE		0x8000
#define	CPUCONFG_ADDR		0x01c25c00 /* not in use */
#define	CPUCONFG_SIZE		0x200
#define	CPUCNTRS_ADDR		0x01c25e00 /* used by sxitimer */
#define	CPUCNTRS_SIZE		0x200


code:
/*	$OpenBSD: sxiahci.c,v 1.8 2015/01/22 14:33:01 krw Exp $	*/
/*
 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
 * Copyright (c) 2013,2014 Artturi Alm
 *
 * 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/buf.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/queue.h>

#include <machine/bus.h>

#include <dev/ic/ahcireg.h>
#include <dev/ic/ahcivar.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>
#include <armv7/sunxi/sxiccmuvar.h>
#include <armv7/sunxi/sxipiovar.h>

#define	SXIAHCI_CAP	0x0000
#define	SXIAHCI_GHC	0x0004
#define	SXIAHCI_PI	0x000c
#define	SXIAHCI_PHYCS0	0x00c0
#define	SXIAHCI_PHYCS1	0x00c4
#define	SXIAHCI_PHYCS2	0x00c8
#define	SXIAHCI_TIMER1MS	0x00e0
#define	SXIAHCI_RWC	0x00fc
#define	SXIAHCI_TIMEOUT	0x100000
#define SXIAHCI_PWRPIN	40

#define SXIAHCI_PREG_DMA	0x70
#define  SXIAHCI_PREG_DMA_MASK	(0xff<<8)
#define  SXIAHCI_PREG_DMA_INIT	(0x44<<8)

void	sxiahci_attach(struct device *, struct device *, void *);
int	sxiahci_detach(struct device *, int);
int	sxiahci_activate(struct device *, int);
int	sxiahci_port_start(struct ahci_port *, int);

extern int ahci_intr(void *);
extern u_int32_t ahci_read(struct ahci_softc *, bus_size_t);
extern void ahci_write(struct ahci_softc *, bus_size_t, u_int32_t);
extern u_int32_t ahci_pread(struct ahci_port *, bus_size_t);
extern void ahci_pwrite(struct ahci_port *, bus_size_t, u_int32_t);
extern int ahci_default_port_start(struct ahci_port *, int);

struct sxiahci_softc {
	struct ahci_softc	sc;

};

struct cfattach sxiahci_ca = {
	sizeof(struct sxiahci_softc),
	NULL,
	sxiahci_attach,
	sxiahci_detach,
	sxiahci_activate
};

struct cfdriver sxiahci_cd = {
	NULL, "ahci", DV_DULL
};

void
sxiahci_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct sxiahci_softc *sxisc = (struct sxiahci_softc *)self;
	struct ahci_softc *sc = &sxisc->sc;
	uint32_t timo;

	sc->sc_iot = aa->aa_iot;
	sc->sc_ios = aa->aa_dev->mem[0].size;
	sc->sc_dmat = aa->aa_dmat;

	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    sc->sc_ios, 0, &sc->sc_ioh))
		panic("sxiahci_attach: bus_space_map failed!");

	/* enable clock */
	sxiccmu_enablemodule(CCMU_AHCI);
	delay(5000);

	/* XXX setup magix */
	SXIWRITE4(sc, SXIAHCI_RWC, 0);
	delay(10);

	SXISET4(sc, SXIAHCI_PHYCS1, 1 << 19);
	delay(10);

	SXICMS4(sc, SXIAHCI_PHYCS0, 7 << 24,
	    1 << 23 | 5 << 24 | 1 << 18);
	delay(10);

	SXICMS4(sc, SXIAHCI_PHYCS1,
	    3 << 16 | 0x1f << 8 | 3 << 6,
	    2 << 16 | 0x06 << 8 | 2 << 6);
	delay(10);

	SXISET4(sc, SXIAHCI_PHYCS1, 1 << 28 | 1 << 15);
	delay(10);

	SXICLR4(sc, SXIAHCI_PHYCS1, 1 << 19);
	delay(10);

	SXICMS4(sc, SXIAHCI_PHYCS0, 0x07 << 20, 0x03 << 20);
	SXICMS4(sc, SXIAHCI_PHYCS2, 0x1f <<  5, 0x19 << 5);
	delay(5000);

	SXISET4(sc, SXIAHCI_PHYCS0, 1 << 19);
	delay(20);

	timo = SXIAHCI_TIMEOUT;
	while ((SXIREAD4(sc, SXIAHCI_PHYCS0) >> 28 & 7) != 2 && --timo)
		delay(10);
	if (!timo) {
		printf(": AHCI phy power up failed.\n");
		goto dismod;
	}

	SXISET4(sc, SXIAHCI_PHYCS2, 1 << 24);

	timo = SXIAHCI_TIMEOUT;
	while ((SXIREAD4(sc, SXIAHCI_PHYCS2) & (1 << 24)) && --timo)
		delay(10);
	if (!timo) {
		printf(": AHCI phy calibration failed.\n");
		goto dismod;
	}

	delay(15000);
	SXIWRITE4(sc, SXIAHCI_RWC, 7);

	/* power up phy */
	sxipio_setcfg(SXIAHCI_PWRPIN, SXIPIO_OUTPUT);
	sxipio_setpin(SXIAHCI_PWRPIN);

	sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_BIO,
	    ahci_intr, sc, sc->sc_dev.dv_xname);
	if (sc->sc_ih == NULL) {
		printf(": unable to establish interrupt\n");
		goto clrpwr;
	}

	SXIWRITE4(sc, SXIAHCI_PI, 1);
	SXICLR4(sc, SXIAHCI_CAP, AHCI_REG_CAP_SPM);
	sc->sc_flags |= AHCI_F_NO_PMP;
	sc->sc_port_start = sxiahci_port_start;
	if (ahci_attach(sc) != 0) {
		/* error printed by ahci_attach */
		goto irq;
	}

	return;
irq:
	arm_intr_disestablish(sc->sc_ih);
clrpwr:
	sxipio_clrpin(SXIAHCI_PWRPIN);
dismod:
	sxiccmu_disablemodule(CCMU_AHCI);
	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
}

int
sxiahci_detach(struct device *self, int flags)
{
	struct sxiahci_softc *sxisc = (struct sxiahci_softc *) self;
	struct ahci_softc *sc = &sxisc->sc;

	ahci_detach(sc, flags);
	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
	return 0;
}

int
sxiahci_activate(struct device *self, int act)
{
	struct sxiahci_softc *sxisc = (struct sxiahci_softc *) self;
	struct ahci_softc *sc = &sxisc->sc;

	return ahci_activate((struct device *)sc, act);
}

int
sxiahci_port_start(struct ahci_port *ap, int fre_only)
{
	u_int32_t			r;

	/* Setup DMA */
	r = ahci_pread(ap, SXIAHCI_PREG_DMA);
	r &= ~SXIAHCI_PREG_DMA_MASK;
	r |= SXIAHCI_PREG_DMA_INIT; /* XXX if fre_only? */
	ahci_pwrite(ap, SXIAHCI_PREG_DMA, r);

	return (ahci_default_port_start(ap, fre_only));
}


code:
/*	$OpenBSD: sxiccmu.c,v 1.5 2013/11/26 20:33:12 deraadt Exp $	*/
/*
 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
 * Copyright (c) 2013 Artturi Alm
 *
 * 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/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/device.h>

#include <arm/cpufunc.h>

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

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>
#include <armv7/sunxi/sxiccmuvar.h>

#ifdef DEBUG_CCMU
#define DPRINTF(x)	do { printf x; } while (0)
#else
#define DPRINTF(x)
#endif

#define	CCMU_SCLK_GATING		(1U << 31)
#define	CCMU_GET_CLK_DIV_RATIO_N(x)	(((x) >> 16) & 0x03)
#define	CCMU_GET_CLK_DIV_RATIO_M(x)	((x) & 0x07)

#define	CCMU_PLL6_CFG			0x28
#define	CCMU_PLL6_EN			(1U << 31)
#define	CCMU_PLL6_BYPASS_EN		(1 << 30)
#define	CCMU_PLL6_SATA_CLK_EN		(1 << 14)
#define	CCMU_PLL6_FACTOR_N		(31 << 8)
#define	CCMU_PLL6_FACTOR_K		(3 << 4)
#define	CCMU_PLL6_FACTOR_M		(3 << 0)

#define	CCMU_AHB_GATING0		0x60
#define	CCMU_AHB_GATING_USB0		(1 << 0)
#define	CCMU_AHB_GATING_EHCI0		(1 << 1)
#define	CCMU_AHB_GATING_OHCI0		(1 << 2)
#define	CCMU_AHB_GATING_EHCI1		(1 << 3)
#define	CCMU_AHB_GATING_OHCI1		(1 << 4)
#define	CCMU_AHB_GATING_SS		(1 << 5)
#define	CCMU_AHB_GATING_DMA		(1 << 6)
#define	CCMU_AHB_GATING_BIST		(1 << 7)
#define	CCMU_AHB_GATING_SDMMCx(x)	(1 << (8 + (x)))
#define	CCMU_AHB_GATING_NAND		(1 << 13)
#define	CCMU_AHB_GATING_EMAC		(1 << 17)
#define	CCMU_AHB_GATING_SATA		(1 << 25)

#define	CCMU_APB_GATING0		0x68
#define	CCMU_APB_GATING_PIO		(1 << 5)
#define	CCMU_APB_GATING1		0x6c
#define	CCMU_APB_GATING_UARTx(x)	(1 << (16 + (x)))
#define	CCMU_APB_GATING_TWIx(x)		(1 << (x))
#define	CCMU_APB_GATING_TWI4		(1 << 15)

#define	CCMU_NAND_CLK			0x80
#define	CCMU_NAND_CLK_SRC_GATING_OSC24M	(0 << 24)
#define	CCMU_NAND_CLK_SRC_GATING_PLL6	(1 << 24)
#define	CCMU_NAND_CLK_SRC_GATING_PLL5	(2 << 24)
#define	CCMU_NAND_CLK_SRC_GATING_MASK	(3 << 24)

#define	CCMU_SATA_CLK			0xc8
#define	CCMU_SATA_CLK_SRC_GATING	(1 << 24)

#define	CCMU_USB_CLK			0xcc
#define	CCMU_USB_PHY			(1 << 8)
#define	CCMU_SCLK_GATING_OHCI1		(1 << 7)
#define	CCMU_SCLK_GATING_OHCI0		(1 << 6)
#define	CCMU_OHCI_CLK_SRC		(1 << 4)
#define	CCMU_USB2_RESET			(1 << 2)
#define	CCMU_USB1_RESET			(1 << 1)
#define	CCMU_USB0_RESET			(1 << 0)

struct sxiccmu_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

void	sxiccmu_attach(struct device *, struct device *, void *);
void	sxiccmu_enablemodule(int);

struct cfattach	sxiccmu_ca = {
	sizeof (struct sxiccmu_softc), NULL, sxiccmu_attach
};

struct cfdriver sxiccmu_cd = {
	NULL, "sxiccmu", DV_DULL
};

void
sxiccmu_attach(struct device *parent, struct device *self, void *args)
{
	struct sxiccmu_softc *sc = (struct sxiccmu_softc *)self;
	struct armv7_attach_args *aa = args;

	sc->sc_iot = aa->aa_iot;

	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
		panic("sxiccmu_attach: bus_space_map failed!");

	printf("\n");
}

/* XXX spl? */
void
sxiccmu_enablemodule(int mod)
{
	struct sxiccmu_softc *sc = sxiccmu_cd.cd_devs[0];
	uint32_t reg;

	DPRINTF(("\nsxiccmu_enablemodule: mod %d\n", mod));

	/* XXX reorder? */
	switch (mod) {
	case CCMU_EHCI0:
		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_EHCI0);
		SXISET4(sc, CCMU_USB_CLK, CCMU_USB1_RESET | CCMU_USB_PHY);
		break;
	case CCMU_EHCI1:
		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_EHCI1);
		SXISET4(sc, CCMU_USB_CLK, CCMU_USB2_RESET | CCMU_USB_PHY);
		break;
	case CCMU_OHCI0:
		SXISET4(sc, CCMU_USB_CLK, CCMU_OHCI_CLK_SRC);
		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_OHCI0);
		SXISET4(sc, CCMU_USB_CLK, CCMU_SCLK_GATING_OHCI0);
		break;
	case CCMU_OHCI1:
		SXISET4(sc, CCMU_USB_CLK, CCMU_OHCI_CLK_SRC);
		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_OHCI1);
		SXISET4(sc, CCMU_USB_CLK, CCMU_SCLK_GATING_OHCI1);
		break;
	case CCMU_AHCI:
		reg = SXIREAD4(sc, CCMU_PLL6_CFG);
		reg &= ~(CCMU_PLL6_BYPASS_EN | CCMU_PLL6_FACTOR_M |
		    CCMU_PLL6_FACTOR_N);
		reg |= CCMU_PLL6_EN | CCMU_PLL6_SATA_CLK_EN;
		reg |= 25 << 8;
		reg |= (reg >> 4 & 3);
		SXIWRITE4(sc, CCMU_PLL6_CFG, reg);

		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_SATA);
		delay(1000);

		SXIWRITE4(sc, CCMU_SATA_CLK, CCMU_SCLK_GATING);
		break;
	case CCMU_EMAC:
		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_EMAC);
		break;
	case CCMU_DMA:
		SXISET4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_DMA);
		break;
	case CCMU_UART0:
	case CCMU_UART1:
	case CCMU_UART2:
	case CCMU_UART3:
	case CCMU_UART4:
	case CCMU_UART5:
	case CCMU_UART6:
	case CCMU_UART7:
		SXISET4(sc, CCMU_APB_GATING1,
		    CCMU_APB_GATING_UARTx(mod - CCMU_UART0));
		break;
	case CCMU_SDMMC0:
	case CCMU_SDMMC1:
	case CCMU_SDMMC2:
	case CCMU_SDMMC3:
		SXISET4(sc, CCMU_AHB_GATING0,
		    CCMU_AHB_GATING_SDMMCx(mod - CCMU_SDMMC0));
		break;
	case CCMU_TWI0:
	case CCMU_TWI1:
	case CCMU_TWI2:
	case CCMU_TWI3:
	case CCMU_TWI4:
		SXISET4(sc, CCMU_APB_GATING1, mod == CCMU_TWI4
		    ? CCMU_APB_GATING_TWI4
		    : CCMU_APB_GATING_TWIx(mod - CCMU_TWI0));
		break;
	case CCMU_PIO:
		SXISET4(sc, CCMU_APB_GATING0, CCMU_APB_GATING_PIO);
		break;
	default:
		break;
	}
}

void
sxiccmu_disablemodule(int mod)
{
	struct sxiccmu_softc *sc = sxiccmu_cd.cd_devs[0];

	DPRINTF(("\nsxiccmu_disablemodule: mod %d\n", mod));

	switch (mod) {
	case CCMU_EHCI0:
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_EHCI0);
		SXICLR4(sc, CCMU_USB_CLK, CCMU_USB1_RESET | CCMU_USB_PHY);
		break;
	case CCMU_EHCI1:
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_EHCI1);
		SXICLR4(sc, CCMU_USB_CLK, CCMU_USB2_RESET | CCMU_USB_PHY);
		break;
	case CCMU_OHCI0:
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_OHCI0);
		SXICLR4(sc, CCMU_USB_CLK, CCMU_SCLK_GATING_OHCI0);
	case CCMU_OHCI1:
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_OHCI1);
		SXICLR4(sc, CCMU_USB_CLK, CCMU_SCLK_GATING_OHCI1);
		break;
	case CCMU_AHCI:
		/* XXX possibly wrong */
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_SATA);
		SXIWRITE4(sc, CCMU_SATA_CLK, 0);
		break;
	case CCMU_EMAC:
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_EMAC);
		break;
	case CCMU_DMA:
		SXICLR4(sc, CCMU_AHB_GATING0, CCMU_AHB_GATING_DMA);
		break;
	case CCMU_UART0:
	case CCMU_UART1:
	case CCMU_UART2:
	case CCMU_UART3:
	case CCMU_UART4:
	case CCMU_UART5:
	case CCMU_UART6:
	case CCMU_UART7:
		SXICLR4(sc, CCMU_APB_GATING1,
		    CCMU_APB_GATING_UARTx(mod - CCMU_UART0));
		break;
	case CCMU_SDMMC0:
	case CCMU_SDMMC1:
	case CCMU_SDMMC2:
	case CCMU_SDMMC3:
		SXICLR4(sc, CCMU_AHB_GATING0,
		    CCMU_AHB_GATING_SDMMCx(mod - CCMU_SDMMC0));
		break;
	case CCMU_TWI0:
	case CCMU_TWI1:
	case CCMU_TWI2:
	case CCMU_TWI3:
	case CCMU_TWI4:
		SXICLR4(sc, CCMU_APB_GATING1, mod == CCMU_TWI4
		    ? CCMU_APB_GATING_TWI4
		    : CCMU_APB_GATING_TWIx(mod - CCMU_TWI0));
		break;
	case CCMU_PIO:
		SXICLR4(sc, CCMU_APB_GATING0, CCMU_APB_GATING_PIO);
		break;
	default:
		break;
	}
}


code:
/*	$OpenBSD: sxiccmuvar.h,v 1.2 2013/11/01 21:15:05 aalm Exp $	*/
/*
 * Copyright (c) 2007,2009 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.
 */

void sxiccmu_enablemodule(int);
void sxiccmu_disablemodule(int);

enum CCMU_MODULES {
	CCMU_EHCI0,
	CCMU_EHCI1,
	CCMU_OHCI0,
	CCMU_OHCI1,
	CCMU_AHCI,
	CCMU_EMAC,
	CCMU_DMA,
	CCMU_UART0,
	CCMU_UART1,
	CCMU_UART2,
	CCMU_UART3,
	CCMU_UART4,
	CCMU_UART5,
	CCMU_UART6,
	CCMU_UART7,
	CCMU_TWI0,
	CCMU_TWI1,
	CCMU_TWI2,
	CCMU_TWI3,
	CCMU_TWI4,
	CCMU_SDMMC0,
	CCMU_SDMMC1,
	CCMU_SDMMC2,
	CCMU_SDMMC3,
	CCMU_PIO
};


code:
/* $OpenBSD: sxidog.c,v 1.6 2014/12/13 00:49:20 jsg Exp $ */
/*
 * Copyright (c) 2007,2009 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/queue.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/socket.h>
#include <sys/timeout.h>

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

#include <armv7/sunxi/sunxireg.h>
#include <armv7/armv7/armv7var.h>

/* XXX other way around than bus_space_subregion? */
extern bus_space_handle_t sxitimer_ioh;

/* registers */
#define WDOG_CR			0x00
#define WDOG_MR			0x04

#define WDOG_CTRL_KEY		(0x0a57 << 1)
#define WDOG_RESTART		0x01
/*
 * 0x00 0,5sec
 * 0x01 1sec
 * 0x02 2sec
 * 0x03 3sec
 * 0x04 4sec
 * 0x05 5sec
 * 0x06 6sec
 * 0x07 8sec
 * 0x08 10sec
 * 0x09 12sec
 * 0x0a 14sec
 * 0x0b 16sec
 */
#define WDOG_INTV_VALUE(x)	((x) << 3)
#define WDOG_RST_EN		(1 << 1) /* system reset */
#define WDOG_EN			(1 << 0)

struct sxidog_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

struct sxidog_softc *sxidog_sc = NULL;	/* for sxidog_reset() */

void sxidog_attach(struct device *, struct device *, void *);
int sxidog_activate(struct device *, int);
int sxidog_callback(void *, int);
#if 0
int sxidog_intr(void *);
#endif
void sxidog_reset(void);

struct cfattach	sxidog_ca = {
	sizeof (struct sxidog_softc), NULL, sxidog_attach,
	NULL, sxidog_activate
};

struct cfdriver sxidog_cd = {
	NULL, "sxidog", DV_DULL
};

void
sxidog_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct sxidog_softc *sc = (struct sxidog_softc *)self;

	sc->sc_iot = aa->aa_iot;
	if (bus_space_subregion(sc->sc_iot, sxitimer_ioh,
	    aa->aa_dev->mem[0].addr, aa->aa_dev->mem[0].size, &sc->sc_ioh))
		panic("sxidog_attach: bus_space_subregion failed!");

#ifdef DEBUG
	printf(": ctrl %x mode %x\n", SXIREAD4(sc, WDOG_CR),
	    SXIREAD4(sc, WDOG_MR));
#endif
#if 0
	(void)intc_intr_establish(aa->aa_dev->irq[0], IPL_HIGH, /* XXX */
	    sxidog_intr, sc, sc->sc_dev.dv_xname);
#endif
	sxidog_sc = sc;

#ifndef SMALL_KERNEL
	wdog_register(sxidog_callback, sc);
#endif

	printf("\n");
}

int
sxidog_activate(struct device *self, int act)
{
	switch (act) {
	case DVACT_POWERDOWN:
#ifndef SMALL_KERNEL
		wdog_shutdown(self);
#endif
		break;
	}

	return (0);
}

int
sxidog_callback(void *arg, int period)
{
	struct sxidog_softc *sc = (struct sxidog_softc *)arg;

	if (period > 0x0b)
		period = 0x0b;
	else if (period < 0)
		period = 0;
	/*
	 * clearing bits in mode reg has no effect according
	 * to the user manual, so just set new timeout and enable it.
	 * XXX
	 */
	SXIWRITE4(sc, WDOG_MR, WDOG_EN | WDOG_RST_EN |
	    WDOG_INTV_VALUE(period));
	/* reset */
	SXIWRITE4(sc, WDOG_CR, WDOG_CTRL_KEY | WDOG_RESTART);

	return period;
}

#if 0
int
sxidog_intr(void *arg)
{
	struct sxidog_softc *sc = (struct sxidog_softc *)arg;

	/* XXX */
	SXIWRITE4(sc, WDOG_CR, WDOG_CTRL_KEY | WDOG_RESTART);
	return 1;
}
#endif

void
sxidog_reset(void)
{
	if (sxidog_sc == NULL)
		return;

	SXIWRITE4(sxidog_sc, WDOG_MR, WDOG_INTV_VALUE(0x00) |
	    WDOG_RST_EN | WDOG_EN);
	SXIWRITE4(sxidog_sc, WDOG_CR, WDOG_CTRL_KEY | WDOG_RESTART);
	delay(900000);
}


code:
/*	$OpenBSD: sxie.c,v 1.9 2015/06/24 09:40:53 mpi Exp $	*/
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 * Copyright (c) 2013 Artturi Alm
 *
 * 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 this should use dedicated dma for RX, atleast */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <sys/mbuf.h>
#include <machine/intr.h>
#include <machine/bus.h>

#include "bpfilter.h"

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>
#include <armv7/sunxi/sxiccmuvar.h>
#include <armv7/sunxi/sxipiovar.h>

/* configuration registers */
#define	SXIE_CR			0x0000
#define	SXIE_TXMODE		0x0004
#define	SXIE_TXFLOW		0x0008
#define	SXIE_TXCR0		0x000c
#define	SXIE_TXCR1		0x0010
#define	SXIE_TXINS		0x0014
#define	SXIE_TXPKTLEN0		0x0018
#define	SXIE_TXPKTLEN1		0x001c
#define	SXIE_TXSR		0x0020
#define	SXIE_TXIO0		0x0024
#define	SXIE_TXIO1		0x0028
#define	SXIE_TXTSVL0		0x002c
#define	SXIE_TXTSVH0		0x0030
#define	SXIE_TXTSVL1		0x0034
#define	SXIE_TXTSVH1		0x0038
#define	SXIE_RXCR		0x003c
#define	SXIE_RXHASH0		0x0040
#define	SXIE_RXHASH1		0x0044
#define	SXIE_RXSR		0x0048
#define	SXIE_RXIO		0x004C
#define	SXIE_RXFBC		0x0050
#define	SXIE_INTCR		0x0054
#define	SXIE_INTSR		0x0058
#define	SXIE_MACCR0		0x005C
#define	SXIE_MACCR1		0x0060
#define	SXIE_MACIPGT		0x0064
#define	SXIE_MACIPGR		0x0068
#define	SXIE_MACCLRT		0x006C
#define	SXIE_MACMFL		0x0070
#define	SXIE_MACSUPP		0x0074
#define	SXIE_MACTEST		0x0078
#define	SXIE_MACMCFG		0x007C
#define	SXIE_MACMCMD		0x0080
#define	SXIE_MACMADR		0x0084
#define	SXIE_MACMWTD		0x0088
#define	SXIE_MACMRDD		0x008C
#define	SXIE_MACMIND		0x0090
#define	SXIE_MACSSRR		0x0094
#define	SXIE_MACA0		0x0098
#define	SXIE_MACA1		0x009c
#define	SXIE_MACA2		0x00a0

/* i once spent hours on pretty defines, cvs up ate 'em. these shall do */
#define SXIE_INTR_ENABLE		0x010f
#define SXIE_INTR_DISABLE	0x0000
#define SXIE_INTR_CLEAR		0x0000

#define	SXIE_RX_ENABLE		0x0004
#define	SXIE_TX_ENABLE		0x0003
#define	SXIE_RXTX_ENABLE		0x0007

#define	SXIE_RXDRQM		0x0002
#define	SXIE_RXTM		0x0004
#define	SXIE_RXFLUSH		0x0008
#define	SXIE_RXPA		0x0010
#define	SXIE_RXPCF		0x0020
#define	SXIE_RXPCRCE		0x0040
#define	SXIE_RXPLE		0x0080
#define	SXIE_RXPOR		0x0100
#define	SXIE_RXUCAD		0x10000
#define	SXIE_RXDAF		0x20000
#define	SXIE_RXMCO		0x100000
#define	SXIE_RXMHF		0x200000
#define	SXIE_RXBCO		0x400000
#define	SXIE_RXSAF		0x1000000
#define	SXIE_RXSAIF		0x2000000

#define	SXIE_MACRXFC		0x0004
#define	SXIE_MACTXFC		0x0008
#define SXIE_MACSOFTRESET	0x8000

#define	SXIE_MACDUPLEX		0x0001	/* full = 1 */
#define	SXIE_MACFLC		0x0002
#define	SXIE_MACHF		0x0004
#define	SXIE_MACDCRC		0x0008
#define	SXIE_MACCRC		0x0010
#define	SXIE_MACPC		0x0020
#define	SXIE_MACVC		0x0040
#define	SXIE_MACADP		0x0080
#define	SXIE_MACPRE		0x0100
#define	SXIE_MACLPE		0x0200
#define	SXIE_MACNB		0x1000
#define	SXIE_MACBNB		0x2000
#define	SXIE_MACED		0x4000

#define	SXIE_RX_ERRLENOOR	0x0040
#define	SXIE_RX_ERRLENCHK	0x0020
#define	SXIE_RX_ERRCRC		0x0010
#define	SXIE_RX_ERRRCV		0x0008 /* XXX receive code violation ? */
#define	SXIE_RX_ERRMASK		0x0070

#define	SXIE_MII_TIMEOUT	100
#define SXIE_MAX_RXD		8
#define SXIE_MAX_PKT_SIZE	ETHER_MAX_DIX_LEN

#define SXIE_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))

struct sxie_softc {
	struct device			sc_dev;
	struct arpcom			sc_ac;
	struct mii_data			sc_mii;
	int				sc_phyno;
	bus_space_tag_t			sc_iot;
	bus_space_handle_t		sc_ioh;
	bus_space_handle_t		sc_sid_ioh;
	void				*sc_ih; /* Interrupt handler */
	uint32_t			intr_status; /* soft interrupt status */
	uint32_t			pauseframe;
	uint32_t			txf_inuse;
};

struct sxie_softc *sxie_sc;

void	sxie_attach(struct device *, struct device *, void *);
void	sxie_setup_interface(struct sxie_softc *, struct device *);
void	sxie_socware_init(struct sxie_softc *);
int	sxie_ioctl(struct ifnet *, u_long, caddr_t);
void	sxie_start(struct ifnet *);
void	sxie_watchdog(struct ifnet *);
void	sxie_init(struct sxie_softc *);
void	sxie_stop(struct sxie_softc *);
void	sxie_reset(struct sxie_softc *);
void	sxie_iff(struct sxie_softc *, struct ifnet *);
struct mbuf * sxie_newbuf(void);
int	sxie_intr(void *);
void	sxie_recv(struct sxie_softc *);
int	sxie_miibus_readreg(struct device *, int, int);
void	sxie_miibus_writereg(struct device *, int, int, int);
void	sxie_miibus_statchg(struct device *);
int	sxie_ifm_change(struct ifnet *);
void	sxie_ifm_status(struct ifnet *, struct ifmediareq *);

struct cfattach sxie_ca = {
	sizeof (struct sxie_softc), NULL, sxie_attach
};

struct cfdriver sxie_cd = {
	NULL, "sxie", DV_IFNET
};

void
sxie_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct sxie_softc *sc = (struct sxie_softc *) self;
	struct mii_data *mii;
	struct ifnet *ifp;
	int s;

	sc->sc_iot = aa->aa_iot;

	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
		panic("sxie_attach: bus_space_map ioh failed!");

	if (bus_space_map(sc->sc_iot, SID_ADDR, SID_SIZE, 0, &sc->sc_sid_ioh))
		panic("sxie_attach: bus_space_map sid_ioh failed!");

	sxie_socware_init(sc);
	sc->txf_inuse = 0;

	sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_NET,
	    sxie_intr, sc, sc->sc_dev.dv_xname);

	s = splnet();

	printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr));

	/* XXX verify flags & capabilities */
	ifp = &sc->sc_ac.ac_if;
	ifp->if_softc = sc;
	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
	ifp->if_ioctl = sxie_ioctl;
	ifp->if_start = sxie_start;
	ifp->if_watchdog = sxie_watchdog;
	ifp->if_capabilities = IFCAP_VLAN_MTU; /* XXX status check in recv? */

	IFQ_SET_MAXLEN(&ifp->if_snd, 1);
	IFQ_SET_READY(&ifp->if_snd);

	/* Initialize MII/media info. */
	mii = &sc->sc_mii;
	mii->mii_ifp = ifp;
	mii->mii_readreg = sxie_miibus_readreg;
	mii->mii_writereg = sxie_miibus_writereg;
	mii->mii_statchg = sxie_miibus_statchg;
	mii->mii_flags = MIIF_AUTOTSLEEP;

	ifmedia_init(&mii->mii_media, 0, sxie_ifm_change, sxie_ifm_status);
	mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);

	if (LIST_FIRST(&mii->mii_phys) == NULL) {
		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
	} else
		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);

	if_attach(ifp);
	ether_ifattach(ifp);
	splx(s);

	sxie_sc = sc;
}

void
sxie_socware_init(struct sxie_softc *sc)
{
	int i, have_mac = 0;
	uint32_t reg;

	for (i = 0; i < SXIPIO_EMAC_NPINS; i++)
		sxipio_setcfg(i, 2); /* mux pins to EMAC */
	sxiccmu_enablemodule(CCMU_EMAC);

	/* MII clock cfg */
	SXICMS4(sc, SXIE_MACMCFG, 15 << 2, 13 << 2);

	SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE);
	SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR);

	/*
	 * If u-boot doesn't set emac, use the Security ID area
	 * to generate a consistent MAC address of.
	 */
	reg = SXIREAD4(sc, SXIE_MACA0);
	if (reg != 0) {
		sc->sc_ac.ac_enaddr[3] = reg >> 16 & 0xff;
		sc->sc_ac.ac_enaddr[4] = reg >> 8 & 0xff;
		sc->sc_ac.ac_enaddr[5] = reg & 0xff;
		reg = SXIREAD4(sc, SXIE_MACA1);
		sc->sc_ac.ac_enaddr[0] = reg >> 16 & 0xff;
		sc->sc_ac.ac_enaddr[1] = reg >> 8 & 0xff;
		sc->sc_ac.ac_enaddr[2] = reg & 0xff;

		have_mac = 1;
	}

	reg = bus_space_read_4(sc->sc_iot, sc->sc_sid_ioh, 0x0);

	if (!have_mac && reg != 0) {
		sc->sc_ac.ac_enaddr[0] = 0x02;
		sc->sc_ac.ac_enaddr[1] = reg & 0xff;
		reg = bus_space_read_4(sc->sc_iot, sc->sc_sid_ioh, 0x0c);
		sc->sc_ac.ac_enaddr[2] = reg >> 24 & 0xff;
		sc->sc_ac.ac_enaddr[3] = reg >> 16 & 0xff;
		sc->sc_ac.ac_enaddr[4] = reg >> 8 & 0xff;
		sc->sc_ac.ac_enaddr[5] = reg & 0xff;

		have_mac = 1;
	}

	if (!have_mac)
		ether_fakeaddr(&sc->sc_ac.ac_if);

	sc->sc_phyno = 1;
}

void
sxie_setup_interface(struct sxie_softc *sc, struct device *dev)
{
	uint32_t clr_m, set_m;

	/* configure TX */
	SXICMS4(sc, SXIE_TXMODE, 3, 1);	/* cpu mode */

	/* configure RX */
	clr_m = SXIE_RXDRQM | SXIE_RXTM | SXIE_RXPA | SXIE_RXPCF |
	    SXIE_RXPCRCE | SXIE_RXPLE | SXIE_RXMHF | SXIE_RXSAF |
	    SXIE_RXSAIF;
	set_m = SXIE_RXPOR | SXIE_RXUCAD | SXIE_RXDAF | SXIE_RXBCO;
	SXICMS4(sc, SXIE_RXCR, clr_m, set_m);

	/* configure MAC */
	SXISET4(sc, SXIE_MACCR0, SXIE_MACTXFC | SXIE_MACRXFC);
	clr_m =	SXIE_MACHF | SXIE_MACDCRC | SXIE_MACVC | SXIE_MACADP |
	    SXIE_MACPRE | SXIE_MACLPE | SXIE_MACNB | SXIE_MACBNB |
	    SXIE_MACED;
	set_m = SXIE_MACFLC | SXIE_MACCRC | SXIE_MACPC;
	set_m |= sxie_miibus_readreg(dev, sc->sc_phyno, 0) >> 8 & 1;
	SXICMS4(sc, SXIE_MACCR1, clr_m, set_m);

	/* XXX */
	SXIWRITE4(sc, SXIE_MACIPGT, 0x0015);
	SXIWRITE4(sc, SXIE_MACIPGR, 0x0c12);

	/* XXX set collision window */
	SXIWRITE4(sc, SXIE_MACCLRT, 0x370f);

	/* set max frame length */
	SXIWRITE4(sc, SXIE_MACMFL, SXIE_MAX_PKT_SIZE);

	/* set lladdr */
	SXIWRITE4(sc, SXIE_MACA0,
	    sc->sc_ac.ac_enaddr[3] << 16 |
	    sc->sc_ac.ac_enaddr[4] << 8 |
	    sc->sc_ac.ac_enaddr[5]);
	SXIWRITE4(sc, SXIE_MACA1,
	    sc->sc_ac.ac_enaddr[0] << 16 |
	    sc->sc_ac.ac_enaddr[1] << 8 |
	    sc->sc_ac.ac_enaddr[2]);

	sxie_reset(sc);
	/* XXX possibly missing delay in here. */
}

void
sxie_init(struct sxie_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	struct device *dev = (struct device *)sc;
	int phyreg;

	sxie_reset(sc);

	SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE);

	SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR);

	SXISET4(sc, SXIE_RXCR, SXIE_RXFLUSH);

	/* soft reset */
	SXICLR4(sc, SXIE_MACCR0, SXIE_MACSOFTRESET);

	/* zero rx counter */
	SXIWRITE4(sc, SXIE_RXFBC, 0);

	sxie_setup_interface(sc, dev);

	/* power up PHY */
	sxie_miibus_writereg(dev, sc->sc_phyno, 0,
	    sxie_miibus_readreg(dev, sc->sc_phyno, 0) & ~(1 << 11));
	delay(1000);
	phyreg = sxie_miibus_readreg(dev, sc->sc_phyno, 0);

	/* set duplex */
	SXICMS4(sc, SXIE_MACCR1, 1, phyreg >> 8 & 1);

	/* set speed */
	SXICMS4(sc, SXIE_MACSUPP, 1 << 8, (phyreg >> 13 & 1) << 8);

	SXISET4(sc, SXIE_CR, SXIE_RXTX_ENABLE);

	/* Indicate we are up and running. */
	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	SXISET4(sc, SXIE_INTCR, SXIE_INTR_ENABLE);

	sxie_start(ifp);
}

int
sxie_intr(void *arg)
{
	struct sxie_softc *sc = arg;
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	uint32_t pending;

	SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE);

	pending = SXIREAD4(sc, SXIE_INTSR);
	SXIWRITE4(sc, SXIE_INTSR, pending);

	/*
	 * Handle incoming packets.
	 */
	if (pending & 0x0100) {
		if (ifp->if_flags & IFF_RUNNING)
			sxie_recv(sc);
	}

	pending &= 3;

	if (pending) {
		ifp->if_flags &= ~IFF_OACTIVE;
		sc->txf_inuse--;
		ifp->if_opackets++;
		if (pending == 3) { /* 2 packets got sent */
			sc->txf_inuse--;
			ifp->if_opackets++;
		}
		if (sc->txf_inuse == 0)
			ifp->if_timer = 0;
		else
			ifp->if_timer = 5;
	}

	if (ifp->if_flags & IFF_RUNNING && !IFQ_IS_EMPTY(&ifp->if_snd))
		sxie_start(ifp);

	SXISET4(sc, SXIE_INTCR, SXIE_INTR_ENABLE);

	return 1;
}

/*
 * XXX there's secondary tx fifo to be used.
 */
void
sxie_start(struct ifnet *ifp)
{
	struct sxie_softc *sc = ifp->if_softc;
	struct mbuf *m;
	struct mbuf *head;
	uint8_t *td;
	uint32_t fifo;
	uint32_t txbuf[SXIE_MAX_PKT_SIZE / sizeof(uint32_t)]; /* XXX !!! */

	if (sc->txf_inuse > 1)
		ifp->if_flags |= IFF_OACTIVE;

	if ((ifp->if_flags & (IFF_OACTIVE | IFF_RUNNING)) != IFF_RUNNING)
		return;

	td = (uint8_t *)&txbuf[0];
	m = NULL;
	head = NULL;
trynext:
	IFQ_POLL(&ifp->if_snd, m);
	if (m == NULL)
		return;

	if (m->m_pkthdr.len > SXIE_MAX_PKT_SIZE) {
		printf("sxie_start: packet too big\n");
		m_freem(m);
		return;
	}

	if (sc->txf_inuse > 1) {
		printf("sxie_start: tx fifos in use.\n");
		ifp->if_flags |= IFF_OACTIVE;
		return;
	}

	/* select fifo */
	fifo = sc->txf_inuse;
	SXIWRITE4(sc, SXIE_TXINS, fifo);

	sc->txf_inuse++;

	/* set packet length */
	SXIWRITE4(sc, SXIE_TXPKTLEN0 + (fifo * 4), m->m_pkthdr.len);

	/* copy the actual packet to fifo XXX through 'align buffer'.. */
	m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)td);
	bus_space_write_multi_4(sc->sc_iot, sc->sc_ioh,
	    SXIE_TXIO0 + (fifo * 4),
	    (uint32_t *)td, SXIE_ROUNDUP(m->m_pkthdr.len, 4) >> 2);

	/* transmit to PHY from fifo */
	SXISET4(sc, SXIE_TXCR0 + (fifo * 4), 1);
	ifp->if_timer = 5;
	IFQ_DEQUEUE(&ifp->if_snd, m);

#if NBPFILTER > 0
	if (ifp->if_bpf)
		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif
	m_freem(m);

	goto trynext;
}

void
sxie_stop(struct sxie_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ac.ac_if;

	sxie_reset(sc);

	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
	ifp->if_timer = 0;
}

void
sxie_reset(struct sxie_softc *sc)
{
	/* reset the controller */
	SXIWRITE4(sc, SXIE_CR, 0);
	delay(200);
	SXIWRITE4(sc, SXIE_CR, 1);
	delay(200);
}

void
sxie_watchdog(struct ifnet *ifp)
{
	struct sxie_softc *sc = ifp->if_softc;
	if (sc->pauseframe) {
		ifp->if_timer = 5;
		return;
	}
	printf("%s: watchdog tx timeout\n", sc->sc_dev.dv_xname);
	ifp->if_oerrors++;
	sxie_init(sc);
	sxie_start(ifp);
}

/*
 * XXX DMA?
 */
void
sxie_recv(struct sxie_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	uint32_t fbc, reg;
	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
	struct mbuf *m;
	uint16_t pktstat;
	int16_t pktlen;
	int rlen;
	char rxbuf[SXIE_MAX_PKT_SIZE]; /* XXX !!! */
trynext:
	fbc = SXIREAD4(sc, SXIE_RXFBC);
	if (!fbc)
		goto done;

	/*
	 * first bit of MSB is packet valid flag,
	 * it is 'padded' with 0x43414d = "MAC"
	 */
	reg = SXIREAD4(sc, SXIE_RXIO);
	if (reg != 0x0143414d) {	/* invalid packet */
		/* disable, flush, enable */
		SXICLR4(sc, SXIE_CR, SXIE_RX_ENABLE);
		SXISET4(sc, SXIE_RXCR, SXIE_RXFLUSH);
		while (SXIREAD4(sc, SXIE_RXCR) & SXIE_RXFLUSH);
		SXISET4(sc, SXIE_CR, SXIE_RX_ENABLE);

		goto err_out;
	}

	m = sxie_newbuf();
	if (m == NULL)
		goto err_out;

	reg = SXIREAD4(sc, SXIE_RXIO);
	pktstat = (uint16_t)reg >> 16;
	pktlen = (int16_t)reg; /* length of useful data */

	if (pktstat & SXIE_RX_ERRMASK || pktlen < ETHER_MIN_LEN) {
		ifp->if_ierrors++;
		goto trynext;
	}
	if (pktlen > SXIE_MAX_PKT_SIZE)
		pktlen = SXIE_MAX_PKT_SIZE; /* XXX is truncating ok? */

	m->m_pkthdr.len = m->m_len = pktlen;
	/* XXX m->m_pkthdr.csum_flags ? */
	m_adj(m, ETHER_ALIGN);

	/* read the actual packet from fifo XXX through 'align buffer'.. */
	if (pktlen & 3)
		rlen = SXIE_ROUNDUP(pktlen, 4);
	else
		rlen = pktlen;
	bus_space_read_multi_4(sc->sc_iot, sc->sc_ioh,
	    SXIE_RXIO, (uint32_t *)&rxbuf[0], rlen >> 2);
	memcpy(mtod(m, char *), (char *)&rxbuf[0], pktlen);

	ml_enqueue(&ml, m);
	goto trynext;
err_out:
	ifp->if_ierrors++;
done:
	if_input(ifp, &ml);
}

int
sxie_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
	struct sxie_softc *sc = ifp->if_softc;
	struct ifreq *ifr = (struct ifreq *)data;
	struct ifaddr *ifa = (struct ifaddr *)data;
	int s, error = 0;

	s = splnet();

	switch (cmd) {
	case SIOCSIFADDR:
		if (!(ifp->if_flags & IFF_UP)) {
			ifp->if_flags |= IFF_UP;
			sxie_init(sc);
		}
		if (ifa->ifa_addr->sa_family == AF_INET)
			arp_ifinit(&sc->sc_ac, ifa);
		break;
	case SIOCSIFFLAGS:
		if (ifp->if_flags & IFF_UP) {
			if (ifp->if_flags & IFF_RUNNING)
				error = ENETRESET;
			else
				sxie_init(sc);
		} else if (ifp->if_flags & IFF_RUNNING)
			sxie_stop(sc);
		break;
	case SIOCGIFMEDIA:
	case SIOCSIFMEDIA:
		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
		break;
	default:
		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
	}
	if (error == ENETRESET) {
		if (ifp->if_flags & IFF_RUNNING)
			sxie_iff(sc, ifp);
		error = 0;
	}

	splx(s);
	return error;
}

struct mbuf *
sxie_newbuf(void)
{
	struct mbuf *m;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return (NULL);

	MCLGET(m, M_DONTWAIT);
	if (!(m->m_flags & M_EXT)) {
		m_freem(m);
		return (NULL);
	}

	return (m);
}

void
sxie_iff(struct sxie_softc *sc, struct ifnet *ifp)
{
	/* XXX set interface features */
}

/*
 * MII
 */
int
sxie_miibus_readreg(struct device *dev, int phy, int reg)
{
	struct sxie_softc *sc = (struct sxie_softc *)dev;
	int timo = SXIE_MII_TIMEOUT;

	SXIWRITE4(sc, SXIE_MACMADR, phy << 8 | reg);

	SXIWRITE4(sc, SXIE_MACMCMD, 1);
	while (SXIREAD4(sc, SXIE_MACMIND) & 1 && --timo)
		delay(10);
#ifdef DIAGNOSTIC
	if (!timo)
		printf("%s: sxie_miibus_readreg timeout.\n",
		    sc->sc_dev.dv_xname);
#endif

	SXIWRITE4(sc, SXIE_MACMCMD, 0);

	return SXIREAD4(sc, SXIE_MACMRDD) & 0xffff;
}

void
sxie_miibus_writereg(struct device *dev, int phy, int reg, int val)
{
	struct sxie_softc *sc = (struct sxie_softc *)dev;
	int timo = SXIE_MII_TIMEOUT;

	SXIWRITE4(sc, SXIE_MACMADR, phy << 8 | reg);

	SXIWRITE4(sc, SXIE_MACMCMD, 1);
	while (SXIREAD4(sc, SXIE_MACMIND) & 1 && --timo)
		delay(10);
#ifdef DIAGNOSTIC
	if (!timo)
		printf("%s: sxie_miibus_readreg timeout.\n",
		    sc->sc_dev.dv_xname);
#endif

	SXIWRITE4(sc, SXIE_MACMCMD, 0);

	SXIWRITE4(sc, SXIE_MACMWTD, val);
}

void
sxie_miibus_statchg(struct device *dev)
{
	/* XXX */
#if 0
	struct sxie_softc *sc = (struct sxie_softc *)dev;

	switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
	case IFM_10_T:
	case IFM_100_TX:
	/*case IFM_1000_T: only on GMAC */
		break;
	default:
		break;
	}
#endif
}

int
sxie_ifm_change(struct ifnet *ifp)
{
	struct sxie_softc *sc = ifp->if_softc;
	struct mii_data *mii = &sc->sc_mii;

	if (mii->mii_instance) {
		struct mii_softc *miisc;

		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
			mii_phy_reset(miisc);
	}
	return mii_mediachg(mii);
}

void
sxie_ifm_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
	struct sxie_softc *sc = (struct sxie_softc *)ifp->if_softc;

	mii_pollstat(&sc->sc_mii);
	ifmr->ifm_active = sc->sc_mii.mii_media_active;
	ifmr->ifm_status = sc->sc_mii.mii_media_status;
}


code:
/*	$OpenBSD: sxiehci.c,v 1.4 2014/05/19 13:11:31 mpi Exp $ */

/*
 * Copyright (c) 2005 David Gwynne <dlg@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.
 */

/*-
 * Copyright (c) 2011
 *	Ben Gray <ben.r.gray@gmail.com>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>
#include <sys/timeout.h>

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

#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sxiccmuvar.h>
#include <armv7/sunxi/sxipiovar.h>

#include <dev/usb/ehcireg.h>
#include <dev/usb/ehcivar.h>

#define EHCI_HC_DEVSTR		"Sunxi Integrated USB 2.0 controller"

#define USB_PMU_IRQ_ENABLE	0x800

#define SDRAM_REG_HPCR_USB1	(0x250 + ((1 << 2) * 4))
#define SDRAM_REG_HPCR_USB2	(0x250 + ((1 << 2) * 5))
#define SDRAM_BP_HPCR_ACCESS	(1 << 0)

#define ULPI_BYPASS		(1 << 0)
#define AHB_INCRX_ALIGN		(1 << 8)
#define AHB_INCR4		(1 << 9)
#define AHB_INCR8		(1 << 10)

void	sxiehci_attach(struct device *, struct device *, void *);
int	sxiehci_detach(struct device *, int);
int	sxiehci_activate(struct device *, int);

struct sxiehci_softc {
	struct ehci_softc	 sc;
	void			*sc_ih;
};

int sxiehci_init(struct sxiehci_softc *, int);

struct cfattach sxiehci_ca = {
	sizeof (struct sxiehci_softc), NULL, sxiehci_attach,
	sxiehci_detach, sxiehci_activate
};

/* XXX
 * given the nature of SoCs, i think this should just panic on failure,
 * instead of disestablishing interrupt and unmapping space etc..
 */
void
sxiehci_attach(struct device *parent, struct device *self, void *aux)
{
	struct sxiehci_softc	*sc = (struct sxiehci_softc *)self;
	struct armv7_attach_args *aa = aux;
	usbd_status		 r;
	char			*devname = sc->sc.sc_bus.bdev.dv_xname;

	sc->sc.iot = aa->aa_iot;
	sc->sc.sc_bus.dmatag = aa->aa_dmat;
	sc->sc.sc_size = aa->aa_dev->mem[0].size;

	if (bus_space_map(sc->sc.iot, aa->aa_dev->mem[0].addr,
		aa->aa_dev->mem[0].size, 0, &sc->sc.ioh)) {
		printf(": cannot map mem space\n");
		goto out;
	}

	printf("\n");

	if (sxiehci_init(sc, self->dv_unit))
		return;

	/* Disable interrupts, so we don't get any spurious ones. */
	sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
	EOWRITE2(&sc->sc, EHCI_USBINTR, 0);

	sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_USB,
	    ehci_intr, &sc->sc, devname);
	if (sc->sc_ih == NULL) {
		printf(": unable to establish interrupt\n");
		printf("XXX - disable ehci");
#if 0
		sxiccmu_disable(CCMU_EHCI+unit?);
#endif
		goto mem0;
	}

	strlcpy(sc->sc.sc_vendor, "Allwinner", sizeof(sc->sc.sc_vendor));
	r = ehci_init(&sc->sc);
	if (r != USBD_NORMAL_COMPLETION) {
		printf("%s: init failed, error=%d\n", devname, r);
		printf("XXX - disable ehci");
#if 0
		sxiccmu_disable(CCMU_EHCI+unit?);
#endif
		goto intr;
	}

	config_found(self, &sc->sc.sc_bus, usbctlprint);

	goto out;

intr:
	arm_intr_disestablish(sc->sc_ih);
	sc->sc_ih = NULL;
mem0:
	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
	sc->sc.sc_size = 0;
out:
	return;
}

int
sxiehci_init(struct sxiehci_softc *sc, int unit)
{
	uint32_t r, val;
	int pin, mod;

	if (unit > 1)
		panic("sxiehci_init: unit >1 %d", unit);
	else if (unit == 0) {
		pin = SXIPIO_USB1_PWR;
		r = SDRAM_REG_HPCR_USB1;
		mod = CCMU_EHCI0;
	} else {
		pin = SXIPIO_USB2_PWR;
		r = SDRAM_REG_HPCR_USB2;
		mod = CCMU_EHCI1;
	}

	sxiccmu_enablemodule(mod);

	/* power up */
	sxipio_setcfg(pin, SXIPIO_OUTPUT);
	sxipio_setpin(pin);

	val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USB_PMU_IRQ_ENABLE);
	val |= AHB_INCR8;	/* AHB INCR8 enable */
	val |= AHB_INCR4;	/* AHB burst type INCR4 enable */
	val |= AHB_INCRX_ALIGN;	/* AHB INCRX align enable */
	val |= ULPI_BYPASS;	/* ULPI bypass enable */
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USB_PMU_IRQ_ENABLE, val);

	val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, r);
	val |= SDRAM_BP_HPCR_ACCESS;
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, r, val);

	return (0);
}

int
sxiehci_detach(struct device *self, int flags)
{
	struct sxiehci_softc *sc = (struct sxiehci_softc *)self;
	int rv;

	rv = ehci_detach(self, flags);
	if (rv)
		return (rv);

	if (sc->sc_ih != NULL) {
		arm_intr_disestablish(sc->sc_ih);
		sc->sc_ih = NULL;
	}

	if (sc->sc.sc_size) {
		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
		sc->sc.sc_size = 0;
	}
	/* XXX */
#if 0
	sxiccmu_disable(CCMU_EHCI+unit?);
	sxipio_clrpin(pin);
#endif
	return (0);
}

int
sxiehci_activate(struct device *self, int act)
{
	struct sxiehci_softc *sc = (struct sxiehci_softc *)self;

	switch (act) {
	case DVACT_SUSPEND:
		sc->sc.sc_bus.use_polling++;
		/* FIXME */
		sc->sc.sc_bus.use_polling--;
		break;
	case DVACT_RESUME:
		sc->sc.sc_bus.use_polling++;
		/* FIXME */
		sc->sc.sc_bus.use_polling--;
		break;
	case DVACT_POWERDOWN:
		ehci_reset(&sc->sc);
		break;
	}
	return 0;
}


code:
/*	$OpenBSD: sxipio.c,v 1.7 2015/05/20 03:49:23 jsg Exp $	*/
/*
 * Copyright (c) 2010 Miodrag Vallat.
 * Copyright (c) 2013 Artturi Alm
 *
 * 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/gpio.h>
#include <sys/evcount.h>

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

#include <dev/gpio/gpiovar.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>
#include <armv7/sunxi/sxipiovar.h>

#include "gpio.h"

#define	SXIPIO_NPORT		9
#define	SXIPIO_PA_NPIN		18
#define	SXIPIO_PB_NPIN		24
#define	SXIPIO_PC_NPIN		25
#define	SXIPIO_PD_NPIN		28
#define	SXIPIO_PE_NPIN		12
#define	SXIPIO_PF_NPIN		6
#define	SXIPIO_PG_NPIN		12
#define	SXIPIO_PH_NPIN		28
#define	SXIPIO_PI_NPIN		22
#define	SXIPIO_NPIN	(SXIPIO_PA_NPIN + SXIPIO_PB_NPIN + SXIPIO_PC_NPIN + \
	SXIPIO_PD_NPIN + SXIPIO_PE_NPIN + SXIPIO_PF_NPIN + SXIPIO_PG_NPIN + \
	SXIPIO_PH_NPIN + SXIPIO_PI_NPIN)
#define	SXIPIO_PS_NPIN		84 /* for DRAM controller */

struct intrhand {
	int (*ih_func)(void *);		/* handler */
	void *ih_arg;			/* arg for handler */
	int ih_ipl;			/* IPL_* */
	int ih_irq;			/* IRQ number */
	int ih_gpio;			/* gpio pin */
	struct evcount ih_count;
	char *ih_name;
};

struct sxipio_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	void			*sc_ih_h;
	void			*sc_ih_l;
	int 			sc_max_il;
	int 			sc_min_il;
	int			sc_irq;

	struct gpio_chipset_tag	 sc_gpio_tag[SXIPIO_NPORT];
	gpio_pin_t		 sc_gpio_pins[SXIPIO_NPORT][32];

	struct intrhand		*sc_handlers[32];
};

#define	SXIPIO_CFG(port, pin)	0x00 + ((port) * 0x24) + ((pin) << 2)
#define	SXIPIO_DAT(port)		0x10 + ((port) * 0x24)
/* XXX add support for registers below */
#define	SXIPIO_DRV(port, pin)	0x14 + ((port) * 0x24) + ((pin) << 2)
#define	SXIPIO_PUL(port, pin)	0x1c + ((port) * 0x24) + ((pin) << 2)
#define	SXIPIO_INT_CFG0(port)	0x0200 + ((port) * 0x04)
#define	SXIPIO_INT_CTL		0x0210
#define	SXIPIO_INT_STA		0x0214
#define	SXIPIO_INT_DEB		0x0218 /* debounce register */

void sxipio_attach(struct device *, struct device *, void *);
void sxipio_attach_gpio(struct device *);

struct cfattach sxipio_ca = {
	sizeof (struct sxipio_softc), NULL, sxipio_attach
};

struct cfdriver sxipio_cd = {
	NULL, "sxipio", DV_DULL
};

struct sxipio_softc	*sxipio_sc = NULL;
bus_space_tag_t		 sxipio_iot;
bus_space_handle_t	 sxipio_ioh;

void
sxipio_attach(struct device *parent, struct device *self, void *args)
{
	struct sxipio_softc *sc = (struct sxipio_softc *)self;
	struct armv7_attach_args *aa = args;

	/* XXX check unit, bail if != 0 */

	sc->sc_iot = sxipio_iot = aa->aa_iot;
	if (bus_space_map(sxipio_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
		panic("sxipio_attach: bus_space_map failed!");
	sxipio_ioh = sc->sc_ioh;

	sxipio_sc = sc;

	sc->sc_irq = aa->aa_dev->irq[0];

	config_defer(self, sxipio_attach_gpio);

	printf("\n");
}

/*
 * GPIO support code
 */

int	sxipio_pin_read(void *, int);
void	sxipio_pin_write(void *, int, int);
void	sxipio_pin_ctl(void *, int, int);

static const struct gpio_chipset_tag sxipio_gpio_tag = {
	.gp_pin_read = sxipio_pin_read,
	.gp_pin_write = sxipio_pin_write,
	.gp_pin_ctl = sxipio_pin_ctl
};

int
sxipio_pin_read(void *portno, int pin)
{
	return sxipio_getpin((*(uint32_t *)portno * 32) + pin)
	    ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
}

void
sxipio_pin_write(void *portno, int pin, int val)
{
	if (val)
		sxipio_setpin((*(uint32_t *)portno * 32) + pin);
	else
		sxipio_clrpin((*(uint32_t *)portno * 32) + pin);
}

void
sxipio_pin_ctl(void *portno, int pin, int flags)
{
	if (ISSET(flags, GPIO_PIN_OUTPUT))
		sxipio_setcfg((*(uint32_t *)portno * 32) + pin, SXIPIO_OUTPUT);
	else
		sxipio_setcfg((*(uint32_t *)portno * 32) + pin, SXIPIO_INPUT);
}

/* XXX ugly, but cookie has no other purposeful use. */
static const uint32_t sxipio_ports[SXIPIO_NPORT] = {
	0, 1, 2, 3, 4, 5, 6, 7, 8
};

static const int sxipio_last_pin[SXIPIO_NPORT] = {
	SXIPIO_PA_NPIN,
	SXIPIO_PB_NPIN,
	SXIPIO_PC_NPIN,
	SXIPIO_PD_NPIN,
	SXIPIO_PE_NPIN,
	SXIPIO_PF_NPIN,
	SXIPIO_PG_NPIN,
	SXIPIO_PH_NPIN,
	SXIPIO_PI_NPIN
};

void
sxipio_attach_gpio(struct device *parent)
{
	struct sxipio_softc *sc = (struct sxipio_softc *)parent;
	struct gpiobus_attach_args gba;
	int cfg, pin, port;
	/* get value & state of enabled pins, and disable those in use */
	pin = 0;
	port = 0;
next:
	sc->sc_gpio_pins[port][pin].pin_num = pin;
	cfg = sxipio_getcfg((port * 32) + pin);
#if DEBUG
	printf("port %d pin %d cfg %d\n", port, pin, cfg);
#endif
	if (cfg < 2) {
		sc->sc_gpio_pins[port][pin].pin_caps =
		    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
		sc->sc_gpio_pins[port][pin].pin_state =
		    sxipio_getpin((port * 32) + pin);
		sc->sc_gpio_pins[port][pin].pin_flags = GPIO_PIN_SET |
		    (cfg ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT);
	} else {
		/* disable control of taken over pins */
		sc->sc_gpio_pins[port][pin].pin_caps = 0;
		sc->sc_gpio_pins[port][pin].pin_state = 0;
		sc->sc_gpio_pins[port][pin].pin_flags = 0;
	}

	if (++pin < sxipio_last_pin[port])
		goto next;

	bcopy(&sxipio_gpio_tag, &sc->sc_gpio_tag[port], sizeof(sxipio_gpio_tag));
	sc->sc_gpio_tag[port].gp_cookie = (void *)&sxipio_ports[port];
	gba.gba_name = "gpio";
	gba.gba_gc = &sc->sc_gpio_tag[port];
	gba.gba_pins = &sc->sc_gpio_pins[port][0];
	gba.gba_npins = pin;

#if NGPIO > 0
	config_found(&sc->sc_dev, &gba, gpiobus_print);
#endif

	pin = 0;
	if (++port < SXIPIO_NPORT)
		goto next;
}

/*
 * Lower Port I/O for MD code under arch/armv7/sunxi.
 * XXX see the comment in sxipiovar.h
 */
int
sxipio_getcfg(int pin)
{
	struct sxipio_softc *sc = sxipio_sc;
	uint32_t bit, data, off, reg, port;
	int s;

	port = pin >> 5;
	bit = pin - (port << 5);
	reg = SXIPIO_CFG(port, bit >> 3);
	off = (bit & 7) << 2;

	s = splhigh();

	data = SXIREAD4(sc, reg);

	splx(s);

	return data >> off & 7;
}

void
sxipio_setcfg(int pin, int mux)
{
	struct sxipio_softc *sc = sxipio_sc;
	uint32_t bit, cmask, mask, off, reg, port;
	int s;

	port = pin >> 5;
	bit = pin - (port << 5);
	reg = SXIPIO_CFG(port, bit >> 3);
	off = (bit & 7) << 2;
	cmask = 7 << off;
	mask = mux << off;

	s = splhigh();

	SXICMS4(sc, reg, cmask, mask);

	splx(s);
}

int
sxipio_getpin(int pin)
{
	struct sxipio_softc *sc = sxipio_sc;
	uint32_t bit, data, mask, reg, port;
	int s;

	port = pin >> 5;
	bit = pin - (port << 5);
	reg = SXIPIO_DAT(port);
	mask = 1 << (bit & 31);

	s = splhigh();

	data = SXIREAD4(sc, reg);

	splx(s);

	return data & mask ? 1 : 0;
}

void
sxipio_setpin(int pin)
{
	struct sxipio_softc *sc = sxipio_sc;
	uint32_t bit, mask, reg, port;
	int s;

	port = pin >> 5;
	bit = pin - (port << 5);
	reg = SXIPIO_DAT(port);
	mask = 1 << (bit & 31);

	s = splhigh();

	SXISET4(sc, reg, mask);

	splx(s);
}

void
sxipio_clrpin(int pin)
{
	struct sxipio_softc *sc = sxipio_sc;
	uint32_t bit, mask, reg, port;
	int s;

	port = pin >> 5;
	bit = pin - (port << 5);
	reg = SXIPIO_DAT(port);
	mask = 1 << (bit & 31);

	s = splhigh();

	SXICLR4(sc, reg, mask);

	splx(s);
}

int
sxipio_togglepin(int pin)
{
	struct sxipio_softc *sc = sxipio_sc;
	uint32_t bit, data, mask, reg, port;
	int s;

	port = pin >> 5;
	bit = pin - (port << 5);
	reg = SXIPIO_DAT(port);
	mask = 1 << (bit & 31);

	s = splhigh();

	data = SXIREAD4(sc, reg);
	SXIWRITE4(sc, reg, data ^ mask);

	splx(s);

	return data & mask ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
}


code:
/*	$OpenBSD: sxipiovar.h,v 1.3 2015/05/20 03:49:23 jsg Exp $	*/
/*
 * Copyright (c) 2013 Artturi Alm
 *
 * 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/gpio.h>

/*
 * XXX To keep things simple for now, functions below work as if there
 * is 32pins per port, this needs to be taken into account when writing
 * these pin defines.
 */

#define	SXIPIO_INPUT		0
#define	SXIPIO_OUTPUT		1

#define SXIPIO_USB1_PWR		230
#define SXIPIO_USB2_PWR		227
#define SXIPIO_SATA_PWR		40
#define	SXIPIO_EMAC_NPINS	18	/* PORTA 0-17 */

int sxipio_getcfg(int);
void sxipio_setcfg(int, int);
int sxipio_getpin(int);
void sxipio_setpin(int);
void sxipio_clrpin(int);
int sxipio_togglepin(int);


code:
/*	$OpenBSD: sxirtc.c,v 1.4 2014/04/25 09:49:33 jsg Exp $	*/
/*
 * Copyright (c) 2008 Mark Kettenis
 * Copyright (c) 2013 Artturi Alm
 *
 * 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.
 */
/* XXX this doesn't support A20 yet. */
	/* year & 0xff on A20, 0x3f on A10 */
	/* leap << 24 on A20, << 22 on A10 */

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

#include <dev/clock_subr.h>

#include <machine/bus.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/sunxi/sunxireg.h>

#define	SXIRTC_YYMMDD	0x00
#define	SXIRTC_HHMMSS	0x04

#define LEAPYEAR(y)        \
    (((y) % 4 == 0 &&    \
    (y) % 100 != 0) ||    \
    (y) % 400 == 0)

/* XXX other way around than bus_space_subregion? */
extern bus_space_handle_t sxitimer_ioh;

extern todr_chip_handle_t todr_handle;

struct sxirtc_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

void	sxirtc_attach(struct device *, struct device *, void *);

struct cfattach sxirtc_ca = {
	sizeof(struct device), NULL, sxirtc_attach
};

struct cfdriver sxirtc_cd = {
	NULL, "sxirtc", DV_DULL
};

int	sxirtc_gettime(todr_chip_handle_t, struct timeval *);
int	sxirtc_settime(todr_chip_handle_t, struct timeval *);

void
sxirtc_attach(struct device *parent, struct device *self, void *args)
{
	struct sxirtc_softc *sc = (struct sxirtc_softc *)self;
	struct armv7_attach_args *aa = args;
	todr_chip_handle_t handle;

	handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
	if (handle == NULL)
		panic("sxirtc_attach: couldn't allocate todr_handle");

	sc->sc_iot = aa->aa_iot;
	if (bus_space_subregion(sc->sc_iot, sxitimer_ioh,
	    aa->aa_dev->mem[0].addr, aa->aa_dev->mem[0].size, &sc->sc_ioh))
		panic("sxirtc_attach: bus_space_subregion failed!");

	handle->cookie = self;
	handle->todr_gettime = sxirtc_gettime;
	handle->todr_settime = sxirtc_settime;
	handle->todr_getcal = NULL;
	handle->todr_setcal = NULL;
	handle->bus_cookie = NULL;
	handle->todr_setwen = NULL;
	todr_handle = handle;

	printf("\n");
}

int
sxirtc_gettime(todr_chip_handle_t handle, struct timeval *tv)
{
	struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie;
	struct clock_ymdhms dt;
	uint32_t reg;

	reg = SXIREAD4(sc, SXIRTC_HHMMSS);
	dt.dt_sec = reg & 0x3f;
	dt.dt_min = reg >> 8 & 0x3f;
	dt.dt_hour = reg >> 16 & 0x1f;
	dt.dt_wday = reg >> 29 & 0x07;

	reg = SXIREAD4(sc, SXIRTC_YYMMDD);
	dt.dt_day = reg & 0x1f;
	dt.dt_mon = reg >> 8 & 0x0f;
	dt.dt_year = (reg >> 16 & 0x3f) + 2010; /* 0xff on A20 */

	if (dt.dt_sec > 59 || dt.dt_min > 59 ||
	    dt.dt_hour > 23 || dt.dt_wday > 6 ||
	    dt.dt_day > 31 || dt.dt_day == 0 ||
	    dt.dt_mon > 12 || dt.dt_mon == 0)
		return 1;

	tv->tv_sec = clock_ymdhms_to_secs(&dt);
	tv->tv_usec = 0;
	return 0;
}

int
sxirtc_settime(todr_chip_handle_t handle, struct timeval *tv)
{
	struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie;
	struct clock_ymdhms dt;

	clock_secs_to_ymdhms(tv->tv_sec, &dt);

	if (dt.dt_sec > 59 || dt.dt_min > 59 ||
	    dt.dt_hour > 23 || dt.dt_wday > 6 ||
	    dt.dt_day > 31 || dt.dt_day == 0 ||
	    dt.dt_mon > 12 || dt.dt_mon == 0)
		return 1;

	SXICMS4(sc, SXIRTC_HHMMSS, 0xe0000000 | 0x1f0000 | 0x3f00 | 0x3f,
	    dt.dt_sec | (dt.dt_min << 8) | (dt.dt_hour << 16) |
	    (dt.dt_wday << 29));

	SXICMS4(sc, SXIRTC_YYMMDD, 0x00400000 | 0x003f0000 | 0x0f00 | 0x1f,
	   dt.dt_day | (dt.dt_mon << 8) | ((dt.dt_year - 2010) << 16) |
	   (LEAPYEAR(dt.dt_year) << 22));

	return 0;
}