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); +}