Hi,

tested w/call to axp20x_shutdown(); before the hang regression on cubie,
and pmap spam on cubie2. unfortunately i was lazy, so this includes the
earlier diff for sd/mmc support also.
both really simple, sdmmc doesn't use dma transfers yet, and axp driver is
only for shutting down and possibly w/few line diff for preventing ahci from 
attaching when pwr is not sourced from ACin.. but i believe these provide
decent starting point for anyone interested.

-Artturi


diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC
index 882a6e7..97265a0 100644
--- a/sys/arch/armv7/conf/GENERIC
+++ b/sys/arch/armv7/conf/GENERIC
@@ -98,6 +98,12 @@ ehci*                at sunxi?               # EHCI (shim)
 usb*           at ehci?        #flags 0x1
 #ohci*         at sunxi?
 #usb*          at ohci?
+sxisdmmc*      at sunxi?               # SD/MMC card controller
+sdmmc*         at sxisdmmc?            # SD/MMC bus
+sxitwi*                at sunxi?
+iic*           at sxitwi?
+
+axppmic*       at iic?
 
 # ARM Versatile Express
 vexpress0      at mainbus?
diff --git a/sys/arch/armv7/sunxi/axp20x.c b/sys/arch/armv7/sunxi/axp20x.c
new file mode 100644
index 0000000..cd42718
--- /dev/null
+++ b/sys/arch/armv7/sunxi/axp20x.c
@@ -0,0 +1,150 @@
+/*     $OpenBSD$       */
+/*
+ * Copyright (c) 2014,2016 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/sensors.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <machine/bus.h>
+
+/* Power Status Register / Input power status */
+#define        AXP209_PSR              0x00
+#define        AXP209_PSR_ACIN         (1 << 7)        /* ACIN Exists */
+#define        AXP209_PSR_VBUS         (1 << 5)        /* VBUS Exists */
+
+/* Shutdown settings, battery detection, and CHGLED Pin control */
+#define        AXP209_SDR              0x32
+#define        AXP209_SDR_SHUTDOWN     (1 << 7)        /* Shutdown Control */
+
+#define DNAME(sc)      ((sc)->sc_dev.dv_xname)
+
+struct axp20x_softc {
+       struct device   sc_dev;
+       i2c_tag_t       sc_i2c;
+       i2c_addr_t      sc_addr;
+};
+
+int    axp20x_match(struct device *, void *, void *);
+void   axp20x_attach(struct device *, struct device *, void *);
+
+int    axp20x_readb(u_char, u_char *);
+int    axp20x_writeb(u_char, u_char);
+u_int  axp20x_get_acin(void);
+void   axp20x_shutdown(void);
+
+struct cfattach axppmic_ca = {
+       sizeof(struct axp20x_softc), axp20x_match, axp20x_attach
+};
+
+struct cfdriver axppmic_cd = {
+       NULL, "axppmic", DV_DULL
+};
+
+int
+axp20x_match(struct device *parent, void *cf, void *args)
+{
+       struct i2c_attach_args *ia = args;
+       /* XXX if (BOARD_IS(_CUBIE) ?? */
+       if (strcmp(ia->ia_name, "axppmic") == 0)
+               return 1;
+       return 0;
+}
+
+void
+axp20x_attach(struct device *parent, struct device *self, void *args)
+{
+       struct axp20x_softc *sc = (struct axp20x_softc *)self;
+       struct i2c_attach_args *ia = args;
+       uint8_t psr;
+
+       sc->sc_i2c = ia->ia_tag;
+       sc->sc_addr = ia->ia_addr;
+
+       axp20x_readb(AXP209_PSR, &psr);
+       printf(" addr %#x: AXP209,", sc->sc_addr);
+       if (!(psr & (AXP209_PSR_ACIN | AXP209_PSR_VBUS)))
+               printf(" BAT");
+       else {
+               if (psr & AXP209_PSR_ACIN)
+                       printf(" ACIN");
+               if (psr & AXP209_PSR_VBUS)
+                       printf(" VBUS");
+       }
+       printf("\n");
+}
+
+int
+axp20x_readb(u_char reg, u_char *val)
+{
+       struct axp20x_softc *sc = axppmic_cd.cd_devs[0];
+       int flags = I2C_F_POLL;
+       int ret;
+
+       if (sc == NULL)
+               return 1;
+
+       iic_acquire_bus(sc->sc_i2c, flags);
+       ret = iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val, flags);
+       iic_release_bus(sc->sc_i2c, flags);
+       return ret;
+
+}
+
+int
+axp20x_writeb(u_char reg, u_char data)
+{
+       struct axp20x_softc *sc = axppmic_cd.cd_devs[0];
+       int flags = I2C_F_POLL;
+       int ret;
+
+       if (sc == NULL)
+               return 1;
+
+       iic_acquire_bus(sc->sc_i2c, flags);
+       ret = iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, data, flags);
+       iic_release_bus(sc->sc_i2c, flags);
+       return ret;
+}
+
+/*
+ * XXX this will detect power from usb-otg port as ACin on cubieboard,
+ * but as only user is the on-board ahci driver, this will likely be
+ * enough to protect against trying to spin-up on battery atleast.
+ */
+u_int
+axp20x_get_acin(void)
+{
+       u_char psr;
+       if (axp20x_readb(AXP209_PSR, &psr))
+               psr = 0;
+       if (psr & AXP209_PSR_ACIN)
+               return 1;
+       return 0;
+}
+
+void
+axp20x_shutdown(void)
+{
+       /* XXX
+        * if (!i2c_initialized) sxitwi_init(); ?
+        * or bring back bitbanging gpio 'soft'i2c..
+        */
+       axp20x_writeb(AXP209_SDR, AXP209_SDR_SHUTDOWN);
+}
diff --git a/sys/arch/armv7/sunxi/files.sunxi b/sys/arch/armv7/sunxi/files.sunxi
index 521b2d7..efdde23 100644
--- a/sys/arch/armv7/sunxi/files.sunxi
+++ b/sys/arch/armv7/sunxi/files.sunxi
@@ -48,3 +48,16 @@ file arch/armv7/sunxi/sxiuart.c              sxiuart
 device sxie: ether, ifnet, mii, ifmedia
 attach sxie at sunxi
 file   arch/armv7/sunxi/sxie.c                 sxie
+
+device sxisdmmc: sdmmcbus
+attach sxisdmmc at sunxi
+file   arch/armv7/sunxi/sxisdmmc.c             sxisdmmc
+
+device sxitwi: i2cbus
+attach sxitwi at sunxi
+file   arch/armv7/sunxi/sxitwi.c               sxitwi
+file   arch/armv7/sunxi/gttwsi_core.c          sxitwi
+
+device axppmic
+attach axppmic at i2c
+file   arch/armv7/sunxi/axp20x.c               axppmic
diff --git a/sys/arch/armv7/sunxi/gttwsi_core.c 
b/sys/arch/armv7/sunxi/gttwsi_core.c
new file mode 100644
index 0000000..e9d1113
--- /dev/null
+++ b/sys/arch/armv7/sunxi/gttwsi_core.c
@@ -0,0 +1,428 @@
+/*     $NetBSD: gttwsi_core.c,v 1.2 2014/11/23 13:37:27 jmcneill Exp $ */
+/*
+ * Copyright (c) 2008 Eiji Kawauchi.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Eiji Kawauchi.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+/*
+ * Copyright (c) 2005 Brocade Communcations, inc.
+ * All rights reserved.
+ *
+ * Written by Matt Thomas for Brocade Communcations, Inc.
+ *
+ * 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.
+ * 3. The name of Brocade Communications, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROCADE COMMUNICATIONS, INC. ``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 EITHER BROCADE COMMUNICATIONS, INC. 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.
+ */
+
+/*
+ * Marvell Two-Wire Serial Interface (aka I2C) master driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/rwlock.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <machine/bus.h>
+
+#include <armv7/sunxi/gttwsivar.h>
+
+#define        GTTWSI_SIZE             0x100
+
+#define        GTTWSI_ALLWINNER
+#if defined(GTTWSI_ALLWINNER)
+#define        TWSI_SLAVEADDR          0x00
+#define        TWSI_EXTEND_SLAVEADDR   0x04
+#define        TWSI_DATA               0x08
+#define        TWSI_CONTROL            0x0c
+#define        TWSI_STATUS             0x10
+#define        TWSI_BAUDRATE           0x14
+#define        TWSI_SOFTRESET          0x18
+#else
+#define        TWSI_SLAVEADDR          0x00
+#define        TWSI_EXTEND_SLAVEADDR   0x10
+#define        TWSI_DATA               0x04
+#define        TWSI_CONTROL            0x08
+#define        TWSI_STATUS             0x0c    /* for read */
+#define        TWSI_BAUDRATE           0x0c    /* for write */
+#define        TWSI_SOFTRESET          0x1c
+#endif
+
+#define        SLAVEADDR_GCE_MASK      0x01
+#define        SLAVEADDR_SADDR_MASK    0xfe
+
+#define        EXTEND_SLAVEADDR_MASK   0xff
+
+#define        DATA_MASK               0xff
+
+#define        CONTROL_ACK             (1 << 2)
+#define        CONTROL_IFLG            (1 << 3)
+#define        CONTROL_STOP            (1 << 4)
+#define        CONTROL_START           (1 << 5)
+#define        CONTROL_TWSIEN          (1 << 6)
+#define        CONTROL_INTEN           (1 << 7)
+
+#define        STAT_BE         0x00    /* Bus Error */
+#define        STAT_SCT        0x08    /* Start condition transmitted */
+#define        STAT_RSCT       0x10    /* Repeated start condition transmitted 
*/
+#define        STAT_AWBT_AR    0x18    /* Address + write bit transd, ack 
recvd */
+#define        STAT_AWBT_ANR   0x20    /* Address + write bit transd, ack not 
recvd */
+#define        STAT_MTDB_AR    0x28    /* Master transd data byte, ack recvd */
+#define        STAT_MTDB_ANR   0x30    /* Master transd data byte, ack not 
recvd */
+#define        STAT_MLADADT    0x38    /* Master lost arbitr during addr or 
data tx */
+#define        STAT_ARBT_AR    0x40    /* Address + read bit transd, ack recvd 
*/
+#define        STAT_ARBT_ANR   0x48    /* Address + read bit transd, ack not 
recvd */
+#define        STAT_MRRD_AT    0x50    /* Master received read data, ack 
transd */
+#define        STAT_MRRD_ANT   0x58    /* Master received read data, ack not 
transd */
+#define        STAT_SAWBT_AR   0xd0    /* Second addr + write bit transd, ack 
recvd */
+#define        STAT_SAWBT_ANR  0xd8    /* S addr + write bit transd, ack not 
recvd */
+#define        STAT_SARBT_AR   0xe0    /* Second addr + read bit transd, ack 
recvd */
+#define        STAT_SARBT_ANR  0xe8    /* S addr + read bit transd, ack not 
recvd */
+#define        STAT_NRS        0xf8    /* No relevant status */
+
+#define        SOFTRESET_VAL           0               /* reset value */
+
+#define        TWSI_RETRY_COUNT        1000            /* retry loop count */
+#define        TWSI_RETRY_DELAY        1               /* retry delay */
+#define        TWSI_STAT_DELAY         1               /* poll status delay */
+#define        TWSI_READ_DELAY         2               /* read delay */
+#define        TWSI_WRITE_DELAY        2               /* write delay */
+
+
+#define        TWSI_DEBUG
+/*#undef       TWSI_DEBUG*/
+#ifdef TWSI_DEBUG
+/*
+ * conditional debugging
+ */
+#define        CD_INIT         0x00000001      /* init */
+#define        CD_ERR          0x00000002      /* errors */
+#define        CD_TIMO         0x00000004      /* timeout */
+#define        CD_INFO         0x00000004      /* timeout */
+#define        CD_DBG          0x00000010      /* just dbg */
+#define        CD_SPAM         0x00000020      /* verbose boot */
+#define        CD_ALL          0xffffffff
+
+int gttwsi_debug = 0 /*| CD_INIT | CD_DBG | CD_SPAM | CD_ALL*/;
+
+#define        DPRINTF(flg, stmt) \
+do { \
+       if (gttwsi_debug & (flg)) \
+               printf stmt; \
+} while (0)
+#else
+#define        DPRINTF(flg, stmt) do { } while (0)
+#endif /* TWSI_DEBUG */
+
+#define DNAME(sc)      ((sc)->sc_dev.dv_xname)
+
+int    gttwsi_acquire_bus(void *, int);
+void   gttwsi_release_bus(void *, int);
+int    gttwsi_send_start(void *v, int flags);
+int    gttwsi_send_stop(void *v, int flags);
+int    gttwsi_initiate_xfer(void *v, i2c_addr_t addr, int flags);
+int    gttwsi_read_byte(void *v, uint8_t *valp, int flags);
+int    gttwsi_write_byte(void *v, uint8_t val, int flags);
+
+int    gttwsi_wait(struct gttwsi_softc *, uint32_t, uint32_t, int);
+
+inline u_int
+gttwsi_read_4(struct gttwsi_softc *sc, u_int reg)
+{
+       u_int val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
+
+       DPRINTF(CD_SPAM, ("I2C:R:%02x:%02x\n", reg, val));
+#if !defined(TWSI_DEBUG)
+       delay(TWSI_READ_DELAY);
+#endif
+
+       return val;
+}
+
+inline void
+gttwsi_write_4(struct gttwsi_softc *sc, u_int reg, u_int val)
+{
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
+
+       DPRINTF(CD_SPAM, ("I2C:W:%02x:%02x\n", reg, val));
+#if !defined(TWSI_DEBUG)
+       delay(TWSI_WRITE_DELAY);
+#endif
+
+       return;
+}
+
+void
+gttwsi_attach_subr(struct device *self, bus_space_tag_t iot, 
bus_space_handle_t ioh)
+{
+       struct gttwsi_softc * const sc = (struct gttwsi_softc *)self;
+
+       sc->sc_iot = iot;
+       sc->sc_ioh = ioh;
+
+       rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname);
+
+       sc->sc_started = 0;
+       sc->sc_ic.ic_cookie = sc;
+       sc->sc_ic.ic_acquire_bus = gttwsi_acquire_bus;
+       sc->sc_ic.ic_release_bus = gttwsi_release_bus;
+       sc->sc_ic.ic_exec = NULL;
+       sc->sc_ic.ic_send_start = gttwsi_send_start;
+       sc->sc_ic.ic_send_stop = gttwsi_send_stop;
+       sc->sc_ic.ic_initiate_xfer = gttwsi_initiate_xfer;
+       sc->sc_ic.ic_read_byte = gttwsi_read_byte;
+       sc->sc_ic.ic_write_byte = gttwsi_write_byte;
+
+       /*
+        * Put the controller into Soft Reset.
+        */
+
+       gttwsi_write_4(sc, TWSI_SOFTRESET, SOFTRESET_VAL);
+
+       printf("\n");
+}
+
+void
+gttwsi_config_children(struct device *self)
+{
+       struct gttwsi_softc * const sc = (struct gttwsi_softc *)self;
+       struct i2cbus_attach_args iba;
+
+       memset(&iba, 0, sizeof(iba));
+       iba.iba_name = "iic";
+       iba.iba_tag = &sc->sc_ic;
+
+       (void)config_found(&sc->sc_dev, &iba, iicbus_print);
+}
+
+int
+gttwsi_intr(void *arg)
+{
+       struct gttwsi_softc *sc = arg;
+       u_int val;
+
+       val = gttwsi_read_4(sc, TWSI_CONTROL);
+       if (val & CONTROL_IFLG) {
+               gttwsi_write_4(sc, TWSI_CONTROL, val & ~CONTROL_INTEN);
+               wakeup(&sc->sc_dev);
+               return 1;
+       }
+       return 0;
+}
+
+int
+gttwsi_acquire_bus(void *arg, int flags)
+{
+       struct gttwsi_softc *sc = arg;
+
+       if (flags & I2C_F_POLL)
+               return 0;
+
+       return rw_enter(&sc->sc_buslock, RW_WRITE);
+}
+
+void
+gttwsi_release_bus(void *arg, int flags)
+{
+       struct gttwsi_softc *sc = arg;
+
+       if (flags & I2C_F_POLL)
+               return;
+
+       rw_exit(&sc->sc_buslock);
+}
+
+int
+gttwsi_send_start(void *v, int flags)
+{
+       struct gttwsi_softc *sc = v;
+       int expect;
+
+       if (sc->sc_started)
+               expect = STAT_RSCT;
+       else
+               expect = STAT_SCT;
+       sc->sc_started = 1;
+       return gttwsi_wait(sc, CONTROL_START, expect, flags);
+}
+
+int
+gttwsi_send_stop(void *v, int flags)
+{
+       struct gttwsi_softc *sc = v;
+       int retry = TWSI_RETRY_COUNT;
+       u_int control;
+
+       sc->sc_started = 0;
+
+       /* Interrupt is not generated for STAT_NRS. */
+       control = CONTROL_STOP | CONTROL_TWSIEN;
+       gttwsi_write_4(sc, TWSI_CONTROL, control);
+       while (--retry > 0) {
+               if (gttwsi_read_4(sc, TWSI_STATUS) == STAT_NRS)
+                       return 0;
+               delay(TWSI_STAT_DELAY);
+       }
+
+       DPRINTF(CD_ERR, ("%s: send STOP failed\n", DNAME(sc)));
+
+       return -1;
+}
+
+int
+gttwsi_initiate_xfer(void *v, i2c_addr_t addr, int flags)
+{
+       struct gttwsi_softc *sc = v;
+       u_int data, expect;
+       int error, read;
+
+       gttwsi_send_start(v, flags);
+
+       read = (flags & I2C_F_READ) != 0;
+       if (read)
+               expect = STAT_ARBT_AR;
+       else
+               expect = STAT_AWBT_AR;
+
+       /*
+        * First byte contains whether this xfer is a read or write.
+        */
+       data = read;
+       if (addr > 0x7f) {
+               /*
+                * If this is a 10bit request, the first address byte is
+                * 0b11110<b9><b8><r/w>.
+                */
+               data |= 0xf0 | ((addr & 0x300) >> 7);
+               gttwsi_write_4(sc, TWSI_DATA, data);
+               error = gttwsi_wait(sc, 0, expect, flags);
+               if (error)
+                       return error;
+               /*
+                * The first address byte has been sent, now to send
+                * the second one.
+                */
+               if (read)
+                       expect = STAT_SARBT_AR;
+               else
+                       expect = STAT_SAWBT_AR;
+               data = (uint8_t)addr;
+       } else
+               data |= (addr << 1);
+
+       gttwsi_write_4(sc, TWSI_DATA, data);
+       return gttwsi_wait(sc, 0, expect, flags);
+}
+
+int
+gttwsi_read_byte(void *v, uint8_t *valp, int flags)
+{
+       struct gttwsi_softc *sc = v;
+       int error;
+
+       if (flags & I2C_F_LAST)
+               error = gttwsi_wait(sc, 0, STAT_MRRD_ANT, flags);
+       else
+               error = gttwsi_wait(sc, CONTROL_ACK, STAT_MRRD_AT, flags);
+       if (!error)
+               *valp = gttwsi_read_4(sc, TWSI_DATA);
+       if ((flags & (I2C_F_LAST | I2C_F_STOP)) == (I2C_F_LAST | I2C_F_STOP))
+               error = gttwsi_send_stop(sc, flags);
+       return error;
+}
+
+int
+gttwsi_write_byte(void *v, uint8_t val, int flags)
+{
+       struct gttwsi_softc *sc = v;
+       int error;
+
+       gttwsi_write_4(sc, TWSI_DATA, val);
+       error = gttwsi_wait(sc, 0, STAT_MTDB_AR, flags);
+       if (flags & I2C_F_STOP)
+               gttwsi_send_stop(sc, flags);
+       return error;
+}
+
+int
+gttwsi_wait(struct gttwsi_softc *sc, u_int control, u_int expect, int flags)
+{
+       u_int status;
+       int timo, error = 0;
+
+       delay(5);
+       if (!(flags & I2C_F_POLL))
+               control |= CONTROL_INTEN;
+       gttwsi_write_4(sc, TWSI_CONTROL, control | CONTROL_TWSIEN);
+
+       timo = 0;
+       do {
+               control = gttwsi_read_4(sc, TWSI_CONTROL);
+               if (control & CONTROL_IFLG)
+                       break;
+               if (flags & I2C_F_POLL)
+                       delay(TWSI_RETRY_DELAY);
+               else {
+                       error = tsleep(&sc->sc_dev, PWAIT, "gttwsi", 100);
+                       if (error)
+                               return error;
+               }
+       } while (++timo < 1000000);
+
+       status = gttwsi_read_4(sc, TWSI_STATUS);
+       if (status != expect) {
+               DPRINTF(CD_ERR, ("%s: status %#x expected %#x\n",
+                   DNAME(sc), status, expect));
+               return EIO;
+       }
+       return error;
+}
diff --git a/sys/arch/armv7/sunxi/gttwsivar.h b/sys/arch/armv7/sunxi/gttwsivar.h
new file mode 100644
index 0000000..eca2e4b
--- /dev/null
+++ b/sys/arch/armv7/sunxi/gttwsivar.h
@@ -0,0 +1,89 @@
+/*     $NetBSD: gttwsivar.h,v 1.2 2014/11/23 13:37:27 jmcneill Exp $   */
+/*
+ * Copyright (c) 2008 Eiji Kawauchi.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Eiji Kawauchi.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+/*
+ * Copyright (c) 2005 Brocade Communcations, inc.
+ * All rights reserved.
+ *
+ * Written by Matt Thomas for Brocade Communcations, Inc.
+ *
+ * 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.
+ * 3. The name of Brocade Communications, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROCADE COMMUNICATIONS, INC. ``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 EITHER BROCADE COMMUNICATIONS, INC. 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.
+ */
+
+#ifndef _DEV_MARVELL_GTTWSIVAR_H_
+#define _DEV_MARVELL_GTTWSIVAR_H_
+
+/*
+ * Marvell Two-Wire Serial Interface (aka I2C) master driver
+ */
+#include <sys/device.h>
+#include <sys/rwlock.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <machine/bus.h>
+
+struct gttwsi_softc {
+       struct device           sc_dev;
+       bus_space_tag_t         sc_iot;
+       bus_space_handle_t      sc_ioh;
+       u_int                   sc_started;
+       struct i2c_controller   sc_ic;
+       struct rwlock           sc_buslock;
+};
+
+void   gttwsi_attach_subr(struct device *, bus_space_tag_t, 
bus_space_handle_t);
+void   gttwsi_config_children(struct device *);
+
+int    gttwsi_intr(void *);
+
+#endif /* _DEV_MARVELL_GTTSWI_VAR_H_ */
diff --git a/sys/arch/armv7/sunxi/sun4i.c b/sys/arch/armv7/sunxi/sun4i.c
index 6c3197a..92a6d09 100644
--- a/sys/arch/armv7/sunxi/sun4i.c
+++ b/sys/arch/armv7/sunxi/sun4i.c
@@ -126,6 +126,23 @@ struct armv7_dev sxia1x_devs[] = {
          .irq = { EMAC_IRQ}
        },
 
+       /* TWI (i2c) */
+       { .name = "sxitwi",
+         .unit = 0,
+         .mem = { { TWIx_ADDR(0), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(0) }
+       },
+       { .name = "sxitwi",
+         .unit = 1,
+         .mem = { { TWIx_ADDR(1), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(1) }
+       },
+       { .name = "sxitwi",
+         .unit = 2,
+         .mem = { { TWIx_ADDR(2), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(2) }
+       },
+
        /* SATA/AHCI */
        { .name = "ahci",
          .unit = 0,
@@ -155,6 +172,28 @@ struct armv7_dev sxia1x_devs[] = {
          .irq = { USB1_IRQ }
        },
 
+       /* SD/MMC */
+       { .name = "sxisdmmc",
+         .unit = 0,
+         .mem = { { SDMMCx_ADDR(0), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(0) }
+       },
+       { .name = "sxisdmmc",
+         .unit = 1,
+         .mem = { { SDMMCx_ADDR(1), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(1) }
+       },
+       { .name = "sxisdmmc",
+         .unit = 2,
+         .mem = { { SDMMCx_ADDR(2), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(2) }
+       },
+       { .name = "sxisdmmc",
+         .unit = 3,
+         .mem = { { SDMMCx_ADDR(3), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(3) }
+       },
+
        /* Terminator */
        { .name = NULL,
          .unit = 0,
diff --git a/sys/arch/armv7/sunxi/sun7i.c b/sys/arch/armv7/sunxi/sun7i.c
index 53978f3..6c49f0d 100644
--- a/sys/arch/armv7/sunxi/sun7i.c
+++ b/sys/arch/armv7/sunxi/sun7i.c
@@ -108,6 +108,34 @@ struct armv7_dev sxia20_devs[] = {
          .irq = { EMAC_IRQ}
        },
 
+       /* TWI (i2c) */
+       { .name = "sxitwi",
+         .unit = 0,
+         .mem = { { TWIx_ADDR(0), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(0) }
+       },
+       { .name = "sxitwi",
+         .unit = 1,
+         .mem = { { TWIx_ADDR(1), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(1) }
+       },
+       { .name = "sxitwi",
+         .unit = 2,
+         .mem = { { TWIx_ADDR(2), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(2) }
+       },
+       { .name = "sxitwi",
+         .unit = 3,
+         .mem = { { TWIx_ADDR(3), TWIx_SIZE } },
+         .irq = { TWIx_IRQ(3) }
+       },
+       { .name = "sxitwi",
+         .unit = 4,
+         .mem = { { TWI4_ADDR, TWIx_SIZE } },
+         .irq = { TWIx_IRQ(4) }
+       },
+
+
        /* SATA/AHCI */
        { .name = "ahci",
          .unit = 0,
@@ -137,6 +165,28 @@ struct armv7_dev sxia20_devs[] = {
          .irq = { USB1_IRQ }
        },
 
+       /* SD/MMC */
+       { .name = "sxisdmmc",
+         .unit = 0,
+         .mem = { { SDMMCx_ADDR(0), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(0) }
+       },
+       { .name = "sxisdmmc",
+         .unit = 1,
+         .mem = { { SDMMCx_ADDR(1), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(1) }
+       },
+       { .name = "sxisdmmc",
+         .unit = 2,
+         .mem = { { SDMMCx_ADDR(2), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(2) }
+       },
+       { .name = "sxisdmmc",
+         .unit = 3,
+         .mem = { { SDMMCx_ADDR(3), SDMMCx_SIZE } },
+         .irq = { SDMMCx_IRQ(3) }
+       },
+
        /* Terminator */
        { .name = NULL,
          .unit = 0,
diff --git a/sys/arch/armv7/sunxi/sunxi.c b/sys/arch/armv7/sunxi/sunxi.c
index 2ed498d..4675522 100644
--- a/sys/arch/armv7/sunxi/sunxi.c
+++ b/sys/arch/armv7/sunxi/sunxi.c
@@ -62,6 +62,8 @@ struct board_dev sun4i_devs[] = {
        { "ohci",       0 },
        { "ohci",       1 },
 #endif
+       { "sxisdmmc",   0 },
+       { "sxitwi",     0 },
        { NULL,         0 }
 };
 
@@ -86,6 +88,8 @@ struct board_dev sun7i_devs[] = {
        { "ohci",       0 },
        { "ohci",       1 },
 #endif
+       { "sxisdmmc",   0 },
+       { "sxitwi",     0 },
        { NULL,         0 }
 };
 
diff --git a/sys/arch/armv7/sunxi/sunxi_machdep.c 
b/sys/arch/armv7/sunxi/sunxi_machdep.c
index 32dfcc8..f49a4f2 100644
--- a/sys/arch/armv7/sunxi/sunxi_machdep.c
+++ b/sys/arch/armv7/sunxi/sunxi_machdep.c
@@ -37,6 +37,7 @@ 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 void axp20x_shutdown(void);
 extern int comcnspeed;
 extern int comcnmode;
 
@@ -83,7 +84,7 @@ sunxi_platform_watchdog_reset(void)
 void
 sunxi_platform_powerdown(void)
 {
-
+       axp20x_shutdown();
 }
 
 const char *
diff --git a/sys/arch/armv7/sunxi/sunxireg.h b/sys/arch/armv7/sunxi/sunxireg.h
index 8153efa..514d82d 100644
--- a/sys/arch/armv7/sunxi/sunxireg.h
+++ b/sys/arch/armv7/sunxi/sunxireg.h
@@ -37,6 +37,7 @@
 #define        SXICMS4(sc, reg, mask, bits)                                    
\
        SXIWRITE4((sc), (reg), (SXIREAD4((sc), (reg)) & ~(mask)) | (bits))
 
+#define        SUNXI_REF_FREQ          (24 * 1000 * 1000)      /* OSC24M */
 #define        TIMER0_FREQUENCY        (32768)
 #define        TIMER1_FREQUENCY        (32768)
 #define        TIMER2_FREQUENCY        (32768)
@@ -50,9 +51,9 @@
 #define        DMAC_SIZE               0x1000
 #define        DMAC_IRQ                27
 
-#define        SDMMC0_ADDR             0x01c0f000
 #define        SDMMCx_SIZE             0x1000
-#define        SDMMC0_IRQ              32
+#define        SDMMCx_ADDR(x)          (0x01c0f000 + ((x) * SDMMCx_SIZE))
+#define        SDMMCx_IRQ(x)           (32 + (x))
 
 #define        SATA_ADDR               0x01c18000
 #define        SATA_SIZE               0x1000
@@ -102,6 +103,11 @@
 #define        UART6_IRQ               19
 #define        UART7_IRQ               20
 
+#define        TWIx_SIZE               0x400
+#define        TWIx_ADDR(x)            (0x01c2ac00 + ((x) * TWIx_SIZE))
+#define        TWI4_ADDR               0x01c2c000
+#define        TWIx_IRQ(x)             (((x) < 3 ? 7 : 88) + (x))
+
 #define        USB0_ADDR               0x01c13000 /* usb otg */
 #define        USB1_ADDR               0x01c14000 /* first port up from pcb */
 #define        USB2_ADDR               0x01c1c000 /* 'top port' == above USB1 
*/
diff --git a/sys/arch/armv7/sunxi/sxiccmu.c b/sys/arch/armv7/sunxi/sxiccmu.c
index ddfc415..9bc56de 100644
--- a/sys/arch/armv7/sunxi/sxiccmu.c
+++ b/sys/arch/armv7/sunxi/sxiccmu.c
@@ -40,7 +40,9 @@
 
 #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_GET_CLK_DIV_RATIO_M(x)     ((x) & 0x0f)
+#define        CCMU_CLK_DIV_RATIO_N(x)         (((x) & 0x03) << 16)
+#define        CCMU_CLK_DIV_RATIO_M(x)         ((x) & 0x0f)
 
 #define        CCMU_PLL6_CFG                   0x28
 #define        CCMU_PLL6_EN                    (1U << 31)
@@ -49,6 +51,9 @@
 #define        CCMU_PLL6_FACTOR_N              (31 << 8)
 #define        CCMU_PLL6_FACTOR_K              (3 << 4)
 #define        CCMU_PLL6_FACTOR_M              (3 << 0)
+#define        CCMU_PLL6_GET_FACTOR_N(x)       (((x) >> 8) & 31)
+#define        CCMU_PLL6_GET_FACTOR_K(x)       (((x) >> 4) & 3)
+#define        CCMU_PLL6_GET_FACTOR_M(x)       ((x) & 3)
 
 #define        CCMU_AHB_GATING0                0x60
 #define        CCMU_AHB_GATING_USB0            (1 << 0)
@@ -77,6 +82,26 @@
 #define        CCMU_NAND_CLK_SRC_GATING_PLL5   (2 << 24)
 #define        CCMU_NAND_CLK_SRC_GATING_MASK   (3 << 24)
 
+#define        CCMU_SDMMCx_CLK(x)                      (0x88 + (4 * (x)))
+#define        CCMU_SDMMC_CLK_SRC_GATING_OSC24M        (0 << 24)
+#define        CCMU_SDMMC_CLK_SRC_GATING_PLL6          (1 << 24)
+#define        CCMU_SDMMC_CLK_SRC_GATING_PLL5          (2 << 24)
+#define        CCMU_SDMMC_CLK_SRC_GATING_MASK          (3 << 24)
+#define        CCMU_SDMMC_CLK_PHASE_CTR_MASK           (7 << 20)
+#define        CCMU_SDMMC_CLK_OUTPUT_PHASE_CTR_MASK    (7 << 8)
+#define        CCMU_SDMMC_CLK_DIV_RATIO_N_MASK         (3 << 16)
+#define        CCMU_SDMMC_CLK_DIV_RATIO_M_MASK         (7 << 0)
+
+#define        CCMU_SDMMC_CLK_SET_PHASE_CTR(x)         (((x) & 7) << 20)
+#define        CCMU_SDMMC_CLK_SET_OUTPUT_PHASE_CTR(x)  (((x) & 7) << 8)
+
+#define        CCMU_SDMMC_CLK_CLR_MASK         \
+       (CCMU_SDMMC_CLK_SRC_GATING_MASK | \
+       CCMU_SDMMC_CLK_PHASE_CTR_MASK | \
+       CCMU_SDMMC_CLK_OUTPUT_PHASE_CTR_MASK | \
+       CCMU_SDMMC_CLK_DIV_RATIO_N_MASK | \
+       CCMU_SDMMC_CLK_DIV_RATIO_M_MASK)
+
 #define        CCMU_SATA_CLK                   0xc8
 #define        CCMU_SATA_CLK_SRC_GATING        (1 << 24)
 
@@ -273,3 +298,63 @@ sxiccmu_disablemodule(int mod)
                break;
        }
 }
+
+/* XXX dirty because */
+inline u_int
+sxiccmu_pll6_get_rate(void)
+{
+       struct sxiccmu_softc *sc = sxiccmu_cd.cd_devs[0];
+       u_int n, k, m;
+       const u_int pll6 = SXIREAD4(sc, CCMU_PLL6_CFG);
+        n = CCMU_PLL6_GET_FACTOR_N(pll6);
+        k = CCMU_PLL6_GET_FACTOR_K(pll6) + 1;
+        m = 2;
+       return (SUNXI_REF_FREQ * n * k) / m;
+}
+
+int
+sxiccmu_set_sdmmc_clock(u_int sdmmc_port, u_int freq)
+{
+       struct sxiccmu_softc *sc = sxiccmu_cd.cd_devs[0];
+       u_int odly, sdly, clksrc, n, m;
+       u_int osc24m_freq = SUNXI_REF_FREQ / 1000;
+       u_int pll_freq;
+
+       pll_freq = sxiccmu_pll6_get_rate() / 1000;
+
+       DPRINTF(("\n%s: freq = %d pll_freq = %d\n", __func__, freq, pll_freq));
+
+       if (freq <= 400) {
+               odly = 0;
+               sdly = 0;
+               clksrc = CCMU_SDMMC_CLK_SRC_GATING_OSC24M;
+               n = 2;
+               if (freq > 0)
+                       m = ((osc24m_freq / (1 << n)) / freq) - 1;
+               else
+                       m = 15;
+       } else if (freq <= 25000) {
+               odly = 0;
+               sdly = 5;
+               clksrc = CCMU_SDMMC_CLK_SRC_GATING_PLL6;
+               n = 0;
+               m = ((pll_freq / freq) / (1 << n)) - 1;
+       } else if (freq <= 50000) {
+               odly = 3;
+               sdly = 5;
+               clksrc = CCMU_SDMMC_CLK_SRC_GATING_PLL6;
+               n = 0;
+               m = ((pll_freq / freq) / (1 << n)) - 1;
+       } else {
+               /* UHS speeds not implemented yet */
+               return EIO;
+       }
+
+       SXICMS4(sc, CCMU_SDMMCx_CLK(sdmmc_port), CCMU_SDMMC_CLK_CLR_MASK,
+           CCMU_CLK_DIV_RATIO_M(m) | CCMU_GET_CLK_DIV_RATIO_N(n) |
+           CCMU_SDMMC_CLK_SET_OUTPUT_PHASE_CTR(odly) | clksrc |
+           CCMU_SDMMC_CLK_SET_PHASE_CTR(sdly) | CCMU_PLL6_EN);
+       delay(20000);
+
+       return 0;
+}
diff --git a/sys/arch/armv7/sunxi/sxiccmuvar.h 
b/sys/arch/armv7/sunxi/sxiccmuvar.h
index cf68e7c..42ac85b 100644
--- a/sys/arch/armv7/sunxi/sxiccmuvar.h
+++ b/sys/arch/armv7/sunxi/sxiccmuvar.h
@@ -17,6 +17,7 @@
 
 void sxiccmu_enablemodule(int);
 void sxiccmu_disablemodule(int);
+int sxiccmu_set_sdmmc_clock(u_int, u_int);
 
 enum CCMU_MODULES {
        CCMU_EHCI0,
diff --git a/sys/arch/armv7/sunxi/sxisdmmc.c b/sys/arch/armv7/sunxi/sxisdmmc.c
new file mode 100644
index 0000000..9d3e5ac
--- /dev/null
+++ b/sys/arch/armv7/sunxi/sxisdmmc.c
@@ -0,0 +1,600 @@
+/*     $OpenBSD$       */
+/* $NetBSD: awin_mmc.c,v 1.5 2014/09/07 15:38:06 jmcneill Exp $ */
+
+/*
+ * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
+ * 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 THE AUTHOR ``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 THE AUTHOR 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/device.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#include <dev/sdmmc/sdmmcchip.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#include <armv7/armv7/armv7var.h>
+
+#include <arm88k/sunxi/sunxireg.h>
+#include <arm88k/sunxi/sxiccmuvar.h>
+
+#define        SXISDMMC_DEBUG
+/*#undef       SXISDMMC_DEBUG*/
+#ifdef SXISDMMC_DEBUG
+/*
+ * conditional debugging
+ */
+#define        CD_INIT         0x00000001      /* init */
+#define        CD_ERR          0x00000002      /* errors */
+#define        CD_TIMO         0x00000004      /* timeout */
+#define        CD_DBG          0x00000010      /* just dbg */
+#define        CD_SPAM         0x00000020      /* verbose boot */
+#define CD_ALL         0xffffffff
+
+int sxisdmmc_debug = 0 | CD_INIT /*| CD_DBG | CD_SPAM | CD_ALL*/;
+
+#define        DPRINTF(flg, stmt) \
+do { \
+       if (sxisdmmc_debug & (flg)) \
+               printf stmt; \
+} while (0)
+#else
+#define        DPRINTF(flg, stmt) do { } while (0)
+#endif /* SXISDMMC_DEBUG */
+
+#define DNAME(sc)      ((sc)->sc_dev.dv_xname)
+
+/* registers */
+#define SDMMC_GCTRL                    0x0000
+#define SDMMC_CLKCR                    0x0004
+#define SDMMC_TIMEOUT                  0x0008
+#define SDMMC_WIDTH                    0x000c
+#define SDMMC_BLKSZ                    0x0010
+#define SDMMC_BYTECNT                  0x0014
+#define SDMMC_CMD                      0x0018
+#define SDMMC_ARG                      0x001c
+#define SDMMC_RESP0                    0x0020
+#define SDMMC_RESP1                    0x0024
+#define SDMMC_RESP2                    0x0028
+#define SDMMC_RESP3                    0x002c
+#define SDMMC_IMASK                    0x0030
+#define SDMMC_MINT                     0x0034
+#define SDMMC_RINT                     0x0038
+#define SDMMC_STATUS                   0x003c
+#define SDMMC_FTRGLEVEL                        0x0040
+#define SDMMC_FUNCSEL                  0x0044
+#define SDMMC_CBCR                     0x0048
+#define SDMMC_BBCR                     0x004c
+#define SDMMC_DBGC                     0x0050
+#define SDMMC_DMAC                     0x0080
+#define SDMMC_DLBA                     0x0084
+#define SDMMC_IDST                     0x0088
+#define SDMMC_IDIE                     0x008c
+#define SDMMC_CHDA                     0x0090
+#define SDMMC_CBDA                     0x0094
+#define SDMMC_FIFO                     0x0100
+
+/* register bitdefs */
+#define SDMMC_GCTRL_ACC_BY_AHB         (1 << 31)
+#define SDMMC_GCTRL_WAIT_MEM_ACC_DONE  (1 << 30)
+#define SDMMC_GCTRL_DDR_MODE           (1 << 10)
+#define SDMMC_GCTRL_DEBOUNCEEN         (1 << 8)
+#define SDMMC_GCTRL_DMAEN              (1 << 5)
+#define SDMMC_GCTRL_INTEN              (1 << 4)
+#define SDMMC_GCTRL_DMARESET           (1 << 2)
+#define SDMMC_GCTRL_FIFORESET          (1 << 1)
+#define SDMMC_GCTRL_SOFTRESET          (1 << 0)
+#define SDMMC_GCTRL_RESET              \
+       (SDMMC_GCTRL_SOFTRESET | \
+        SDMMC_GCTRL_FIFORESET | \
+        SDMMC_GCTRL_DMARESET)
+
+#define SDMMC_CLKCR_LOWPOWERON         (1 << 17)
+#define SDMMC_CLKCR_CARDCLKON          (1 << 16)
+
+#define SDMMC_CMD_START                        (1 << 31)
+#define SDMMC_CMD_USE_HOLD_REG         (1 << 29)
+#define SDMMC_CMD_VOL_SWITCH           (1 << 28)
+#define SDMMC_CMD_BOOT_ABORT           (1 << 27)
+#define SDMMC_CMD_BOOT_ACK_EXP         (1 << 26)
+#define SDMMC_CMD_ALT_BOOT_OPT         (1 << 25)
+#define SDMMC_CMD_ENBOOT               (1 << 24)
+#define SDMMC_CMD_CCS_EXP              (1 << 23)
+#define SDMMC_CMD_RD_CEATA_DEV         (1 << 22)
+#define SDMMC_CMD_UPCLK_ONLY           (1 << 21)
+#define SDMMC_CMD_SEND_INIT_SEQ                (1 << 15)
+#define SDMMC_CMD_STOP_ABORT_CMD       (1 << 14)
+#define SDMMC_CMD_WAIT_PRE_OVER                (1 << 13)
+#define SDMMC_CMD_SEND_AUTO_STOP       (1 << 12)
+#define SDMMC_CMD_SEQMOD               (1 << 11)
+#define SDMMC_CMD_WRITE                        (1 << 10)
+#define SDMMC_CMD_DATA_EXP             (1 << 9)
+#define SDMMC_CMD_CHECK_RSP_CRC                (1 << 8)
+#define SDMMC_CMD_LONG_RSP             (1 << 7)
+#define SDMMC_CMD_RSP_EXP              (1 << 6)
+
+#define SDMMC_INT_CARD_REMOVE          (1 << 31)
+#define SDMMC_INT_CARD_INSERT          (1 << 30)
+#define SDMMC_INT_SDIO_INT             (1 << 16)
+#define SDMMC_INT_END_BIT_ERR          (1 << 15)
+#define SDMMC_INT_AUTO_CMD_DONE                (1 << 14)
+#define SDMMC_INT_START_BIT_ERR                (1 << 13)
+#define SDMMC_INT_HW_LOCKED            (1 << 12)
+#define SDMMC_INT_FIFO_RUN_ERR         (1 << 11)
+#define SDMMC_INT_VOL_CHG_DONE         (1 << 10)
+#define SDMMC_INT_DATA_STARVE          (1 << 10)
+#define SDMMC_INT_BOOT_START           (1 << 9)
+#define SDMMC_INT_DATA_TIMEOUT         (1 << 9)
+#define SDMMC_INT_ACK_RCV              (1 << 8)
+#define SDMMC_INT_RESP_TIMEOUT         (1 << 8)
+#define SDMMC_INT_DATA_CRC_ERR         (1 << 7)
+#define SDMMC_INT_RESP_CRC_ERR         (1 << 6)
+#define SDMMC_INT_RX_DATA_REQ          (1 << 5)
+#define SDMMC_INT_TX_DATA_REQ          (1 << 4)
+#define SDMMC_INT_DATA_OVER            (1 << 3)
+#define SDMMC_INT_CMD_DONE             (1 << 2)
+#define SDMMC_INT_RESP_ERR             (1 << 1)
+#define SDMMC_INT_ERROR \
+       (SDMMC_INT_RESP_ERR | SDMMC_INT_RESP_CRC_ERR | \
+        SDMMC_INT_DATA_CRC_ERR | SDMMC_INT_RESP_TIMEOUT | \
+        SDMMC_INT_FIFO_RUN_ERR | SDMMC_INT_HW_LOCKED | \
+        SDMMC_INT_START_BIT_ERR  | SDMMC_INT_END_BIT_ERR)
+
+#define SDMMC_STATUS_DMAREQ            (1 << 31)
+#define SDMMC_STATUS_DATA_FSM_BUSY     (1 << 10)
+#define SDMMC_STATUS_CARD_DATA_BUSY    (1 << 9)
+#define SDMMC_STATUS_CARD_PRESENT      (1 << 8)
+#define SDMMC_STATUS_FIFO_FULL         (1 << 3)
+#define SDMMC_STATUS_FIFO_EMPTY                (1 << 2)
+#define SDMMC_STATUS_TXWL_FLAG         (1 << 1)
+#define SDMMC_STATUS_RXWL_FLAG         (1 << 0)
+
+#define SDMMC_FUNCSEL_CEATA_DEV_INTEN   (1 << 10)
+#define SDMMC_FUNCSEL_SEND_AUTO_STOP_CCSD (1 << 9)
+#define SDMMC_FUNCSEL_SEND_CCSD                (1 << 8)
+#define SDMMC_FUNCSEL_ABT_RD_DATA      (1 << 2)
+#define SDMMC_FUNCSEL_SDIO_RD_WAIT     (1 << 1)
+#define SDMMC_FUNCSEL_SEND_IRQ_RSP     (1 << 0)
+
+#define SDMMC_DMAC_REFETCH_DES         (1 << 31)
+#define SDMMC_DMAC_IDMA_ON             (1 << 7)
+#define SDMMC_DMAC_FIX_BURST           (1 << 1)
+#define SDMMC_DMAC_SOFTRESET           (1 << 0)
+
+#define SDMMC_IDST_HOST_ABT            (1 << 10)
+#define SDMMC_IDST_ABNORMAL_INT_SUM    (1 << 9)
+#define SDMMC_IDST_NORMAL_INT_SUM      (1 << 8)
+#define SDMMC_IDST_CARD_ERR_SUM                (1 << 5)
+#define SDMMC_IDST_DES_INVALID         (1 << 4)
+#define SDMMC_IDST_FATAL_BUS_ERR       (1 << 2)
+#define SDMMC_IDST_RECEIVE_INT         (1 << 1)
+#define SDMMC_IDST_TRANSMIT_INT                (1 << 0)
+#define SDMMC_IDST_ERROR               \
+           (SDMMC_IDST_ABNORMAL_INT_SUM | \
+           SDMMC_IDST_CARD_ERR_SUM | \
+           SDMMC_IDST_DES_INVALID | \
+           SDMMC_IDST_FATAL_BUS_ERR)
+#define SDMMC_IDST_COMPLETE            \
+           (SDMMC_IDST_RECEIVE_INT | \
+           SDMMC_IDST_TRANSMIT_INT)
+
+struct sxisdmmc_idma_descriptor {
+       u_int   dma_config;
+#define SDMMC_IDMA_CONFIG_DIC          (1 << 1)
+#define SDMMC_IDMA_CONFIG_LD           (1 << 2)
+#define SDMMC_IDMA_CONFIG_FD           (1 << 3)
+#define SDMMC_IDMA_CONFIG_CH           (1 << 4)
+#define SDMMC_IDMA_CONFIG_ER           (1 << 5)
+#define SDMMC_IDMA_CONFIG_CES          (1 << 30)
+#define SDMMC_IDMA_CONFIG_OWN          (1 << 31)
+       u_int   dma_buf_size;
+       u_int   dma_buf_addr;
+       u_int   dma_next;
+};
+
+#define SDMMC_NDESC            16
+#define SDMMC_DMA_FTRGLEVEL_A20        0x20070008
+
+void   sxisdmmc_attach(struct device *, struct device *, void *);
+
+int    sxisdmmc_host_reset(sdmmc_chipset_handle_t);
+uint32_t       sxisdmmc_host_ocr(sdmmc_chipset_handle_t);
+int    sxisdmmc_host_maxblklen(sdmmc_chipset_handle_t);
+int    sxisdmmc_card_detect(sdmmc_chipset_handle_t);
+int    sxisdmmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
+int    sxisdmmc_bus_clock(sdmmc_chipset_handle_t, int, int);
+int    sxisdmmc_bus_width(sdmmc_chipset_handle_t, int);
+void   sxisdmmc_exec_command(sdmmc_chipset_handle_t,
+                                     struct sdmmc_command *);
+void   sxisdmmc_card_intr_mask(sdmmc_chipset_handle_t, int);
+void   sxisdmmc_card_intr_ack(sdmmc_chipset_handle_t);
+
+struct sdmmc_chip_functions sxisdmmc_chip_functions = {
+       /* host controller reset */
+       sxisdmmc_host_reset,
+       /* host controller capabilities */
+       sxisdmmc_host_ocr,
+       sxisdmmc_host_maxblklen,
+       /* card detection */
+       sxisdmmc_card_detect,
+       /* bus power and clock frequency */
+       sxisdmmc_bus_power,
+       sxisdmmc_bus_clock,
+       sxisdmmc_bus_width,
+       /* command execution */
+       sxisdmmc_exec_command,
+       /* card interrupt */
+       sxisdmmc_card_intr_mask,
+       sxisdmmc_card_intr_ack,
+};
+
+struct sxisdmmc_softc {
+       struct device            sc_dev;
+       bus_space_tag_t          sc_iot;
+       bus_space_handle_t       sc_ioh;
+       u_int                    sc_port;
+       struct device           *sc_sdmmc_dev;
+};
+
+struct cfdriver sxisdmmc_cd = {
+       NULL, "sxisdmmc", DV_DULL
+};
+
+struct cfattach sxisdmmc_ca = {
+       sizeof(struct sxisdmmc_softc), NULL, sxisdmmc_attach
+};
+
+void
+sxisdmmc_attach(struct device *parent, struct device *self, void *args)
+{
+       struct sxisdmmc_softc           *sc = (struct sxisdmmc_softc *)self;
+       struct armv7_attach_args        *aa = args;
+       struct sdmmcbus_attach_args      saa;
+
+       sc->sc_iot = aa->aa_iot;
+       sc->sc_port = aa->aa_dev->unit;
+       if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
+           aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
+               panic("sxisdmmc_attach: bus_space_map failed!");
+
+       printf(": SD/MMC interface\n");
+
+       sxisdmmc_host_reset(sc);
+       sxisdmmc_bus_width(sc, 1);
+       sxiccmu_set_sdmmc_clock(sc->sc_port, 400);
+
+       memset(&saa, 0, sizeof(saa));
+       saa.saa_busname = "sdmmc";
+       saa.sct = &sxisdmmc_chip_functions;
+       saa.sch = sc;
+       saa.caps = SMC_CAPS_4BIT_MODE|
+                      SMC_CAPS_8BIT_MODE|
+                      SMC_CAPS_SD_HIGHSPEED|
+                      SMC_CAPS_MMC_HIGHSPEED|
+                      SMC_CAPS_AUTO_STOP;
+
+       sc->sc_sdmmc_dev = config_found(self, &saa, NULL);
+}
+
+int
+sxisdmmc_host_reset(sdmmc_chipset_handle_t sch)
+{
+       struct sxisdmmc_softc *sc = sch;
+       DPRINTF(CD_INIT, ("%s: host reset\n", DNAME(sc)));
+       SXISET4(sc, SDMMC_GCTRL, SDMMC_GCTRL_RESET);
+       delay(20); /* XXX ? */
+       return 0;
+}
+
+uint32_t
+sxisdmmc_host_ocr(sdmmc_chipset_handle_t sch)
+{
+       return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V;
+}
+
+int
+sxisdmmc_host_maxblklen(sdmmc_chipset_handle_t sch)
+{
+       return 4096;
+}
+
+int
+sxisdmmc_card_detect(sdmmc_chipset_handle_t sch)
+{
+       return 1;       /* XXX impl. gpio CD */
+}
+
+int
+sxisdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
+{
+       return 0;
+}
+
+int
+sxisdmmc_update_clock(struct sxisdmmc_softc *sc)
+{
+       u_int retry;
+
+       SXIWRITE4(sc, SDMMC_CMD,
+           SDMMC_CMD_START | SDMMC_CMD_UPCLK_ONLY | SDMMC_CMD_WAIT_PRE_OVER);
+       for (retry = 0x100000; retry > 0; retry--) {
+               if (!(SXIREAD4(sc, SDMMC_CMD) & SDMMC_CMD_START))
+                       break;
+               delay(10);
+       }
+       if (retry == 0) {
+               DPRINTF(CD_TIMO, ("%s: timeout updating clk\n", DNAME(sc)));
+               return ETIMEDOUT;
+       }
+
+       /* clear pending interrupts */
+       SXIWRITE4(sc, SDMMC_RINT, SXIREAD4(sc, SDMMC_RINT));
+
+       return 0;
+}
+
+int
+sxisdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
+{
+       struct sxisdmmc_softc *sc = sch;
+       u_int clkcr;
+
+       DPRINTF(CD_DBG, ("freq = %d\n", freq));
+
+       clkcr = SXIREAD4(sc, SDMMC_CLKCR);
+       if (clkcr & SDMMC_CLKCR_CARDCLKON) {
+               clkcr &= ~SDMMC_CLKCR_CARDCLKON;
+               SXIWRITE4(sc, SDMMC_CLKCR, clkcr);
+               if (sxisdmmc_update_clock(sc) != 0)
+                       return 1;
+       }
+
+       if (freq) {
+               clkcr &= ~0xffff;
+               SXIWRITE4(sc, SDMMC_CLKCR, clkcr);
+               if (sxisdmmc_update_clock(sc) != 0)
+                       return 1;
+
+               if (sxiccmu_set_sdmmc_clock(sc->sc_port, freq) != 0)
+                       return 1;
+
+               clkcr |= SDMMC_CLKCR_CARDCLKON;
+               SXIWRITE4(sc, SDMMC_CLKCR, clkcr);
+               if (sxisdmmc_update_clock(sc) != 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+int
+sxisdmmc_bus_width(sdmmc_chipset_handle_t sch, int width)
+{
+       struct sxisdmmc_softc *sc = sch;
+
+       DPRINTF(CD_DBG, ("%s: width %d\n", DNAME(sc), width));
+
+       if (width != 1 && width != 4 && width != 8)
+               return 1;
+
+       SXIWRITE4(sc, SDMMC_WIDTH, width >> 2);
+
+       return 0;
+}
+
+int
+sxisdmmc_xfer_wait(struct sxisdmmc_softc *sc, struct sdmmc_command *cmd)
+{
+       u_int wait2clear = cmd->c_flags & SCF_CMD_READ ?
+           SDMMC_STATUS_FIFO_EMPTY : SDMMC_STATUS_FIFO_FULL;
+       u_int retry;
+
+       for (retry = 0x100000; retry > 0; retry--) {
+               if (SXIREAD4(sc, SDMMC_STATUS) & wait2clear) {
+                       delay(10);
+                       continue;
+               }
+               return 0;
+       }
+
+       return ETIMEDOUT;
+}
+
+int
+sxisdmmc_xfer_data(struct sxisdmmc_softc *sc, struct sdmmc_command *cmd)
+{
+       u_int *datap = (u_int *)cmd->c_buf;
+       int i;
+
+       for (i = 0; i < (cmd->c_resid >> 2); i++) {
+               if (sxisdmmc_xfer_wait(sc, cmd))
+                       return ETIMEDOUT;
+
+               if (cmd->c_flags & SCF_CMD_READ)
+                       datap[i] = SXIREAD4(sc, SDMMC_FIFO);
+               else
+                       SXIWRITE4(sc, SDMMC_FIFO, datap[i]);
+       }
+
+       return 0;
+}
+
+void
+sxisdmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
+{
+       struct sxisdmmc_softc *sc = sch;
+       u_int cmdval = SDMMC_CMD_START;
+       u_int status;
+       int retry;
+
+       DPRINTF(CD_SPAM, ("%s: exec opcode %d flags 0x%x data %p datalen %d\n",
+           DNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_data,
+           cmd->c_datalen));
+
+       if (cmd->c_opcode == 0)
+               cmdval |= SDMMC_CMD_SEND_INIT_SEQ;
+       if (cmd->c_flags & SCF_RSP_PRESENT)
+               cmdval |= SDMMC_CMD_RSP_EXP;
+       if (cmd->c_flags & SCF_RSP_136)
+               cmdval |= SDMMC_CMD_LONG_RSP;
+       if (cmd->c_flags & SCF_RSP_CRC)
+               cmdval |= SDMMC_CMD_CHECK_RSP_CRC;
+
+       if (cmd->c_datalen > 0) {
+               u_int nblks;
+
+               cmdval |= SDMMC_CMD_DATA_EXP | SDMMC_CMD_WAIT_PRE_OVER;
+               if (!(cmd->c_flags & SCF_CMD_READ))
+                       cmdval |= SDMMC_CMD_WRITE;
+
+               nblks = cmd->c_datalen / cmd->c_blklen;
+               if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0)
+                       ++nblks;
+               if (nblks > 1)
+                       cmdval |= SDMMC_CMD_SEND_AUTO_STOP;
+
+               SXIWRITE4(sc, SDMMC_BLKSZ, cmd->c_blklen);
+               SXIWRITE4(sc, SDMMC_BYTECNT, nblks * cmd->c_blklen);
+       }
+       DPRINTF(CD_SPAM, ("%s: exec cmdval %#8x\n", DNAME(sc), cmdval));
+
+       SXIWRITE4(sc, SDMMC_ARG, cmd->c_arg);
+
+       if (cmd->c_datalen == 0)
+               SXIWRITE4(sc, SDMMC_CMD, cmdval | cmd->c_opcode);
+       else {
+               SXISET4(sc, SDMMC_GCTRL, SDMMC_GCTRL_ACC_BY_AHB);
+               SXIWRITE4(sc, SDMMC_CMD, cmdval | cmd->c_opcode);
+               cmd->c_resid = cmd->c_datalen;
+               cmd->c_buf = cmd->c_data;
+               cmd->c_error = sxisdmmc_xfer_data(sc, cmd);
+               if (cmd->c_error) {
+                       DPRINTF(CD_TIMO, ("%s: exec xfer data err %d\n",
+                           DNAME(sc), cmd->c_error));
+                       goto done;
+               }
+       }
+
+       retry = 0x100000;
+       while (--retry > 0) {
+               status = SXIREAD4(sc, SDMMC_RINT);
+               if (status & SDMMC_INT_ERROR) {
+                       retry = 0;
+                       break;
+               }
+               if (status & SDMMC_INT_CMD_DONE)
+                       break;
+               delay(10);
+       }
+       if (retry == 0) {
+               DPRINTF(CD_TIMO, ("%s: exec cmd timeout %#8x\n",
+                   DNAME(sc), status));
+               cmd->c_error = ETIMEDOUT;
+               goto done;
+       }
+       DPRINTF(CD_SPAM, ("%s: status %#8x\n", DNAME(sc), status));
+
+       if (cmd->c_datalen > 0) {
+               retry = 0x10000;
+               do {
+                       status = SXIREAD4(sc, SDMMC_RINT);
+                       if (status & SDMMC_INT_ERROR) {
+                               retry = 0;
+                               break;
+                       }
+                       if (cmd->c_blklen < cmd->c_datalen &&
+                           (status & SDMMC_INT_AUTO_CMD_DONE) != 0)
+                               break;
+                       else if (status & SDMMC_INT_DATA_OVER);
+                               break;
+                       delay(10);
+               } while (--retry > 0);
+               if (retry == 0) {
+                       DPRINTF(CD_TIMO, ("%s: exec timeout 2 %#8x\n",
+                           DNAME(sc), status));
+                       cmd->c_error = ETIMEDOUT;
+                       goto done;
+               }
+       }
+
+       if (cmd->c_flags & SCF_RSP_BSY) {
+               retry = 0x100000;
+               while (--retry > 0) {
+                       status = SXIREAD4(sc, SDMMC_STATUS);
+                       if (status & SDMMC_STATUS_CARD_DATA_BUSY)
+                               break;
+               }
+               if (retry == 0) {
+                       DPRINTF(CD_TIMO, ("%s: exec busy timeout %#8x\n",
+                           DNAME(sc), status));
+                       cmd->c_error = ETIMEDOUT;
+                       goto done;
+               }
+       }
+
+       if (cmd->c_flags & SCF_RSP_PRESENT) {
+               if (cmd->c_flags & SCF_RSP_136) {
+                       cmd->c_resp[0] = SXIREAD4(sc, SDMMC_RESP0);
+                       cmd->c_resp[1] = SXIREAD4(sc, SDMMC_RESP1);
+                       cmd->c_resp[2] = SXIREAD4(sc, SDMMC_RESP2);
+                       cmd->c_resp[3] = SXIREAD4(sc, SDMMC_RESP3);
+                       if (cmd->c_flags & SCF_RSP_CRC) {
+                               cmd->c_resp[0] = (cmd->c_resp[0] >> 8) |
+                                   (cmd->c_resp[1] << 24);
+                               cmd->c_resp[1] = (cmd->c_resp[1] >> 8) |
+                                   (cmd->c_resp[2] << 24);
+                               cmd->c_resp[2] = (cmd->c_resp[2] >> 8) |
+                                   (cmd->c_resp[3] << 24);
+                               cmd->c_resp[3] = (cmd->c_resp[3] >> 8);
+                       }
+               } else
+                       cmd->c_resp[0] = SXIREAD4(sc, SDMMC_RESP0);
+       }
+
+done:
+       cmd->c_flags |= SCF_ITSDONE;
+
+       if (cmd->c_error) {
+               DPRINTF(CD_ERR, ("%s: exec i/o err %d\n", DNAME(sc),
+                   cmd->c_error));
+               SXIWRITE4(sc, SDMMC_GCTRL, SDMMC_GCTRL_RESET);
+               sxisdmmc_update_clock(sc);
+       } /* XXX else { ?? or return; from above block */
+               SXIWRITE4(sc, SDMMC_RINT, 0xffffffff);
+               SXISET4(sc, SDMMC_GCTRL, SDMMC_GCTRL_FIFORESET);
+/* XXX } */
+}
+
+void
+sxisdmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
+{
+}
+
+void
+sxisdmmc_card_intr_ack(sdmmc_chipset_handle_t sch)
+{
+}
diff --git a/sys/arch/armv7/sunxi/sxitwi.c b/sys/arch/armv7/sunxi/sxitwi.c
new file mode 100644
index 0000000..cbef524
--- /dev/null
+++ b/sys/arch/armv7/sunxi/sxitwi.c
@@ -0,0 +1,153 @@
+/*     $OpenBSD$       */
+/* $NetBSD: awin_twi.c,v 1.7 2015/12/26 16:54:41 macallan Exp $ */
+/*
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas of 3am Software Foundry.
+ *
+ * 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 THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION 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/device.h>
+#include <sys/systm.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <machine/bus.h>
+
+#include <armv7/sunxi/gttwsivar.h>
+#include <armv7/armv7/armv7var.h>
+#include <armv7/sunxi/sunxireg.h>
+#include <armv7/sunxi/sxiccmuvar.h>
+#include <armv7/sunxi/sxipiovar.h>
+
+#define TWI_CCR_REG            0x14
+#define TWI_CCR_CLK_M          (0x0f << 3)
+#define TWI_CCR_CLK_N          (0x07 << 0)
+
+void   sxitwi_attach(struct device *, struct device *, void *);
+void   sxitwi_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
+
+struct sxitwi_softc {
+       struct gttwsi_softc      sc_sc;
+       void                    *sc_ih;
+};
+
+struct cfdriver sxitwi_cd = {
+       NULL, "sxitwi", DV_DULL
+};
+
+struct cfattach sxitwi_ca = {
+       sizeof(struct sxitwi_softc), NULL, sxitwi_attach
+};
+
+void
+sxitwi_attach(struct device *parent, struct device *self, void *args)
+{
+       struct sxitwi_softc     *sc = (struct sxitwi_softc *)self;
+       struct armv7_attach_args *aa = args;
+       bus_space_tag_t         iot = aa->aa_iot;
+       bus_space_handle_t      ioh;
+
+       if (bus_space_map(iot, aa->aa_dev->mem[0].addr,
+           aa->aa_dev->mem[0].size, 0, &ioh))
+               panic("sxitwi_attach: bus_space_map failed!");
+
+       /*
+        * Acquire the PIO pins needed for the TWI port, and
+        * enable clock gating via CCMU
+        */
+       switch (aa->aa_dev->unit) {
+       case 0:                                 /* PORTB.pin = 32+pin# */
+               sxipio_setcfg(32+0, 2);         /* PB0 TWI0_SCK */
+               sxipio_setcfg(32+1, 2);         /* PB1 TWI0_SDA */
+               sxiccmu_enablemodule(CCMU_TWI0);
+               break;
+       case 1:
+               sxipio_setcfg(32+18, 2);        /* PB18 TWI1_SCK */
+               sxipio_setcfg(32+19, 2);        /* PB19 TWI1_SDA */
+               sxiccmu_enablemodule(CCMU_TWI1);
+               break;
+       case 2:
+               sxipio_setcfg(32+20, 2);        /* PB20 TWI2_SCK */
+               sxipio_setcfg(32+21, 2);        /* PB21 TWI2_SDA */
+               sxiccmu_enablemodule(CCMU_TWI2);
+               break;
+       default:
+               panic("%s: unit?!?", __func__);
+       }
+
+       /*
+        * Set clock rate to 100kHz. From the datasheet:
+        *   For 100Khz standard speed 2Wire, CLK_N=2, CLK_M=11
+        *   F0=48M/2^2=12Mhz, F1=F0/(10*(11+1)) = 0.1Mhz
+        */
+       bus_space_write_4(iot, ioh, TWI_CCR_REG, (11 << 3) | (2 << 0));
+
+       /*
+        * Do the MI attach
+        */
+       gttwsi_attach_subr(self, iot, ioh);
+
+       /*
+        * Establish interrupt for it
+        */
+       sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_BIO,
+           gttwsi_intr, &sc->sc_sc, sc->sc_sc.sc_dev.dv_xname);
+       if (sc->sc_ih == NULL) {
+               printf("failed to establish interrupt %d\n",
+                   aa->aa_dev->irq[0]);
+                return;
+        }
+
+       /*
+        * Configure its children
+        */
+
+       struct i2cbus_attach_args iba;
+
+       memset(&iba, 0, sizeof(iba));
+       iba.iba_name = "iic";
+       iba.iba_tag = &sc->sc_sc.sc_ic;
+       iba.iba_bus_scan = sxitwi_bus_scan;
+       iba.iba_bus_scan_arg = sc;
+
+       (void)config_found(&sc->sc_sc.sc_dev, &iba, iicbus_print);
+
+}
+
+void
+sxitwi_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
+{
+       struct i2c_attach_args   ia;
+
+       /* XXX get ptr to platform/board array of i2c devices to scan for? */
+       memset(&ia, 0, sizeof(ia));
+       ia.ia_tag = iba->iba_tag;
+       ia.ia_addr = 0x34;
+       ia.ia_size = 1;
+       ia.ia_name = "axppmic";
+       config_found(self, &ia, iicbus_print);
+}

Reply via email to