code:
/* $OpenBSD: imx6.c,v 1.3 2015/05/30 08:09:19 jsg Exp $ */
/*
 * Copyright (c) 2011 Uwe Stuehler <uwe@openbsd.org>
 * Copyright (c) 2012 Patrick Wildt <patrick@blueri.se>
 *
 * 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 <machine/bus.h>
#include <arch/arm/armv7/armv7var.h>

#include <armv7/armv7/armv7var.h>

/* IRQs are defined without the 32 cpu IRQs */

#define CCM_ADDR	0x020c4000
#define CCM_SIZE	0x5000

#define CCM_IRQ1	87
#define CCM_IRQ2	88

#define ANALOG_ADDR	0x020c8000
#define ANALOG_SIZE	0x1000

#define IOMUXC_ADDR	0x020e0000
#define IOMUXC_SIZE	0x4000

#define WD1_ADDR	0x020bc000
#define WD1_SIZE	0x400
#define WD2_ADDR	0x020c0000
#define WD2_SIZE	0x400

#define OCOTP_ADDR	0x021bc000
#define OCOTP_SIZE	0x4000

#define UARTx_SIZE	0x4000
#define UART1_ADDR	0x02020000
#define UART2_ADDR	0x021e8000
#define UART3_ADDR	0x021ec000
#define UART4_ADDR	0x021f0000
#define UART5_ADDR	0x021f4000

#define UART1_IRQ	26
#define UART2_IRQ	27
#define UART3_IRQ	28
#define UART4_IRQ	29
#define UART5_IRQ	30

#define USBPHYx_SIZE		0x1000
#define USBPHY1_ADDR		0x020c9000
#define USBPHY2_ADDR		0x020ca000
#define USBOTG_ADDR		0x02184000
#define USBOTG_EHCI_ADDR	0x02184100
#define USBUH1_ADDR		0x02184200
#define USBUH1_EHCI_ADDR	0x02184300
#define USBUH2_ADDR		0x02184400
#define USBUH2_EHCI_ADDR	0x02184500
#define USBUH3_ADDR		0x02184600
#define USBUH3_EHCI_ADDR	0x02184700
#define USBNC_ADDR		0x02184800
#define USBx_SIZE		0x100

#define USBH1_IRQ	40
#define USBH2_IRQ	41
#define USBH3_IRQ	42
#define USBOTG_IRQ	43
#define USBPHY0_IRQ	44
#define USBPHY1_IRQ	45

#define GPIOx_SIZE	0x4000
#define GPIO1_ADDR	0x0209c000
#define GPIO2_ADDR	0x020a0000
#define GPIO3_ADDR	0x020a4000
#define GPIO4_ADDR	0x020a8000
#define GPIO5_ADDR	0x020ac000
#define GPIO6_ADDR	0x020b0000
#define GPIO7_ADDR	0x020b4000

#define GPIO1_IRQ7	58
#define GPIO1_IRQ6	59
#define GPIO1_IRQ5	60
#define GPIO1_IRQ4	61
#define GPIO1_IRQ3	62
#define GPIO1_IRQ2	63
#define GPIO1_IRQ1	64
#define GPIO1_IRQ0	65
#define GPIO1_IRQ16	66
#define GPIO1_IRQ32	67
#define GPIO2_IRQ16	68
#define GPIO2_IRQ32	69
#define GPIO3_IRQ16	70
#define GPIO3_IRQ32	71
#define GPIO4_IRQ16	72
#define GPIO4_IRQ32	73
#define GPIO5_IRQ16	74
#define GPIO5_IRQ32	75
#define GPIO6_IRQ16	76
#define GPIO6_IRQ32	77
#define GPIO7_IRQ16	78
#define GPIO7_IRQ32	79

#define I2Cx_SIZE	0x4000
#define I2C1_ADDR	0x021a0000
#define I2C2_ADDR	0x021a4000
#define I2C3_ADDR	0x021a8000

#define I2C1_IRQ	36
#define I2C2_IRQ	37
#define I2C3_IRQ	38

#define ESDHCx_SIZE	0x4000
#define ESDHC1_ADDR	0x02190000
#define ESDHC2_ADDR	0x02194000
#define ESDHC3_ADDR	0x02198000
#define ESDHC4_ADDR	0x0219c000

#define ESDHC1_IRQ	22
#define ESDHC2_IRQ	23
#define ESDHC3_IRQ	24
#define ESDHC4_IRQ	25

#define ENET_ADDR	0x02188000
#define ENET_SIZE	0x4000

#define ENET_IRQ0	118
#define ENET_IRQ1	119

#define SATA_ADDR	0x02200000
#define SATA_SIZE	0x4000

#define SATA_IRQ	39

#define PCIE_REG_ADDR	0x01ffc000
#define PCIE_REG_SIZE	0x4000
#define PCIE_MAP_ADDR	0x01000000
#define PCIE_MAP_SIZE	0xffc000

#define PCIE_IRQ0	120
#define PCIE_IRQ1	121
#define PCIE_IRQ2	122
#define PCIE_IRQ3	123

struct armv7_dev imx6_devs[] = {

	/*
	 * Clock Control Module
	 */
	{ .name = "imxccm",
	  .unit = 0,
	  .mem = { { CCM_ADDR, CCM_SIZE } },
	},

	/*
	 * IOMUX Controller
	 */
	{ .name = "imxiomuxc",
	  .unit = 0,
	  .mem = { { IOMUXC_ADDR, IOMUXC_SIZE } },
	},

	/*
	 * Watchdog Timer
	 */
	{ .name = "imxdog",
	  .unit = 0,
	  .mem = {
	    { WD1_ADDR, WD1_SIZE },
	    { WD2_ADDR, WD2_SIZE },
	  },
	},

	/*
	 * On-Chip OTP Controller
	 */
	{ .name = "imxocotp",
	  .unit = 0,
	  .mem = { { OCOTP_ADDR, OCOTP_SIZE } },
	},

	/*
	 * UART
	 */
	{ .name = "imxuart",
	  .unit = 0,
	  .mem = { { UART1_ADDR, UARTx_SIZE } },
	  .irq = { UART1_IRQ }
	},
	{ .name = "imxuart",
	  .unit = 1,
	  .mem = { { UART2_ADDR, UARTx_SIZE } },
	  .irq = { UART2_IRQ }
	},
	{ .name = "imxuart",
	  .unit = 2,
	  .mem = { { UART3_ADDR, UARTx_SIZE } },
	  .irq = { UART3_IRQ }
	},
	{ .name = "imxuart",
	  .unit = 3,
	  .mem = { { UART4_ADDR, UARTx_SIZE } },
	  .irq = { UART4_IRQ }
	},
	{ .name = "imxuart",
	  .unit = 4,
	  .mem = { { UART5_ADDR, UARTx_SIZE } },
	  .irq = { UART5_IRQ }
	},

	/*
	 * GPIO
	 */
	{ .name = "imxgpio",
	  .unit = 0,
	  .mem = { { GPIO1_ADDR, GPIOx_SIZE } },
	},

	{ .name = "imxgpio",
	  .unit = 1,
	  .mem = { { GPIO2_ADDR, GPIOx_SIZE } },
	},

	{ .name = "imxgpio",
	  .unit = 2,
	  .mem = { { GPIO3_ADDR, GPIOx_SIZE } },
	},

	{ .name = "imxgpio",
	  .unit = 3,
	  .mem = { { GPIO4_ADDR, GPIOx_SIZE } },
	},

	{ .name = "imxgpio",
	  .unit = 4,
	  .mem = { { GPIO5_ADDR, GPIOx_SIZE } },
	},

	{ .name = "imxgpio",
	  .unit = 5,
	  .mem = { { GPIO6_ADDR, GPIOx_SIZE } },
	},

	{ .name = "imxgpio",
	  .unit = 6,
	  .mem = { { GPIO7_ADDR, GPIOx_SIZE } },
	},

	/*
	 * I2C
	 */
	{ .name = "imxiic",
	  .unit = 0,
	  .mem = { { I2C1_ADDR, I2Cx_SIZE } },
	  .irq = { I2C1_IRQ },
	},

	{ .name = "imxiic",
	  .unit = 1,
	  .mem = { { I2C2_ADDR, I2Cx_SIZE } },
	  .irq = { I2C2_IRQ },
	},

	{ .name = "imxiic",
	  .unit = 2,
	  .mem = { { I2C3_ADDR, I2Cx_SIZE } },
	  .irq = { I2C3_IRQ },
	},

	/*
	 * ESDHC
	 */
	{ .name = "imxesdhc",
	  .unit = 0,
	  .mem = { { ESDHC1_ADDR, ESDHCx_SIZE } },
	  .irq = { ESDHC1_IRQ },
	},

	{ .name = "imxesdhc",
	  .unit = 1,
	  .mem = { { ESDHC2_ADDR, ESDHCx_SIZE } },
	  .irq = { ESDHC2_IRQ },
	},

	{ .name = "imxesdhc",
	  .unit = 2,
	  .mem = { { ESDHC3_ADDR, ESDHCx_SIZE } },
	  .irq = { ESDHC3_IRQ },
	},

	{ .name = "imxesdhc",
	  .unit = 3,
	  .mem = { { ESDHC4_ADDR, ESDHCx_SIZE } },
	  .irq = { ESDHC4_IRQ },
	},

	/*
	 * USB
	 */
	{ .name = "ehci",
	  .unit = 0,
	  .mem = {
		  { USBUH1_EHCI_ADDR, USBx_SIZE },
		  { USBUH1_ADDR, USBx_SIZE },
		  { USBPHY2_ADDR, USBPHYx_SIZE },
		  { USBNC_ADDR, USBx_SIZE },
	  },
	  .irq = { USBH1_IRQ }
	},

	{ .name = "ehci",
	  .unit = 1,
	  .mem = {
		  { USBOTG_EHCI_ADDR, USBx_SIZE },
		  { USBOTG_ADDR, USBx_SIZE },
		  { USBPHY1_ADDR, USBPHYx_SIZE },
		  { USBNC_ADDR, USBx_SIZE },
	  },
	  .irq = { USBOTG_IRQ }
	},

	/*
	 * Ethernet
	 */
	{ .name = "imxenet",
	  .unit = 0,
	  .mem = { { ENET_ADDR, ENET_SIZE } },
	  .irq = { ENET_IRQ0, ENET_IRQ1 }
	},

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

void
imx6_init(void)
{
	armv7_set_devs(imx6_devs);
}


code:
/*	$OpenBSD: imx_machdep.c,v 1.15 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/imx/imxuartvar.h>
#include <armv7/armv7/armv7_machdep.h>

extern void imxdog_reset(void);
extern char *imx_board_name(void);
extern struct board_dev *imx_board_devs(void);
extern void imx_board_init(void);
extern int comcnspeed;
extern int comcnmode;

void
imx_platform_smc_write(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off,
    uint32_t op, uint32_t val)
{
	bus_space_write_4(iot, ioh, off, val);
}

void
imx_platform_init_cons(void)
{
	paddr_t paddr;

	switch (board_id) {
	/* UART1 */
	case BOARD_ID_IMX6_CUBOXI:
	case BOARD_ID_IMX6_HUMMINGBOARD:
	case BOARD_ID_IMX6_SABRESD:
	case BOARD_ID_IMX6_WANDBOARD:
		paddr = 0x02020000;
		break;
	/* UART2 */
	case BOARD_ID_IMX6_SABRELITE:
	case BOARD_ID_IMX6_UDOO:
	case BOARD_ID_IMX6_NOVENA:
		paddr = 0x021e8000;
		break;
	/* UART4 */
	case BOARD_ID_IMX6_PHYFLEX:
	case BOARD_ID_IMX6_UTILITE:
		paddr = 0x021f0000;
		break;
	default:
		printf("board type %x unknown", board_id);
		return;
		/* XXX - HELP */
	}
	imxuartcnattach(&armv7_bs_tag, paddr, comcnspeed, comcnmode);
}

void
imx_platform_watchdog_reset(void)
{
	imxdog_reset();
}

void
imx_platform_powerdown(void)
{

}

const char *
imx_platform_board_name(void)
{
	return (imx_board_name());
}

void
imx_platform_disable_l2_if_needed(void)
{

}

void
imx_platform_board_init(void)
{
	imx_board_init();
}

struct armv7_platform imx_platform = {
	.boot_name = "OpenBSD/imx",
	.board_name = imx_platform_board_name,
	.board_init = imx_platform_board_init,
	.smc_write = imx_platform_smc_write,
	.init_cons = imx_platform_init_cons,
	.watchdog_reset = imx_platform_watchdog_reset,
	.powerdown = imx_platform_powerdown,
	.disable_l2_if_needed = imx_platform_disable_l2_if_needed,
};

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

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

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

license: unknown


code:
/* $OpenBSD: imxahci.c,v 1.3 2014/04/14 04:42:22 dlg Exp $ */
/*
 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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/imx/imxccmvar.h>
#include <armv7/imx/imxiomuxcvar.h>

/* registers */
#define SATA_CAP		0x000
#define SATA_GHC		0x004
#define SATA_IS			0x008
#define SATA_PI			0x00C
#define SATA_VS			0x010
#define SATA_CCC_CTL		0x014
#define SATA_CCC_PORTS		0x018
#define SATA_CAP2		0x024
#define SATA_BISTAFR		0x0A0
#define SATA_BISTCR		0x0A4
#define SATA_BISTFCTR		0x0A8
#define SATA_BSTSR		0x0AC
#define SATA_OOBR		0x0BC
#define SATA_GPCR		0x0D0
#define SATA_GPSR		0x0D4
#define SATA_TIMER1MS		0x0E0
#define SATA_TESTR		0x0F4
#define SATA_VERSIONR		0x0F8
#define SATA_P0CLB		0x100
#define SATA_P0FB		0x108
#define SATA_P0IS		0x110
#define SATA_P0IE		0x114
#define SATA_P0CMD		0x118
#define SATA_P0TFD		0x120
#define SATA_P0SIG		0x124
#define SATA_P0SSTS		0x128
#define SATA_P0SCTL		0x12C
#define SATA_P0SERR		0x130
#define SATA_P0SACT		0x134
#define SATA_P0CI		0x138
#define SATA_P0SNTF		0x13C
#define SATA_P0DMACR		0x170
#define SATA_P0PHYCR		0x178
#define SATA_P0PHYSR		0x17C

#define SATA_CAP_SSS		(1 << 27)
#define SATA_GHC_HR		(1 << 0)
#define SATA_P0PHYCR_TEST_PDDQ	(1 << 20)

void	imxahci_attach(struct device *, struct device *, void *);
int	imxahci_detach(struct device *, int);
int	imxahci_activate(struct device *, int);

extern int ahci_intr(void *);

struct imxahci_softc {
	struct ahci_softc	sc;
};

struct cfattach imxahci_ca = {
	sizeof(struct imxahci_softc),
	NULL,
	imxahci_attach,
	imxahci_detach,
	imxahci_activate
};

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

void
imxahci_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct imxahci_softc *imxsc = (struct imxahci_softc *) self;
	struct ahci_softc *sc = &imxsc->sc;
	uint32_t timeout = 0x100000;

	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,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
		panic("imxahci_attach: bus_space_map failed!");

	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 unmap;
	}

	/* power it up */
	imxccm_enable_sata();
	delay(100);

	/* power phy up */
	imxiomuxc_enable_sata();

	/* setup */
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_P0PHYCR,
	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_P0PHYCR) & ~SATA_P0PHYCR_TEST_PDDQ);

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_GHC, SATA_GHC_HR);

	while (!bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_VERSIONR));

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_CAP,
	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_CAP) | SATA_CAP_SSS);

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_PI, 1);

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_TIMER1MS, imxccm_get_ahbclk());

	while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_P0SSTS) & 0xF) && timeout--);

	if (ahci_attach(sc) != 0) {
		/* error printed by ahci_attach */
		goto irq;
	}

	return;
irq:
	arm_intr_disestablish(sc->sc_ih);
unmap:
	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
}

int
imxahci_detach(struct device *self, int flags)
{
	struct imxahci_softc *imxsc = (struct imxahci_softc *) self;
	struct ahci_softc *sc = &imxsc->sc;

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

int
imxahci_activate(struct device *self, int act)
{
	struct imxahci_softc *imxsc = (struct imxahci_softc *) self;
	struct ahci_softc *sc = &imxsc->sc;

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


code:
/* $OpenBSD: imxccm.c,v 1.5 2015/05/30 08:09:19 jsg Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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/sysctl.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/armv7/armv7var.h>

/* registers */
#define CCM_CCR		0x00
#define CCM_CCDR	0x04
#define CCM_CSR		0x08
#define CCM_CCSR	0x0c
#define CCM_CACRR	0x10
#define CCM_CBCDR	0x14
#define CCM_CBCMR	0x18
#define CCM_CSCMR1	0x1c
#define CCM_CSCMR2	0x20
#define CCM_CSCDR1	0x24
#define CCM_CS1CDR	0x28
#define CCM_CS2CDR	0x2c
#define CCM_CDCDR	0x30
#define CCM_CHSCCDR	0x34
#define CCM_CSCDR2	0x38
#define CCM_CSCDR3	0x3c
#define CCM_CSCDR4	0x40
#define CCM_CDHIPR	0x48
#define CCM_CDCR	0x4c
#define CCM_CTOR	0x50
#define CCM_CLPCR	0x54
#define CCM_CISR	0x58
#define CCM_CIMR	0x5c
#define CCM_CCOSR	0x60
#define CCM_CGPR	0x64
#define CCM_CCGR0	0x68
#define CCM_CCGR1	0x6c
#define CCM_CCGR2	0x70
#define CCM_CCGR3	0x74
#define CCM_CCGR4	0x78
#define CCM_CCGR5	0x7c
#define CCM_CCGR6	0x80
#define CCM_CCGR7	0x84
#define CCM_CMEOR	0x88

/* ANALOG */
#define CCM_ANALOG_PLL_ARM			0x4000
#define CCM_ANALOG_PLL_ARM_SET			0x4004
#define CCM_ANALOG_PLL_ARM_CLR			0x4008
#define CCM_ANALOG_PLL_USB1			0x4010
#define CCM_ANALOG_PLL_USB1_SET			0x4014
#define CCM_ANALOG_PLL_USB1_CLR			0x4018
#define CCM_ANALOG_PLL_USB2			0x4020
#define CCM_ANALOG_PLL_USB2_SET			0x4024
#define CCM_ANALOG_PLL_USB2_CLR			0x4028
#define CCM_ANALOG_PLL_SYS			0x4030
#define CCM_ANALOG_USB1_CHRG_DETECT		0x41b0
#define CCM_ANALOG_USB1_CHRG_DETECT_SET		0x41b4
#define CCM_ANALOG_USB1_CHRG_DETECT_CLR		0x41b8
#define CCM_ANALOG_USB2_CHRG_DETECT		0x4210
#define CCM_ANALOG_USB2_CHRG_DETECT_SET		0x4214
#define CCM_ANALOG_USB2_CHRG_DETECT_CLR		0x4218
#define CCM_ANALOG_DIGPROG			0x4260
#define CCM_ANALOG_PLL_ENET			0x40e0
#define CCM_ANALOG_PLL_ENET_SET			0x40e4
#define CCM_ANALOG_PLL_ENET_CLR			0x40e8
#define CCM_ANALOG_PFD_480			0x40f0
#define CCM_ANALOG_PFD_480_SET			0x40f4
#define CCM_ANALOG_PFD_480_CLR			0x40f8
#define CCM_ANALOG_PFD_528			0x4100
#define CCM_ANALOG_PFD_528_SET			0x4104
#define CCM_ANALOG_PFD_528_CLR			0x4108
#define CCM_PMU_MISC1				0x4160

/* bits and bytes */
#define CCM_CCSR_PLL3_SW_CLK_SEL		(1 << 0)
#define CCM_CCSR_PLL2_SW_CLK_SEL		(1 << 1)
#define CCM_CCSR_PLL1_SW_CLK_SEL		(1 << 2)
#define CCM_CCSR_STEP_SEL			(1 << 8)
#define CCM_CBCDR_IPG_PODF_SHIFT		8
#define CCM_CBCDR_IPG_PODF_MASK			0x3
#define CCM_CBCDR_AHB_PODF_SHIFT		10
#define CCM_CBCDR_AHB_PODF_MASK			0x7
#define CCM_CBCDR_PERIPH_CLK_SEL_SHIFT		25
#define CCM_CBCDR_PERIPH_CLK_SEL_MASK		0x1
#define CCM_CBCMR_PERIPH_CLK2_SEL_SHIFT		12
#define CCM_CBCMR_PERIPH_CLK2_SEL_MASK		0x3
#define CCM_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT	18
#define CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK	0x3
#define CCM_CSCDR1_USDHCx_CLK_SEL_SHIFT(x)	((x) + 15)
#define CCM_CSCDR1_USDHCx_CLK_SEL_MASK		0x1
#define CCM_CSCDR1_USDHCx_PODF_MASK		0x7
#define CCM_CSCDR1_UART_PODF_MASK		0x7
#define CCM_CCGR1_ENET				(3 << 10)
#define CCM_CCGR2_I2C(x)			(3 << (6 + 2*x))
#define CCM_CCGR4_125M_PCIE			(3 << 0)
#define CCM_CCGR5_100M_SATA			(3 << 4)
#define CCM_CCGR6_USBOH3			(3 << 0)
#define CCM_CSCMR1_PERCLK_CLK_SEL_MASK		0x1f
#define CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK	0x7f
#define CCM_ANALOG_PLL_ARM_BYPASS		(1 << 16)
#define CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK	0x1
#define CCM_ANALOG_PLL_USB1_EN_USB_CLKS		(1 << 6)
#define CCM_ANALOG_PLL_USB1_POWER		(1 << 12)
#define CCM_ANALOG_PLL_USB1_ENABLE		(1 << 13)
#define CCM_ANALOG_PLL_USB1_BYPASS		(1 << 16)
#define CCM_ANALOG_PLL_USB1_LOCK		(1 << 31)
#define CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK	0x1
#define CCM_ANALOG_PLL_USB2_EN_USB_CLKS		(1 << 6)
#define CCM_ANALOG_PLL_USB2_POWER		(1 << 12)
#define CCM_ANALOG_PLL_USB2_ENABLE		(1 << 13)
#define CCM_ANALOG_PLL_USB2_BYPASS		(1 << 16)
#define CCM_ANALOG_PLL_USB2_LOCK		(1U << 31)
#define CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK	0x1
#define CCM_ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B	(1 << 19)
#define CCM_ANALOG_USB1_CHRG_DETECT_EN_B	(1 << 20)
#define CCM_ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B	(1 << 19)
#define CCM_ANALOG_USB2_CHRG_DETECT_EN_B	(1 << 20)
#define CCM_ANALOG_DIGPROG_MINOR_MASK		0xff
#define CCM_ANALOG_PLL_ENET_DIV_125M		(1 << 11)
#define CCM_ANALOG_PLL_ENET_POWERDOWN		(1 << 12)
#define CCM_ANALOG_PLL_ENET_ENABLE		(1 << 13)
#define CCM_ANALOG_PLL_ENET_BYPASS		(1 << 16)
#define CCM_ANALOG_PLL_ENET_125M_PCIE		(1 << 19)
#define CCM_ANALOG_PLL_ENET_100M_SATA		(1 << 20)
#define CCM_ANALOG_PLL_ENET_LOCK		(1U << 31)
#define CCM_ANALOG_PFD_480_PFDx_FRAC(x, y)	(((x) >> ((y) << 3)) & 0x3f)
#define CCM_ANALOG_PFD_528_PFDx_FRAC(x, y)	(((x) >> ((y) << 3)) & 0x3f)
#define CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_SATA	(0xB << 0)
#define CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_MASK	(0x1f << 0)
#define CCM_PMU_MISC1_LVDSCLK1_OBEN		(1 << 10)
#define CCM_PMU_MISC1_LVDSCLK1_IBEN		(1 << 12)

#define HCLK_FREQ				24000
#define PLL3_80M				80000

#define HREAD4(sc, reg)							\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))

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

enum clocks {
	/* OSC */
	OSC,		/* 24 MHz OSC */

	/* PLLs */
	ARM_PLL1,	/* ARM core PLL */
	SYS_PLL2,	/* System PLL: 528 MHz */
	USB1_PLL3,	/* OTG USB PLL: 480 MHz */
	USB2_PLL,	/* Host USB PLL: 480 MHz */
	AUD_PLL4,	/* Audio PLL */
	VID_PLL5,	/* Video PLL */
	ENET_PLL6,	/* ENET PLL */
	MLB_PLL,	/* MLB PLL */

	/* SYS_PLL2 PFDs */
	SYS_PLL2_PFD0,	/* 352 MHz */
	SYS_PLL2_PFD1,	/* 594 MHz */
	SYS_PLL2_PFD2,	/* 396 MHz */

	/* USB1_PLL3 PFDs */
	USB1_PLL3_PFD0,	/* 720 MHz */
	USB1_PLL3_PFD1,	/* 540 MHz */
	USB1_PLL3_PFD2,	/* 508.2 MHz */
	USB1_PLL3_PFD3,	/* 454.7 MHz */
};

struct imxccm_softc *imxccm_sc;

void imxccm_attach(struct device *parent, struct device *self, void *args);
int imxccm_cpuspeed(int *);
unsigned int imxccm_decode_pll(enum clocks, unsigned int);
unsigned int imxccm_get_pll2_pfd(unsigned int);
unsigned int imxccm_get_pll3_pfd(unsigned int);
unsigned int imxccm_get_armclk(void);
void imxccm_armclk_set_parent(enum clocks);
void imxccm_armclk_set_freq(unsigned int);
unsigned int imxccm_get_usdhx(int x);
unsigned int imxccm_get_periphclk(void);
unsigned int imxccm_get_fecclk(void);
unsigned int imxccm_get_ahbclk(void);
unsigned int imxccm_get_ipgclk(void);
unsigned int imxccm_get_ipg_perclk(void);
unsigned int imxccm_get_uartclk(void);
void imxccm_enable_i2c(int x);
void imxccm_enable_usboh3(void);
void imxccm_disable_usb1_chrg_detect(void);
void imxccm_disable_usb2_chrg_detect(void);
void imxccm_enable_pll_usb1(void);
void imxccm_enable_pll_usb2(void);
void imxccm_enable_pll_enet(void);
void imxccm_enable_enet(void);
void imxccm_enable_sata(void);
void imxccm_enable_pcie(void);

struct cfattach	imxccm_ca = {
	sizeof (struct imxccm_softc), NULL, imxccm_attach
};

struct cfdriver imxccm_cd = {
	NULL, "imxccm", DV_DULL
};

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

	imxccm_sc = sc;
	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("imxccm_attach: bus_space_map failed!");

	printf(": imx6 rev 1.%d CPU freq: %d MHz",
	    HREAD4(sc, CCM_ANALOG_DIGPROG) & CCM_ANALOG_DIGPROG_MINOR_MASK,
	    imxccm_get_armclk() / 1000);

	printf("\n");

	cpu_cpuspeed = imxccm_cpuspeed;
}

int
imxccm_cpuspeed(int *freq)
{
	*freq = imxccm_get_armclk() / 1000;
	return (0);
}

unsigned int
imxccm_decode_pll(enum clocks pll, unsigned int freq)
{
	struct imxccm_softc *sc = imxccm_sc;
	uint32_t div;

	switch (pll) {
	case ARM_PLL1:
		if (HREAD4(sc, CCM_ANALOG_PLL_ARM)
		    & CCM_ANALOG_PLL_ARM_BYPASS)
			return freq;
		div = HREAD4(sc, CCM_ANALOG_PLL_ARM)
		    & CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK;
		return (freq * div) / 2;
	case SYS_PLL2:
		div = HREAD4(sc, CCM_ANALOG_PLL_SYS)
		    & CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK;
		return freq * (20 + (div << 1));
	case USB1_PLL3:
		div = HREAD4(sc, CCM_ANALOG_PLL_USB2)
		    & CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK;
		return freq * (20 + (div << 1));
	default:
		return 0;
	}
}

unsigned int
imxccm_get_pll2_pfd(unsigned int pfd)
{
	struct imxccm_softc *sc = imxccm_sc;

	return imxccm_decode_pll(SYS_PLL2, HCLK_FREQ) * 18
	    / CCM_ANALOG_PFD_528_PFDx_FRAC(HREAD4(sc, CCM_ANALOG_PFD_528), pfd);
}

unsigned int
imxccm_get_pll3_pfd(unsigned int pfd)
{
	struct imxccm_softc *sc = imxccm_sc;

	return imxccm_decode_pll(USB1_PLL3, HCLK_FREQ) * 18
	    / CCM_ANALOG_PFD_480_PFDx_FRAC(HREAD4(sc, CCM_ANALOG_PFD_480), pfd);
}

unsigned int
imxccm_get_armclk()
{
	struct imxccm_softc *sc = imxccm_sc;

	uint32_t ccsr = HREAD4(sc, CCM_CCSR);

	if (!(ccsr & CCM_CCSR_PLL1_SW_CLK_SEL))
		return imxccm_decode_pll(ARM_PLL1, HCLK_FREQ);
	else if (ccsr & CCM_CCSR_STEP_SEL)
		return imxccm_get_pll2_pfd(2);
	else
		return HCLK_FREQ;
}

void
imxccm_armclk_set_parent(enum clocks clock)
{
	struct imxccm_softc *sc = imxccm_sc;

	switch (clock)
	{
	case ARM_PLL1:
		/* jump onto pll1 */
		HCLR4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL);
		/* put step clk on OSC, power saving */
		HCLR4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL);
		break;
	case OSC:
		/* put step clk on OSC */
		HCLR4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL);
		/* jump onto step clk */
		HSET4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL);
		break;
	case SYS_PLL2_PFD2:
		/* put step clk on pll2-pfd2 400 MHz */
		HSET4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL);
		/* jump onto step clk */
		HSET4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL);
		break;
	default:
		panic("%s: parent not possible for arm clk", __func__);
	}
}

void
imxccm_armclk_set_freq(unsigned int freq)
{
	if (freq > 1296000 || freq < 648000)
		panic("%s: frequency must be between 648MHz and 1296MHz!",
		    __func__);
}

unsigned int
imxccm_get_usdhx(int x)
{
	struct imxccm_softc *sc = imxccm_sc;

	uint32_t cscmr1 = HREAD4(sc, CCM_CSCMR1);
	uint32_t cscdr1 = HREAD4(sc, CCM_CSCDR1);
	uint32_t podf, clkroot;

	// Odd bitsetting. Damn you.
	if (x == 1)
		podf = ((cscdr1 >> 11) & CCM_CSCDR1_USDHCx_PODF_MASK);
	else
		podf = ((cscdr1 >> (10 + 3*x)) & CCM_CSCDR1_USDHCx_PODF_MASK);

	if (cscmr1 & (1 << CCM_CSCDR1_USDHCx_CLK_SEL_SHIFT(x)))
		clkroot = imxccm_get_pll2_pfd(0); // 352 MHz
	else
		clkroot = imxccm_get_pll2_pfd(2); // 396 MHz

	return clkroot / (podf + 1);
}

unsigned int
imxccm_get_uartclk()
{
	struct imxccm_softc *sc = imxccm_sc;

	uint32_t clkroot = PLL3_80M;
	uint32_t podf = HREAD4(sc, CCM_CSCDR1) & CCM_CSCDR1_UART_PODF_MASK;

	return clkroot / (podf + 1);
}

unsigned int
imxccm_get_periphclk()
{
	struct imxccm_softc *sc = imxccm_sc;

	if ((HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_PERIPH_CLK_SEL_SHIFT)
		    & CCM_CBCDR_PERIPH_CLK_SEL_MASK) {
		switch((HREAD4(sc, CCM_CBCMR)
		    >> CCM_CBCMR_PERIPH_CLK2_SEL_SHIFT) & CCM_CBCMR_PERIPH_CLK2_SEL_MASK) {
		case 0:
			return imxccm_decode_pll(USB1_PLL3, HCLK_FREQ);
		case 1:
		case 2:
			return HCLK_FREQ;
		default:
			return 0;
		}

	} else {
		switch((HREAD4(sc, CCM_CBCMR)
		    >> CCM_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT) & CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK) {
		default:
		case 0:
			return imxccm_decode_pll(SYS_PLL2, HCLK_FREQ);
		case 1:
			return imxccm_get_pll2_pfd(2); // 396 MHz
		case 2:
			return imxccm_get_pll2_pfd(0); // 352 MHz
		case 3:
			return imxccm_get_pll2_pfd(2) / 2; // 198 MHz
		}
	}
}

unsigned int
imxccm_get_fecclk()
{
	struct imxccm_softc *sc = imxccm_sc;
	uint32_t div = 0;

	switch (HREAD4(sc, CCM_ANALOG_PLL_ENET) & 0x3)
	{
	case 0:
		div = 20;
		break;
	case 1:
		div = 10;
		break;
	case 2:
		div = 5;
		break;
	case 3:
		div = 4;
		break;
	}

	return 500000 / div ;
}

unsigned int
imxccm_get_ahbclk()
{
	struct imxccm_softc *sc = imxccm_sc;
	uint32_t ahb_podf;

	ahb_podf = (HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_AHB_PODF_SHIFT)
	    & CCM_CBCDR_AHB_PODF_MASK;
	return imxccm_get_periphclk() / (ahb_podf + 1);
}

unsigned int
imxccm_get_ipgclk()
{
	struct imxccm_softc *sc = imxccm_sc;
	uint32_t ipg_podf;

	ipg_podf = (HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_IPG_PODF_SHIFT)
	    & CCM_CBCDR_IPG_PODF_MASK;
	return imxccm_get_ahbclk() / (ipg_podf + 1);
}

unsigned int
imxccm_get_ipg_perclk()
{
	struct imxccm_softc *sc = imxccm_sc;
	uint32_t ipg_podf;

	ipg_podf = HREAD4(sc, CCM_CSCMR1) & CCM_CSCMR1_PERCLK_CLK_SEL_MASK;

	return imxccm_get_ipgclk() / (ipg_podf + 1);
}

void
imxccm_enable_i2c(int x)
{
	struct imxccm_softc *sc = imxccm_sc;

	HSET4(sc, CCM_CCGR2, CCM_CCGR2_I2C(x));
}

void
imxccm_enable_usboh3(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	HSET4(sc, CCM_CCGR6, CCM_CCGR6_USBOH3);
}

void
imxccm_enable_pll_enet(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	if (HREAD4(sc, CCM_ANALOG_PLL_ENET) & CCM_ANALOG_PLL_ENET_ENABLE)
		return;

	HCLR4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_POWERDOWN);

	HSET4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_ENABLE);

	while(!(HREAD4(sc, CCM_ANALOG_PLL_ENET) & CCM_ANALOG_PLL_ENET_LOCK));

	HCLR4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_BYPASS);
}

void
imxccm_enable_enet(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	imxccm_enable_pll_enet();
	HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_DIV_125M);

	HSET4(sc, CCM_CCGR1, CCM_CCGR1_ENET);
}

void
imxccm_enable_sata(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	imxccm_enable_pll_enet();
	HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_100M_SATA);

	HSET4(sc, CCM_CCGR5, CCM_CCGR5_100M_SATA);
}

void
imxccm_enable_pcie(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	HWRITE4(sc, CCM_PMU_MISC1,
	    (HREAD4(sc, CCM_PMU_MISC1) & ~CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_MASK)
	    | CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_SATA
	    | CCM_PMU_MISC1_LVDSCLK1_OBEN
	    | CCM_PMU_MISC1_LVDSCLK1_IBEN);

	imxccm_enable_pll_enet();
	HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_125M_PCIE);

	HSET4(sc, CCM_CCGR4, CCM_CCGR4_125M_PCIE);
}

void
imxccm_disable_usb1_chrg_detect(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	HWRITE4(sc, CCM_ANALOG_USB1_CHRG_DETECT_SET,
	      CCM_ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B
	    | CCM_ANALOG_USB1_CHRG_DETECT_EN_B);
}

void
imxccm_disable_usb2_chrg_detect(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	HWRITE4(sc, CCM_ANALOG_USB2_CHRG_DETECT_SET,
	      CCM_ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B
	    | CCM_ANALOG_USB2_CHRG_DETECT_EN_B);
}

void
imxccm_enable_pll_usb1(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	HWRITE4(sc, CCM_ANALOG_PLL_USB1_CLR, CCM_ANALOG_PLL_USB1_BYPASS);

	HWRITE4(sc, CCM_ANALOG_PLL_USB1_SET,
	      CCM_ANALOG_PLL_USB1_ENABLE
	    | CCM_ANALOG_PLL_USB1_POWER
	    | CCM_ANALOG_PLL_USB1_EN_USB_CLKS);
}

void
imxccm_enable_pll_usb2(void)
{
	struct imxccm_softc *sc = imxccm_sc;

	HWRITE4(sc, CCM_ANALOG_PLL_USB2_CLR, CCM_ANALOG_PLL_USB2_BYPASS);

	HWRITE4(sc, CCM_ANALOG_PLL_USB2_SET,
	      CCM_ANALOG_PLL_USB2_ENABLE
	    | CCM_ANALOG_PLL_USB2_POWER
	    | CCM_ANALOG_PLL_USB2_EN_USB_CLKS);
}


code:
/* $OpenBSD: imxccmvar.h,v 1.2 2015/05/30 08:09:19 jsg Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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 IMXCCMVAR_H
#define IMXCCMVAR_H

unsigned int imxccm_get_usdhx(int x);
unsigned int imxccm_get_fecclk(void);
unsigned int imxccm_get_uartclk(void);
unsigned int imxccm_get_ipg_perclk(void);
unsigned int imxccm_get_ahbclk(void);
void imxccm_enable_i2c(int x);
void imxccm_enable_usboh3(void);
void imxccm_disable_usb1_chrg_detect(void);
void imxccm_disable_usb2_chrg_detect(void);
void imxccm_enable_pll_usb1(void);
void imxccm_enable_pll_usb2(void);
void imxccm_enable_enet(void);
void imxccm_enable_sata(void);
void imxccm_enable_pcie(void);

#endif /* IMXCCMVAR_H */


code:
/* $OpenBSD: imxdog.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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/armv7/armv7var.h>

/* registers */
#define WCR		0x00
#define WSR		0x02
#define WRSR		0x04
#define WICR		0x06
#define WMCR		0x08

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

struct imxdog_softc *imxdog_sc;

void imxdog_attach(struct device *parent, struct device *self, void *args);
void imxdog_reset(void);

struct cfattach	imxdog_ca = {
	sizeof (struct imxdog_softc), NULL, imxdog_attach
};

struct cfdriver imxdog_cd = {
	NULL, "imxdog", DV_DULL
};

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

	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("imxdog_attach: bus_space_map failed!");

	printf("\n");
	imxdog_sc = sc;
}

void
imxdog_reset()
{
	if (imxdog_sc == NULL)
		return;

	/* disable watchdog and set timeout to 0 */
	bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 0);

	/* sequence to reset timeout counter */
	bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WSR, 0x5555);
	bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WSR, 0xaaaa);

	/* enable watchdog */
	bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 1);
	/* errata TKT039676 */
	bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 1);

	delay(100000);
}


code:
/*	$OpenBSD: imxehci.c,v 1.8 2015/05/30 08:09:19 jsg Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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/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/imx/imxccmvar.h>
#include <armv7/imx/imxgpiovar.h>

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

/* usb phy */
#define USBPHY_PWD			0x00
#define USBPHY_CTRL			0x30
#define USBPHY_CTRL_SET			0x34
#define USBPHY_CTRL_CLR			0x38
#define USBPHY_CTRL_TOG			0x3c

#define USBPHY_CTRL_ENUTMILEVEL2	(1 << 14)
#define USBPHY_CTRL_ENUTMILEVEL3	(1 << 15)
#define USBPHY_CTRL_CLKGATE		(1 << 30)
#define USBPHY_CTRL_SFTRST		(1U << 31)

/* ehci */
#define EHCI_USBMODE			0xa8

#define EHCI_USBMODE_HOST		(3 << 0)
#define EHCI_PS_PTS_UTMI_MASK	((1 << 25) | (3 << 30))

/* usb non-core */
#define USBNC_USB_OTG_CTRL		0x00
#define USBNC_USB_UH1_CTRL		0x04

#define USBNC_USB_OTG_CTRL_OVER_CUR_POL	(1 << 8)
#define USBNC_USB_OTG_CTRL_OVER_CUR_DIS	(1 << 7)
#define USBNC_USB_UH1_CTRL_OVER_CUR_POL	(1 << 8)
#define USBNC_USB_UH1_CTRL_OVER_CUR_DIS	(1 << 7)

/* port specific addresses */
#define USBOTG_EHCI_ADDR	0x02184100
#define USBUH1_EHCI_ADDR	0x02184300
#define USBUH2_EHCI_ADDR	0x02184500
#define USBUH3_EHCI_ADDR	0x02184700

/* board specific */
#define EHCI_HUMMINGBOARD_USB_H1_PWR	0
#define EHCI_HUMMINGBOARD_USB_OTG_PWR	(2*32+22)
#define EHCI_NITROGEN6X_USB_HUB_RST	(6*32+12)
#define EHCI_PHYFLEX_USB_H1_PWR		0
#define EHCI_PHYFLEX_USB_OTG_PWR	111
#define EHCI_SABRESD_USB_PWR		(0*32+29)
#define EHCI_UTILITE_USB_HUB_RST	(6*32+8)

void	imxehci_attach(struct device *, struct device *, void *);
int	imxehci_detach(struct device *, int);

struct imxehci_softc {
	struct ehci_softc	sc;
	void			*sc_ih;
	bus_space_handle_t	uh_ioh;
	bus_space_handle_t	ph_ioh;
	bus_space_handle_t	nc_ioh;
};

struct cfattach imxehci_ca = {
	sizeof (struct imxehci_softc), NULL, imxehci_attach,
	imxehci_detach, NULL
};

void
imxehci_attach(struct device *parent, struct device *self, void *aux)
{
	struct imxehci_softc	*sc = (struct imxehci_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;

	/* Map I/O space */
	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;
	}

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

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

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

	printf("\n");

	imxccm_enable_usboh3();
	delay(1000);

	if (aa->aa_dev->mem[0].addr == USBUH1_EHCI_ADDR) {
		/* enable usb port power */
		switch (board_id)
		{
		case BOARD_ID_IMX6_PHYFLEX:
			imxgpio_set_dir(EHCI_PHYFLEX_USB_H1_PWR, IMXGPIO_DIR_OUT);
			delay(10);
			imxgpio_set_bit(EHCI_PHYFLEX_USB_H1_PWR);
			delay(10);
			break;
		case BOARD_ID_IMX6_CUBOXI:
		case BOARD_ID_IMX6_HUMMINGBOARD:
			imxgpio_set_bit(EHCI_HUMMINGBOARD_USB_H1_PWR);
			imxgpio_set_dir(EHCI_HUMMINGBOARD_USB_H1_PWR, IMXGPIO_DIR_OUT);
			delay(10);
			break;
		case BOARD_ID_IMX6_SABRELITE:
			imxgpio_clear_bit(EHCI_NITROGEN6X_USB_HUB_RST);
			imxgpio_set_dir(EHCI_NITROGEN6X_USB_HUB_RST, IMXGPIO_DIR_OUT);
			delay(1000 * 2);
			imxgpio_set_bit(EHCI_NITROGEN6X_USB_HUB_RST);
			delay(10);
			break;
		case BOARD_ID_IMX6_SABRESD:
			imxgpio_set_bit(EHCI_SABRESD_USB_PWR);
			imxgpio_set_dir(EHCI_SABRESD_USB_PWR, IMXGPIO_DIR_OUT);
			delay(10);
			break;
		case BOARD_ID_IMX6_UTILITE:
			imxgpio_clear_bit(EHCI_UTILITE_USB_HUB_RST);
			imxgpio_set_dir(EHCI_UTILITE_USB_HUB_RST, IMXGPIO_DIR_OUT);
			delay(10);
			imxgpio_set_bit(EHCI_UTILITE_USB_HUB_RST);
			delay(1000);
			break;
		}

		/* disable the carger detection, else signal on DP will be poor */
		imxccm_disable_usb2_chrg_detect();
		/* power host 1 */
		imxccm_enable_pll_usb2();

		/* over current and polarity setting */
		bus_space_write_4(sc->sc.iot, sc->nc_ioh, USBNC_USB_UH1_CTRL,
		    bus_space_read_4(sc->sc.iot, sc->nc_ioh, USBNC_USB_UH1_CTRL) |
		    (USBNC_USB_UH1_CTRL_OVER_CUR_POL | USBNC_USB_UH1_CTRL_OVER_CUR_DIS));
	} else if (aa->aa_dev->mem[0].addr == USBOTG_EHCI_ADDR) {
		/* enable usb port power */
		switch (board_id)
		{
		case BOARD_ID_IMX6_CUBOXI:
		case BOARD_ID_IMX6_HUMMINGBOARD:
			imxgpio_set_dir(EHCI_HUMMINGBOARD_USB_OTG_PWR, IMXGPIO_DIR_OUT);
			imxgpio_set_bit(EHCI_HUMMINGBOARD_USB_OTG_PWR);
			delay(10);
			break;
		}

		/* disable the carger detection, else signal on DP will be poor */
		imxccm_disable_usb1_chrg_detect();
		/* power host 0 */
		imxccm_enable_pll_usb1();

		/* over current and polarity setting */
		bus_space_write_4(sc->sc.iot, sc->nc_ioh, USBNC_USB_OTG_CTRL,
		    bus_space_read_4(sc->sc.iot, sc->nc_ioh, USBNC_USB_OTG_CTRL) |
		    (USBNC_USB_OTG_CTRL_OVER_CUR_POL | USBNC_USB_OTG_CTRL_OVER_CUR_DIS));
	}

	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR,
	    USBPHY_CTRL_CLKGATE);

	/* 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);

	/* Stop then Reset */
	uint32_t val = EOREAD4(&sc->sc, EHCI_USBCMD);
	val &= ~EHCI_CMD_RS;
	EOWRITE4(&sc->sc, EHCI_USBCMD, val);

	while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_RS)
		;

	val = EOREAD4(&sc->sc, EHCI_USBCMD);
	val |= EHCI_CMD_HCRESET;
	EOWRITE4(&sc->sc, EHCI_USBCMD, val);

	while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_HCRESET)
		;

	/* Reset USBPHY module */
	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, USBPHY_CTRL_SFTRST);

	delay(10);

	/* Remove CLKGATE and SFTRST */
	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR,
	    USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);

	delay(10);

	/* Power up the PHY */
	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_PWD, 0);

	/* enable FS/LS device */
	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET,
	    USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);

	/* set host mode */
	EWRITE4(&sc->sc, EHCI_USBMODE,
	    EREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_HOST);

	/* set to UTMI mode */
	EOWRITE4(&sc->sc, EHCI_PORTSC(1),
	    EOREAD4(&sc->sc, EHCI_PORTSC(1)) & ~EHCI_PS_PTS_UTMI_MASK);

	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");
		goto mem3;
	}

	strlcpy(sc->sc.sc_vendor, "i.MX6", sizeof(sc->sc.sc_vendor));
	r = ehci_init(&sc->sc);
	if (r != USBD_NORMAL_COMPLETION) {
		printf("%s: init failed, error=%d\n", devname, r);
		goto intr;
	}

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

	goto out;

intr:
	arm_intr_disestablish(sc->sc_ih);
	sc->sc_ih = NULL;
mem3:
	bus_space_unmap(sc->sc.iot, sc->nc_ioh, aa->aa_dev->mem[3].addr);
mem2:
	bus_space_unmap(sc->sc.iot, sc->ph_ioh, aa->aa_dev->mem[2].addr);
mem1:
	bus_space_unmap(sc->sc.iot, sc->uh_ioh, aa->aa_dev->mem[1].addr);
mem0:
	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
	sc->sc.sc_size = 0;
out:
	return;
}

int
imxehci_detach(struct device *self, int flags)
{
	struct imxehci_softc		*sc = (struct imxehci_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;
	}

	return (0);
}


code:
/* $OpenBSD: imxenet.c,v 1.16 2015/06/24 09:40:53 mpi Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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/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/imx/imxenet.h>
#include <armv7/imx/imxccmvar.h>
#include <armv7/imx/imxgpiovar.h>
#include <armv7/imx/imxocotpvar.h>

/* configuration registers */
#define ENET_EIR		0x004
#define ENET_EIMR		0x008
#define ENET_RDAR		0x010
#define ENET_TDAR		0x014
#define ENET_ECR		0x024
#define ENET_MMFR		0x040
#define ENET_MSCR		0x044
#define ENET_MIBC		0x064
#define ENET_RCR		0x084
#define ENET_TCR		0x0C4
#define ENET_PALR		0x0E4
#define ENET_PAUR		0x0E8
#define ENET_OPD		0x0EC
#define ENET_IAUR		0x118
#define ENET_IALR		0x11C
#define ENET_GAUR		0x120
#define ENET_GALR		0x124
#define ENET_TFWR		0x144
#define ENET_RDSR		0x180
#define ENET_TDSR		0x184
#define ENET_MRBR		0x188
#define ENET_RSFL		0x190
#define ENET_RSEM		0x194
#define ENET_RAEM		0x198
#define ENET_RAFL		0x19C
#define ENET_TSEM		0x1A0
#define ENET_TAEM		0x1A4
#define ENET_TAFL		0x1A8
#define ENET_TIPG		0x1AC
#define ENET_FTRL		0x1B0
#define ENET_TACC		0x1C0
#define ENET_RACC		0x1C4

#define ENET_RDAR_RDAR		(1 << 24)
#define ENET_TDAR_TDAR		(1 << 24)
#define ENET_ECR_RESET		(1 << 0)
#define ENET_ECR_ETHEREN	(1 << 1)
#define ENET_ECR_EN1588		(1 << 4)
#define ENET_ECR_SPEED		(1 << 5)
#define ENET_ECR_DBSWP		(1 << 8)
#define ENET_MMFR_TA		(2 << 16)
#define ENET_MMFR_RA_SHIFT	18
#define ENET_MMFR_PA_SHIFT	23
#define ENET_MMFR_OP_WR		(1 << 28)
#define ENET_MMFR_OP_RD		(2 << 28)
#define ENET_MMFR_ST		(1 << 30)
#define ENET_RCR_MII_MODE	(1 << 2)
#define ENET_RCR_PROM		(1 << 3)
#define ENET_RCR_FCE		(1 << 5)
#define ENET_RCR_RGMII_MODE	(1 << 6)
#define ENET_RCR_MAX_FL(x)	(((x) & 0x3fff) << 16)
#define ENET_TCR_FDEN		(1 << 2)
#define ENET_EIR_MII		(1 << 23)
#define ENET_EIR_RXF		(1 << 25)
#define ENET_EIR_TXF		(1 << 27)
#define ENET_TFWR_STRFWD	(1 << 8)

/* statistics counters */

/* 1588 control */
#define ENET_ATCR		0x400
#define ENET_ATVR		0x404
#define ENET_ATOFF		0x408
#define ENET_ATPER		0x40C
#define ENET_ATCOR		0x410
#define ENET_ATINC		0x414
#define ENET_ATSTMP		0x418

/* capture / compare block */
#define ENET_TGSR		0x604
#define ENET_TCSR0		0x608
#define ENET_TCCR0		0x60C
#define ENET_TCSR1		0x610
#define ENET_TCCR1		0x614
#define ENET_TCSR2		0x618
#define ENET_TCCR2		0x61C
#define ENET_TCSR3		0x620
#define ENET_TCCR3		0x624

#define ENET_MII_CLK		2500
#define ENET_ALIGNMENT		16

#define ENET_HUMMINGBOARD_PHY			0
#define ENET_HUMMINGBOARD_PHY_RST		(3*32+15)
#define ENET_SABRELITE_PHY			6
#define ENET_SABRELITE_PHY_RST			(2*32+23)
#define ENET_SABRESD_PHY			1
#define ENET_SABRESD_PHY_RST			(0*32+25)
#define ENET_NITROGEN6X_PHY			6
#define ENET_NITROGEN6X_PHY_RST			(0*32+27)
#define ENET_UDOO_PHY				6
#define ENET_UDOO_PHY_RST			(2*32+23)
#define ENET_UDOO_PWR				(1*32+31)
#define ENET_UTILITE_PHY			0
#define ENET_WANDBOARD_PHY			1
#define ENET_PHYFLEX_PHY			3
#define ENET_PHYFLEX_PHY_RST			(2*32+23)
#define ENET_NOVENA_PHY				7
#define ENET_NOVENA_PHY_RST			(2*32+23)

#define HREAD4(sc, reg)							\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))

struct imxenet_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;
	void				*sc_ih; /* Interrupt handler */
	bus_dma_tag_t			sc_dma_tag;
	uint32_t			intr_status;	/* soft interrupt status */
	struct imxenet_dma_alloc	txdma;		/* bus_dma glue for tx desc */
	struct imxenet_buf_desc		*tx_desc_base;
	struct imxenet_dma_alloc	rxdma;		/* bus_dma glue for rx desc */
	struct imxenet_buf_desc		*rx_desc_base;
	struct imxenet_dma_alloc	tbdma;		/* bus_dma glue for packets */
	struct imxenet_buffer		*tx_buffer_base;
	struct imxenet_dma_alloc	rbdma;		/* bus_dma glue for packets */
	struct imxenet_buffer		*rx_buffer_base;
	int				cur_tx;
	int				cur_rx;
};

struct imxenet_softc *imxenet_sc;

void imxenet_attach(struct device *, struct device *, void *);
int imxenet_enaddr_valid(u_char *);
void imxenet_enaddr(struct imxenet_softc *);
void imxenet_chip_init(struct imxenet_softc *);
int imxenet_ioctl(struct ifnet *, u_long, caddr_t);
void imxenet_start(struct ifnet *);
int imxenet_encap(struct imxenet_softc *, struct mbuf *);
void imxenet_init_txd(struct imxenet_softc *);
void imxenet_init_rxd(struct imxenet_softc *);
void imxenet_init(struct imxenet_softc *);
void imxenet_stop(struct imxenet_softc *);
void imxenet_iff(struct imxenet_softc *);
struct mbuf * imxenet_newbuf(void);
int imxenet_intr(void *);
void imxenet_recv(struct imxenet_softc *);
int imxenet_wait_intr(struct imxenet_softc *, int, int);
int imxenet_miibus_readreg(struct device *, int, int);
void imxenet_miibus_writereg(struct device *, int, int, int);
void imxenet_miibus_statchg(struct device *);
int imxenet_ifmedia_upd(struct ifnet *);
void imxenet_ifmedia_sts(struct ifnet *, struct ifmediareq *);
int imxenet_dma_malloc(struct imxenet_softc *, bus_size_t, struct imxenet_dma_alloc *);
void imxenet_dma_free(struct imxenet_softc *, struct imxenet_dma_alloc *);

struct cfattach imxenet_ca = {
	sizeof (struct imxenet_softc), NULL, imxenet_attach
};

struct cfdriver imxenet_cd = {
	NULL, "imxenet", DV_IFNET
};

void
imxenet_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct imxenet_softc *sc = (struct imxenet_softc *) self;
	struct mii_data *mii;
	struct ifnet *ifp;
	int tsize, rsize, tbsize, rbsize, 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("imxenet_attach: bus_space_map failed!");

	sc->sc_dma_tag = aa->aa_dmat;

	/* power it up */
	imxccm_enable_enet();

	switch (board_id)
	{
	case BOARD_ID_IMX6_CUBOXI:
	case BOARD_ID_IMX6_HUMMINGBOARD:
		/* We need to reset the AR8035 PHY twice. */
		imxgpio_clear_bit(ENET_HUMMINGBOARD_PHY_RST);
		imxgpio_set_dir(ENET_HUMMINGBOARD_PHY_RST, IMXGPIO_DIR_OUT);
		delay(2000);
		imxgpio_set_bit(ENET_HUMMINGBOARD_PHY_RST);
		delay(2000);
		imxgpio_clear_bit(ENET_HUMMINGBOARD_PHY_RST);
		delay(2000);
		imxgpio_set_bit(ENET_HUMMINGBOARD_PHY_RST);
		delay(2000);
		break;
	case BOARD_ID_IMX6_PHYFLEX:
	case BOARD_ID_IMX6_SABRELITE:
		/* phyFLEX i.MX6 and SABRE Lite PHY reset */
		imxgpio_clear_bit(ENET_SABRELITE_PHY_RST);
		imxgpio_set_dir(ENET_SABRELITE_PHY_RST, IMXGPIO_DIR_OUT);
		imxgpio_clear_bit(ENET_NITROGEN6X_PHY_RST);
		imxgpio_set_dir(ENET_NITROGEN6X_PHY_RST, IMXGPIO_DIR_OUT);
		delay(1000 * 10);
		imxgpio_set_bit(ENET_SABRELITE_PHY_RST);
		imxgpio_set_bit(ENET_NITROGEN6X_PHY_RST);
		delay(100);
		break;
	case BOARD_ID_IMX6_SABRESD:
		imxgpio_clear_bit(ENET_SABRESD_PHY_RST);
		imxgpio_set_dir(ENET_SABRESD_PHY_RST, IMXGPIO_DIR_OUT);
		delay(1000 * 10);
		imxgpio_set_bit(ENET_SABRESD_PHY_RST);
		delay(100);
		break;
	case BOARD_ID_IMX6_UDOO:
		imxgpio_set_bit(ENET_UDOO_PWR);
		imxgpio_set_dir(ENET_UDOO_PWR, IMXGPIO_DIR_OUT);
		imxgpio_clear_bit(ENET_UDOO_PHY_RST);
		imxgpio_set_dir(ENET_UDOO_PHY_RST, IMXGPIO_DIR_OUT);
		delay(1000 * 1);
		imxgpio_set_bit(ENET_UDOO_PHY_RST);
		delay(1000 * 100);
		break;
	case BOARD_ID_IMX6_NOVENA:
		imxgpio_clear_bit(ENET_NOVENA_PHY_RST);
		imxgpio_set_dir(ENET_NOVENA_PHY_RST, IMXGPIO_DIR_OUT);
		delay(1000 * 10);
		imxgpio_set_bit(ENET_NOVENA_PHY_RST);
		delay(100);
		break;
	}
	printf("\n");

	/* Figure out the hardware address. Must happen before reset. */
	imxenet_enaddr(sc);

	/* reset the controller */
	HSET4(sc, ENET_ECR, ENET_ECR_RESET);
	while(HREAD4(sc, ENET_ECR) & ENET_ECR_RESET);

	HWRITE4(sc, ENET_EIMR, 0);
	HWRITE4(sc, ENET_EIR, 0xffffffff);

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

	tsize = ENET_MAX_TXD * sizeof(struct imxenet_buf_desc);
	tsize = ENET_ROUNDUP(tsize, PAGE_SIZE);

	if (imxenet_dma_malloc(sc, tsize, &sc->txdma)) {
		printf("%s: Unable to allocate tx_desc memory\n",
		    sc->sc_dev.dv_xname);
		goto bad;
	}
	sc->tx_desc_base = (struct imxenet_buf_desc *)sc->txdma.dma_vaddr;

	rsize = ENET_MAX_RXD * sizeof(struct imxenet_buf_desc);
	rsize = ENET_ROUNDUP(rsize, PAGE_SIZE);

	if (imxenet_dma_malloc(sc, rsize, &sc->rxdma)) {
		printf("%s: Unable to allocate rx_desc memory\n",
		    sc->sc_dev.dv_xname);
		goto txdma;
	}
	sc->rx_desc_base = (struct imxenet_buf_desc *)sc->rxdma.dma_vaddr;

	tbsize = ENET_MAX_TXD * ENET_MAX_PKT_SIZE;
	tbsize = ENET_ROUNDUP(tbsize, PAGE_SIZE);

	if (imxenet_dma_malloc(sc, tbsize, &sc->tbdma)) {
		printf("%s: Unable to allocate tx_buffer memory\n",
		    sc->sc_dev.dv_xname);
		goto rxdma;
	}
	sc->tx_buffer_base = (struct imxenet_buffer *)sc->tbdma.dma_vaddr;

	rbsize = ENET_MAX_RXD * ENET_MAX_PKT_SIZE;
	rbsize = ENET_ROUNDUP(rbsize, PAGE_SIZE);

	if (imxenet_dma_malloc(sc, rbsize, &sc->rbdma)) {
		printf("%s: Unable to allocate rx_buffer memory\n",
		    sc->sc_dev.dv_xname);
		goto tbdma;
	}
	sc->rx_buffer_base = (struct imxenet_buffer *)sc->rbdma.dma_vaddr;

	sc->cur_tx = 0;
	sc->cur_rx = 0;

	s = splnet();

	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 = imxenet_ioctl;
	ifp->if_start = imxenet_start;
	ifp->if_capabilities = IFCAP_VLAN_MTU;

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

	/* initialize the chip */
	imxenet_chip_init(sc);

	IFQ_SET_READY(&ifp->if_snd);

	/* Initialize MII/media info. */
	mii = &sc->sc_mii;
	mii->mii_ifp = ifp;
	mii->mii_readreg = imxenet_miibus_readreg;
	mii->mii_writereg = imxenet_miibus_writereg;
	mii->mii_statchg = imxenet_miibus_statchg;
	mii->mii_flags = MIIF_AUTOTSLEEP;

	ifmedia_init(&mii->mii_media, 0, imxenet_ifmedia_upd, imxenet_ifmedia_sts);
	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);

	imxenet_sc = sc;
	return;

tbdma:
	imxenet_dma_free(sc, &sc->tbdma);
rxdma:
	imxenet_dma_free(sc, &sc->rxdma);
txdma:
	imxenet_dma_free(sc, &sc->txdma);
bad:
	bus_space_unmap(sc->sc_iot, sc->sc_ioh, aa->aa_dev->mem[0].size);
}

/* Try to determine a valid hardware address */
void
imxenet_enaddr(struct imxenet_softc *sc)
{
	u_int32_t tmp;
	u_char enaddr[6];

	/* XXX serial EEPROM */
	/* XXX FDT */

	/* Try to get an address from COTP */
	memset(enaddr, 0xff, ETHER_ADDR_LEN);
	imxocotp_get_ethernet_address(enaddr);
	if (imxenet_enaddr_valid(enaddr)) {
		memcpy(sc->sc_ac.ac_enaddr, enaddr, ETHER_ADDR_LEN);
		return;
	}

	/* The firmware or bootloader may have already set an address */
	tmp = HREAD4(sc, ENET_PALR);
	sc->sc_ac.ac_enaddr[0] = (tmp >> 24) & 0xff;
	sc->sc_ac.ac_enaddr[1] = (tmp >> 16) & 0xff;
	sc->sc_ac.ac_enaddr[2] = (tmp >> 8) & 0xff;
	sc->sc_ac.ac_enaddr[3] = tmp & 0xff;
	tmp = HREAD4(sc, ENET_PAUR);
	sc->sc_ac.ac_enaddr[4] = (tmp >> 24) & 0xff;
	sc->sc_ac.ac_enaddr[5] = (tmp >> 16) & 0xff;
	if (imxenet_enaddr_valid(sc->sc_ac.ac_enaddr))
		return;

	/* No usable address found, use a random one */
	printf("%s: no hardware address found, using random\n",
	    sc->sc_dev.dv_xname);
	ether_fakeaddr(&sc->sc_ac.ac_if);
}

int
imxenet_enaddr_valid(u_char addr[6])
{
	/* Multicast */
	if (ETHER_IS_MULTICAST(addr))
		return 0;
	/* All 0/1 */
	if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 &&
	    addr[3] == 0 && addr[4] == 0 && addr[5] == 0)
		return 0;
	if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
	    addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff)
		return 0;
	return 1;
}

void
imxenet_chip_init(struct imxenet_softc *sc)
{
	struct device *dev = (struct device *) sc;
	int phy = 0;
	uint32_t reg;

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MSCR,
	    (((imxccm_get_fecclk() + (ENET_MII_CLK << 2) - 1) / (ENET_MII_CLK << 2)) << 1) | 0x100);

	switch (board_id)
	{
	case BOARD_ID_IMX6_CUBOXI:
	case BOARD_ID_IMX6_HUMMINGBOARD:
		phy = ENET_HUMMINGBOARD_PHY;
		break;
	case BOARD_ID_IMX6_PHYFLEX:
		phy = ENET_PHYFLEX_PHY;
		break;
	case BOARD_ID_IMX6_SABRELITE:
		phy = ENET_SABRELITE_PHY;
		break;
	case BOARD_ID_IMX6_SABRESD:
		phy = ENET_SABRESD_PHY;
		break;
	case BOARD_ID_IMX6_UDOO:
		phy = ENET_UDOO_PHY;
		break;
	case BOARD_ID_IMX6_UTILITE:
		phy = ENET_UTILITE_PHY;
		break;
	case BOARD_ID_IMX6_NOVENA:
		phy = ENET_NOVENA_PHY;
		break;
	case BOARD_ID_IMX6_WANDBOARD:
		phy = ENET_WANDBOARD_PHY;
		break;
	}

	switch (board_id)
	{
	case BOARD_ID_IMX6_UDOO:	/* Micrel KSZ9031 */
		/* prefer master mode */
		imxenet_miibus_writereg(dev, phy, 0x9, 0x1c00);

		/* control data pad skew */
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x0002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0004);
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x4002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0000);

		/* rx data pad skew */
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x0002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0005);
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x4002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0000);

		/* tx data pad skew */
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x0002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0006);
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x4002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0000);

		/* gtx and rx data pad skew */
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x0002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x0008);
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x4002);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x03ff);
		break;
	case BOARD_ID_IMX6_PHYFLEX:
	case BOARD_ID_IMX6_SABRELITE:	/* Micrel KSZ9021 */
		/* prefer master mode */
		imxenet_miibus_writereg(dev, phy, 0x9, 0x1f00);

		/* min rx data delay */
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x8105);
		imxenet_miibus_writereg(dev, phy, 0x0c, 0x0000);

		/* min tx data delay */
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x8106);
		imxenet_miibus_writereg(dev, phy, 0x0c, 0x0000);

		/* max rx/tx clock delay, min rx/tx control delay */
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x8104);
		imxenet_miibus_writereg(dev, phy, 0x0c, 0xf0f0);
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x104);

		/* enable all interrupts */
		imxenet_miibus_writereg(dev, phy, 0x1b, 0xff00);
		break;
	case BOARD_ID_IMX6_NOVENA:	/* Micrel KSZ9021 */
		/* TXEN_SKEW_PS/TXC_SKEW_PS/RXDV_SKEW_PS/RXC_SKEW_PS */
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x8104);
		imxenet_miibus_writereg(dev, phy, 0x0c, 0xf0f0);

		/* RXD0_SKEW_PS/RXD1_SKEW_PS/RXD2_SKEW_PS/RXD3_SKEW_PS */
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x8105);
		imxenet_miibus_writereg(dev, phy, 0x0c, 0x0000);

		/* TXD0_SKEW_PS/TXD1_SKEW_PS/TXD2_SKEW_PS/TXD3_SKEW_PS */
		imxenet_miibus_writereg(dev, phy, 0x0b, 0x8106);
		imxenet_miibus_writereg(dev, phy, 0x0c, 0xffff);
		break;
	case BOARD_ID_IMX6_CUBOXI:		/* AR8035 */
	case BOARD_ID_IMX6_HUMMINGBOARD:	/* AR8035 */
	case BOARD_ID_IMX6_SABRESD:		/* AR8031 */
	case BOARD_ID_IMX6_UTILITE:
	case BOARD_ID_IMX6_WANDBOARD:		/* AR8031 */
		/* disable SmartEEE */
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x0003);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x805d);
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x4003);
		reg = imxenet_miibus_readreg(dev, phy, 0x0e);
		imxenet_miibus_writereg(dev, phy, 0x0e, reg & ~0x0100);

		/* enable 125MHz clk output for AR8031 */
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x0007);
		imxenet_miibus_writereg(dev, phy, 0x0e, 0x8016);
		imxenet_miibus_writereg(dev, phy, 0x0d, 0x4007);

		reg = imxenet_miibus_readreg(dev, phy, 0x0e) & 0xffe3;
		imxenet_miibus_writereg(dev, phy, 0x0e, reg | 0x18);

		/* tx clock delay */
		imxenet_miibus_writereg(dev, phy, 0x1d, 0x0005);
		reg = imxenet_miibus_readreg(dev, phy, 0x1e);
		imxenet_miibus_writereg(dev, phy, 0x1e, reg | 0x0100);

		/* phy power */
		reg = imxenet_miibus_readreg(dev, phy, 0x00);
		if (reg & 0x0800)
			imxenet_miibus_writereg(dev, phy, 0x00, reg & ~0x0800);
		break;
	}
}

void
imxenet_init_rxd(struct imxenet_softc *sc)
{
	int i;

	memset(sc->rx_desc_base, 0, ENET_MAX_RXD * sizeof(struct imxenet_buf_desc));

	for (i = 0; i < ENET_MAX_RXD; i++)
	{
		sc->rx_desc_base[i].status = ENET_RXD_EMPTY;
		sc->rx_desc_base[i].data_pointer = sc->rbdma.dma_paddr + i * ENET_MAX_PKT_SIZE;
#ifdef ENET_ENHANCED_BD
		sc->rx_desc_base[i].enhanced_status = ENET_RXD_INT;
#endif
	}

	sc->rx_desc_base[i - 1].status |= ENET_RXD_WRAP;
}

void
imxenet_init_txd(struct imxenet_softc *sc)
{
	int i;

	memset(sc->tx_desc_base, 0, ENET_MAX_TXD * sizeof(struct imxenet_buf_desc));

	for (i = 0; i < ENET_MAX_TXD; i++)
	{
		sc->tx_desc_base[i].data_pointer = sc->tbdma.dma_paddr + i * ENET_MAX_PKT_SIZE;
	}

	sc->tx_desc_base[i - 1].status |= ENET_TXD_WRAP;
}

void
imxenet_init(struct imxenet_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	int speed = 0;

	/* reset the controller */
	HSET4(sc, ENET_ECR, ENET_ECR_RESET);
	while(HREAD4(sc, ENET_ECR) & ENET_ECR_RESET);

	/* set hw address */
	HWRITE4(sc, ENET_PALR,
	    (sc->sc_ac.ac_enaddr[0] << 24) |
	    (sc->sc_ac.ac_enaddr[1] << 16) |
	    (sc->sc_ac.ac_enaddr[2] << 8) |
	     sc->sc_ac.ac_enaddr[3]);
	HWRITE4(sc, ENET_PAUR,
	    (sc->sc_ac.ac_enaddr[4] << 24) |
	    (sc->sc_ac.ac_enaddr[5] << 16));

	/* clear outstanding interrupts */
	HWRITE4(sc, ENET_EIR, 0xffffffff);

	/* set max receive buffer size, 3-0 bits always zero for alignment */
	HWRITE4(sc, ENET_MRBR, ENET_MAX_PKT_SIZE);

	/* set descriptor */
	HWRITE4(sc, ENET_TDSR, sc->txdma.dma_paddr);
	HWRITE4(sc, ENET_RDSR, sc->rxdma.dma_paddr);

	/* init descriptor */
	imxenet_init_txd(sc);
	imxenet_init_rxd(sc);

	/* set it to full-duplex */
	HWRITE4(sc, ENET_TCR, ENET_TCR_FDEN);

	/*
	 * Set max frame length to 1518 or 1522 with VLANs,
	 * pause frames and promisc mode.
	 * XXX: RGMII mode - phy dependant
	 */
	HWRITE4(sc, ENET_RCR,
	    ENET_RCR_MAX_FL(1522) | ENET_RCR_RGMII_MODE | ENET_RCR_MII_MODE |
	    ENET_RCR_FCE);

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MSCR,
	    (((imxccm_get_fecclk() + (ENET_MII_CLK << 2) - 1) / (ENET_MII_CLK << 2)) << 1) | 0x100);

	/* RX FIFO treshold and pause */
	HWRITE4(sc, ENET_RSEM, 0x84);
	HWRITE4(sc, ENET_RSFL, 16);
	HWRITE4(sc, ENET_RAEM, 8);
	HWRITE4(sc, ENET_RAFL, 8);
	HWRITE4(sc, ENET_OPD, 0xFFF0);

	/* do store and forward, only i.MX6, needs to be set correctly else */
	HWRITE4(sc, ENET_TFWR, ENET_TFWR_STRFWD);

	/* enable gigabit-ethernet and set it to support little-endian */
	switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
	case IFM_1000_T:  /* Gigabit */
		speed |= ENET_ECR_SPEED;
		break;
	default:
		speed &= ~ENET_ECR_SPEED;
	}
	HWRITE4(sc, ENET_ECR, ENET_ECR_ETHEREN | speed | ENET_ECR_DBSWP);

#ifdef ENET_ENHANCED_BD
	HSET4(sc, ENET_ECR, ENET_ECR_EN1588);
#endif

	/* rx descriptors are ready */
	HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR);

	/* program promiscuous mode and multicast filters */
	imxenet_iff(sc);

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

	/* enable interrupts for tx/rx */
	HWRITE4(sc, ENET_EIMR, ENET_EIR_TXF | ENET_EIR_RXF);
	HWRITE4(sc, ENET_EIMR, 0xffffffff);

	imxenet_start(ifp);
}

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

	/*
	 * Mark the interface down and cancel the watchdog timer.
	 */
	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
	ifp->if_timer = 0;

	/* reset the controller */
	HSET4(sc, ENET_ECR, ENET_ECR_RESET);
	while(HREAD4(sc, ENET_ECR) & ENET_ECR_RESET);
}

void
imxenet_iff(struct imxenet_softc *sc)
{
	struct arpcom *ac = &sc->sc_ac;
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	struct ether_multi *enm;
	struct ether_multistep step;
	uint64_t ghash = 0, ihash = 0;
	uint32_t h;

	ifp->if_flags &= ~IFF_ALLMULTI;

	if (ifp->if_flags & IFF_PROMISC) {
		ifp->if_flags |= IFF_ALLMULTI;
		ihash = 0xffffffffffffffffLLU;
	} else if (ac->ac_multirangecnt > 0) {
		ifp->if_flags |= IFF_ALLMULTI;
		ghash = 0xffffffffffffffffLLU;
	} else {
		ETHER_FIRST_MULTI(step, ac, enm);
		while (enm != NULL) {
			h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);

			ghash |= 1LLU << (((uint8_t *)&h)[3] >> 2);

			ETHER_NEXT_MULTI(step, enm);
		}
	}

	HWRITE4(sc, ENET_GAUR, (uint32_t)(ghash >> 32));
	HWRITE4(sc, ENET_GALR, (uint32_t)ghash);

	HWRITE4(sc, ENET_IAUR, (uint32_t)(ihash >> 32));
	HWRITE4(sc, ENET_IALR, (uint32_t)ihash);
}

int
imxenet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
	struct imxenet_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:
		ifp->if_flags |= IFF_UP;
		if (!(ifp->if_flags & IFF_RUNNING))
			imxenet_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
				imxenet_init(sc);
		} else {
			if (ifp->if_flags & IFF_RUNNING)
				imxenet_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)
			imxenet_iff(sc);
		error = 0;
	}

	splx(s);
	return(error);
}

void
imxenet_start(struct ifnet *ifp)
{
	struct imxenet_softc *sc = ifp->if_softc;
	struct mbuf *m_head = NULL;

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

	for (;;) {
		IFQ_POLL(&ifp->if_snd, m_head);
		if (m_head == NULL)
			break;

		if (imxenet_encap(sc, m_head)) {
			ifp->if_flags |= IFF_OACTIVE;
			break;
		}

		IFQ_DEQUEUE(&ifp->if_snd, m_head);

		ifp->if_opackets++;

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

		m_freem(m_head);
	}
}

int
imxenet_encap(struct imxenet_softc *sc, struct mbuf *m)
{
	if (sc->tx_desc_base[sc->cur_tx].status & ENET_TXD_READY) {
		printf("imxenet: tx queue full!\n");
		return EIO;
	}

	if (m->m_pkthdr.len > ENET_MAX_PKT_SIZE) {
		printf("imxenet: packet too big\n");
		return EIO;
	}

	/* copy in the actual packet */
	m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)sc->tx_buffer_base[sc->cur_tx].data);

	sc->tx_desc_base[sc->cur_tx].data_length = m->m_pkthdr.len;

	sc->tx_desc_base[sc->cur_tx].status &= ~ENET_TXD_STATUS_MASK;
	sc->tx_desc_base[sc->cur_tx].status |= (ENET_TXD_READY | ENET_TXD_LAST | ENET_TXD_TC);

#ifdef ENET_ENHANCED_BD
	sc->tx_desc_base[sc->cur_tx].enhanced_status = ENET_TXD_INT;
	sc->tx_desc_base[sc->cur_tx].update_done = 0;
#endif

	bus_dmamap_sync(sc->tbdma.dma_tag, sc->tbdma.dma_map,
	    ENET_MAX_PKT_SIZE * sc->cur_tx, ENET_MAX_PKT_SIZE,
	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);

	bus_dmamap_sync(sc->txdma.dma_tag, sc->txdma.dma_map,
	    sizeof(struct imxenet_buf_desc) * sc->cur_tx,
	    sizeof(struct imxenet_buf_desc),
	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);

	/* tx descriptors are ready */
	HWRITE4(sc, ENET_TDAR, ENET_TDAR_TDAR);

	if (sc->tx_desc_base[sc->cur_tx].status & ENET_TXD_WRAP)
		sc->cur_tx = 0;
	else
		sc->cur_tx++;

	return 0;
}

struct mbuf *
imxenet_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);
}

/*
 * Established by attachment driver at interrupt priority IPL_NET.
 */
int
imxenet_intr(void *arg)
{
	struct imxenet_softc *sc = arg;
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	u_int32_t status;

	/* Find out which interrupts are pending. */
	status = HREAD4(sc, ENET_EIR);

	/* Acknowledge the interrupts we are about to handle. */
	HWRITE4(sc, ENET_EIR, status);

	/*
	 * Wake up the blocking process to service command
	 * related interrupt(s).
	 */
	if (ISSET(status, ENET_EIR_MII)) {
		sc->intr_status |= status;
		wakeup(&sc->intr_status);
	}

	/*
	 * Handle incoming packets.
	 */
	if (ISSET(status, ENET_EIR_RXF)) {
		if (ifp->if_flags & IFF_RUNNING)
			imxenet_recv(sc);
	}

	/* Try to transmit. */
	if (ifp->if_flags & IFF_RUNNING && !IFQ_IS_EMPTY(&ifp->if_snd))
		imxenet_start(ifp);

	return 1;
}

void
imxenet_recv(struct imxenet_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ac.ac_if;
	struct mbuf_list ml = MBUF_LIST_INITIALIZER();

	bus_dmamap_sync(sc->rbdma.dma_tag, sc->rbdma.dma_map,
	    0, sc->rbdma.dma_size,
	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);

	bus_dmamap_sync(sc->rxdma.dma_tag, sc->rxdma.dma_map,
	    0, sc->rxdma.dma_size,
	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);

	while (!(sc->rx_desc_base[sc->cur_rx].status & ENET_RXD_EMPTY))
	{
		struct mbuf *m;
		m = imxenet_newbuf();

		if (m == NULL) {
			ifp->if_ierrors++;
			goto done;
		}

		m->m_pkthdr.len = m->m_len = sc->rx_desc_base[sc->cur_rx].data_length;
		m_adj(m, ETHER_ALIGN);

		memcpy(mtod(m, char *), sc->rx_buffer_base[sc->cur_rx].data,
		    sc->rx_desc_base[sc->cur_rx].data_length);

		sc->rx_desc_base[sc->cur_rx].status |= ENET_RXD_EMPTY;
		sc->rx_desc_base[sc->cur_rx].data_length = 0;

		bus_dmamap_sync(sc->rbdma.dma_tag, sc->rbdma.dma_map,
		    ENET_MAX_PKT_SIZE * sc->cur_rx, ENET_MAX_PKT_SIZE,
		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);

		bus_dmamap_sync(sc->rxdma.dma_tag, sc->rxdma.dma_map,
		    sizeof(struct imxenet_buf_desc) * sc->cur_rx,
		    sizeof(struct imxenet_buf_desc),
		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);

		if (sc->rx_desc_base[sc->cur_rx].status & ENET_RXD_WRAP)
			sc->cur_rx = 0;
		else
			sc->cur_rx++;

		ml_enqueue(&ml, m);
	}

done:
	/* rx descriptors are ready */
	HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR);

	if_input(ifp, &ml);
}

int
imxenet_wait_intr(struct imxenet_softc *sc, int mask, int timo)
{
	int status;
	int s;

	s = splnet();

	status = sc->intr_status;
	while (status == 0) {
		if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo)
		    == EWOULDBLOCK) {
			break;
		}
		status = sc->intr_status;
	}
	sc->intr_status &= ~status;

	splx(s);
	return status;
}

/*
 * MII
 * Interrupts need ENET_ECR_ETHEREN to be set,
 * so we just read the interrupt status registers.
 */
int
imxenet_miibus_readreg(struct device *dev, int phy, int reg)
{
	int r = 0;
	struct imxenet_softc *sc = (struct imxenet_softc *)dev;

	HSET4(sc, ENET_EIR, ENET_EIR_MII);

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR,
	    ENET_MMFR_ST | ENET_MMFR_OP_RD | ENET_MMFR_TA |
	    phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT);

	while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII));

	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR);

	return (r & 0xffff);
}

void
imxenet_miibus_writereg(struct device *dev, int phy, int reg, int val)
{
	struct imxenet_softc *sc = (struct imxenet_softc *)dev;

	HSET4(sc, ENET_EIR, ENET_EIR_MII);

	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR,
	    ENET_MMFR_ST | ENET_MMFR_OP_WR | ENET_MMFR_TA |
	    phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT |
	    (val & 0xffff));

	while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII));

	return;
}

void
imxenet_miibus_statchg(struct device *dev)
{
	struct imxenet_softc *sc = (struct imxenet_softc *)dev;
	int ecr;

	ecr = HREAD4(sc, ENET_ECR);
	switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
	case IFM_1000_T:  /* Gigabit */
		ecr |= ENET_ECR_SPEED;
		break;
	default:
		ecr &= ~ENET_ECR_SPEED;
	}
	HWRITE4(sc, ENET_ECR, ecr);

	return;
}

int
imxenet_ifmedia_upd(struct ifnet *ifp)
{
	struct imxenet_softc *sc = ifp->if_softc;
	struct mii_data *mii = &sc->sc_mii;
	int err;
	if (mii->mii_instance) {
		struct mii_softc *miisc;

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

void
imxenet_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
	struct imxenet_softc *sc = ifp->if_softc;
	struct mii_data *mii = &sc->sc_mii;

	mii_pollstat(mii);

	ifmr->ifm_active = mii->mii_media_active;
	ifmr->ifm_status = mii->mii_media_status;
}

/*
 * Manage DMA'able memory.
 */
int
imxenet_dma_malloc(struct imxenet_softc *sc, bus_size_t size,
    struct imxenet_dma_alloc *dma)
{
	int r;

	dma->dma_tag = sc->sc_dma_tag;
	r = bus_dmamem_alloc(dma->dma_tag, size, ENET_ALIGNMENT, 0, &dma->dma_seg,
	    1, &dma->dma_nseg, BUS_DMA_NOWAIT);
	if (r != 0) {
		printf("%s: imxenet_dma_malloc: bus_dmammem_alloc failed; "
			"size %lu, error %d\n", sc->sc_dev.dv_xname,
			(unsigned long)size, r);
		goto fail_0;
	}

	r = bus_dmamem_map(dma->dma_tag, &dma->dma_seg, dma->dma_nseg, size,
	    &dma->dma_vaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
	if (r != 0) {
		printf("%s: imxenet_dma_malloc: bus_dmammem_map failed; "
			"size %lu, error %d\n", sc->sc_dev.dv_xname,
			(unsigned long)size, r);
		goto fail_1;
	}

	r = bus_dmamap_create(dma->dma_tag, size, 1,
	    size, 0, BUS_DMA_NOWAIT, &dma->dma_map);
	if (r != 0) {
		printf("%s: imxenet_dma_malloc: bus_dmamap_create failed; "
			"error %u\n", sc->sc_dev.dv_xname, r);
		goto fail_2;
	}

	r = bus_dmamap_load(dma->dma_tag, dma->dma_map,
			    dma->dma_vaddr, size, NULL,
			    BUS_DMA_NOWAIT);
	if (r != 0) {
		printf("%s: imxenet_dma_malloc: bus_dmamap_load failed; "
			"error %u\n", sc->sc_dev.dv_xname, r);
		goto fail_3;
	}

	dma->dma_size = size;
	dma->dma_paddr = dma->dma_map->dm_segs[0].ds_addr;
	return (0);

fail_3:
	bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
fail_2:
	bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
fail_1:
	bus_dmamem_free(dma->dma_tag, &dma->dma_seg, dma->dma_nseg);
fail_0:
	dma->dma_map = NULL;
	dma->dma_tag = NULL;

	return (r);
}

void
imxenet_dma_free(struct imxenet_softc *sc, struct imxenet_dma_alloc *dma)
{
	if (dma->dma_tag == NULL)
		return;

	if (dma->dma_map != NULL) {
		bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0,
		    dma->dma_map->dm_mapsize,
		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
		bus_dmamap_unload(dma->dma_tag, dma->dma_map);
		bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, dma->dma_size);
		bus_dmamem_free(dma->dma_tag, &dma->dma_seg, dma->dma_nseg);
		bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
	}
	dma->dma_tag = NULL;
}


code:
/* $OpenBSD: imxenet.h,v 1.1 2013/09/06 20:45:53 patrick Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * 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.
 */

/* what should we use? */
#define ENET_MAX_TXD		32
#define ENET_MAX_RXD		32

#define ENET_MAX_PKT_SIZE	1536

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

/* buffer descriptor status bits */
#define ENET_RXD_EMPTY		(1 << 15)
#define ENET_RXD_WRAP		(1 << 13)
#define ENET_RXD_LAST		(1 << 11)
#define ENET_RXD_MISS		(1 << 8)
#define ENET_RXD_BC		(1 << 7)
#define ENET_RXD_MC		(1 << 6)
#define ENET_RXD_LG		(1 << 5)
#define ENET_RXD_NO		(1 << 4)
#define ENET_RXD_CR		(1 << 2)
#define ENET_RXD_OV		(1 << 1)
#define ENET_RXD_TR		(1 << 0)

#define ENET_TXD_READY		(1 << 15)
#define ENET_TXD_WRAP		(1 << 13)
#define ENET_TXD_LAST		(1 << 11)
#define ENET_TXD_TC		(1 << 10)
#define ENET_TXD_ABC		(1 << 9)
#define ENET_TXD_STATUS_MASK	0x3ff

#ifdef ENET_ENHANCED_BD
/* enhanced */
#define ENET_RXD_INT		(1 << 23)

#define ENET_TXD_INT		(1 << 30)
#endif

/*
 * Bus dma allocation structure used by
 * imxenet_dma_malloc and imxenet_dma_free.
 */
struct imxenet_dma_alloc {
	bus_addr_t		dma_paddr;
	caddr_t			dma_vaddr;
	bus_dma_tag_t		dma_tag;
	bus_dmamap_t		dma_map;
	bus_dma_segment_t	dma_seg;
	bus_size_t		dma_size;
	int			dma_nseg;
};

struct imxenet_buf_desc {
	uint16_t data_length;		/* payload's length in bytes */
	uint16_t status;		/* BD's status (see datasheet) */
	uint32_t data_pointer;		/* payload's buffer address */
#ifdef ENET_ENHANCED_BD
	uint32_t enhanced_status;	/* enhanced status with IEEE 1588 */
	uint32_t reserved0;		/* reserved */
	uint32_t update_done;		/* buffer descriptor update done */
	uint32_t timestamp;		/* IEEE 1588 timestamp */
	uint32_t reserved1[2];		/* reserved */
#endif
};

struct imxenet_buffer {
	uint8_t data[ENET_MAX_PKT_SIZE];
};