diff --git a/sys/arm/allwinner/a10_clk.c b/sys/arm/allwinner/a10_clk.c index d49e6d02dd56..177e5a3d4fda 100644 --- a/sys/arm/allwinner/a10_clk.c +++ b/sys/arm/allwinner/a10_clk.c @@ -400,6 +400,29 @@ a10_clk_mmc_cfg(int devid, int freq) return (0); } +int +a10_clk_i2c_activate(int devid) +{ + struct a10_ccm_softc *sc; + uint32_t reg_value; + + sc = a10_ccm_sc; + if (sc == NULL) + return (ENXIO); + + a10_clk_pll6_enable(); + + /* Gating APB clock for I2C/TWI */ + reg_value = ccm_read_4(sc, CCM_APB1_GATING); + if (devid == 4) + reg_value |= CCM_APB1_GATING_TWI << 15; + else + reg_value |= CCM_APB1_GATING_TWI << devid; + ccm_write_4(sc, CCM_APB1_GATING, reg_value); + + return (0); +} + int a10_clk_dmac_activate(void) { diff --git a/sys/arm/allwinner/a10_clk.h b/sys/arm/allwinner/a10_clk.h index 4df87cc21bfe..5cc8863421b3 100644 --- a/sys/arm/allwinner/a10_clk.h +++ b/sys/arm/allwinner/a10_clk.h @@ -121,6 +121,9 @@ /* AHB_GATING_REG1 */ #define CCM_AHB_GATING_GMAC (1 << 17) +/* APB1_GATING_REG */ +#define CCM_APB1_GATING_TWI (1 << 0) + #define CCM_USB_PHY (1 << 8) #define CCM_USB0_RESET (1 << 0) #define CCM_USB1_RESET (1 << 1) @@ -166,6 +169,7 @@ int a10_clk_gmac_activate(phandle_t); int a10_clk_ahci_activate(void); int a10_clk_mmc_activate(int); int a10_clk_mmc_cfg(int, int); +int a10_clk_i2c_activate(int); int a10_clk_dmac_activate(void); int a10_clk_codec_activate(unsigned int); diff --git a/sys/arm/allwinner/files.allwinner b/sys/arm/allwinner/files.allwinner index 5157850ada89..2db0da5dc424 100644 --- a/sys/arm/allwinner/files.allwinner +++ b/sys/arm/allwinner/files.allwinner @@ -12,4 +12,5 @@ arm/allwinner/a10_wdog.c standard arm/allwinner/a20/a20_cpu_cfg.c standard arm/allwinner/allwinner_machdep.c standard arm/allwinner/if_emac.c optional emac +dev/iicbus/twsi/a10_twsi.c optional twsi #arm/allwinner/console.c standard diff --git a/sys/arm/conf/A10 b/sys/arm/conf/A10 index 31073c723e11..577fa602e46d 100644 --- a/sys/arm/conf/A10 +++ b/sys/arm/conf/A10 @@ -66,8 +66,9 @@ device md device random # Entropy device # I2C support -#device iicbus -#device iic +device iicbus +device iic +device twsi # GPIO device gpio diff --git a/sys/arm/conf/A20 b/sys/arm/conf/A20 index cd0726454194..ded074a5240c 100644 --- a/sys/arm/conf/A20 +++ b/sys/arm/conf/A20 @@ -75,8 +75,9 @@ device md device random # Entropy device # I2C support -#device iicbus -#device iic +device iicbus +device iic +device twsi # GPIO device gpio diff --git a/sys/arm/mv/files.mv b/sys/arm/mv/files.mv index e605aab06f0e..29edffbb13f0 100644 --- a/sys/arm/mv/files.mv +++ b/sys/arm/mv/files.mv @@ -21,6 +21,7 @@ arm/mv/mv_ts.c standard arm/mv/timer.c standard dev/cesa/cesa.c optional cesa +dev/iicbus/twsi/mv_twsi.c optional twsi dev/mge/if_mge.c optional mge dev/nand/nfc_mv.c optional nand dev/mvs/mvs_soc.c optional mvs diff --git a/sys/dev/iicbus/twsi/a10_twsi.c b/sys/dev/iicbus/twsi/a10_twsi.c new file mode 100644 index 000000000000..f07973e057f1 --- /dev/null +++ b/sys/dev/iicbus/twsi/a10_twsi.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "iicbus_if.h" + +#define TWI_ADDR 0x0 +#define TWI_XADDR 0x4 +#define TWI_DATA 0x8 +#define TWI_CNTR 0xC +#define TWI_STAT 0x10 +#define TWI_CCR 0x14 +#define TWI_SRST 0x18 +#define TWI_EFR 0x1C +#define TWI_LCR 0x20 + +static int +a10_twsi_probe(device_t dev) +{ + struct twsi_softc *sc; + + sc = device_get_softc(dev); + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-i2c")) + return (ENXIO); + + device_set_desc(dev, "Allwinner Integrated I2C Bus Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +a10_twsi_attach(device_t dev) +{ + struct twsi_softc *sc; + + sc = device_get_softc(dev); + + /* Activate clock */ + a10_clk_i2c_activate(device_get_unit(dev)); + + sc->reg_data = TWI_DATA; + sc->reg_slave_addr = TWI_ADDR; + sc->reg_slave_ext_addr = TWI_XADDR; + sc->reg_control = TWI_CNTR; + sc->reg_status = TWI_STAT; + sc->reg_baud_rate = TWI_CCR; + sc->reg_soft_reset = TWI_SRST; + + /* Setup baud rate params */ + sc->baud_rate[IIC_SLOW].param = TWSI_BAUD_RATE_PARAM(11, 2); + sc->baud_rate[IIC_FAST].param = TWSI_BAUD_RATE_PARAM(11, 2); + sc->baud_rate[IIC_FASTEST].param = TWSI_BAUD_RATE_PARAM(2, 2); + + return (twsi_attach(dev)); +} + +static phandle_t +a10_twsi_get_node(device_t bus, device_t dev) +{ + return (ofw_bus_get_node(bus)); +} + +static device_method_t a10_twsi_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, a10_twsi_probe), + DEVMETHOD(device_attach, a10_twsi_attach), + + /* OFW methods */ + DEVMETHOD(ofw_bus_get_node, a10_twsi_get_node), + + { 0, 0 } +}; + +DEFINE_CLASS_1(iichb, a10_twsi_driver, a10_twsi_methods, + sizeof(struct twsi_softc), twsi_driver); + +static devclass_t a10_twsi_devclass; + +DRIVER_MODULE(a10_twsi, simplebus, a10_twsi_driver, a10_twsi_devclass, 0, 0); +DRIVER_MODULE(iicbus, a10_twsi, iicbus_driver, iicbus_devclass, 0, 0); +MODULE_DEPEND(a10_twsi, iicbus, 1, 1, 1); diff --git a/sys/dev/iicbus/twsi/mv_twsi.c b/sys/dev/iicbus/twsi/mv_twsi.c new file mode 100644 index 000000000000..db27366892eb --- /dev/null +++ b/sys/dev/iicbus/twsi/mv_twsi.c @@ -0,0 +1,252 @@ +/*- + * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Developed by Semihalf. + * + * 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. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell + * and Allwinner SoCs. Supports master operation only, and works in polling mode. + * + * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software + * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iicbus_if.h" + +#define MV_TWSI_NAME "twsi" +#define IICBUS_DEVNAME "iicbus" + +#define TWSI_ADDR 0x00 +#define TWSI_DATA 0x04 +#define TWSI_CNTR 0x08 +#define TWSI_XADDR 0x10 +#define TWSI_STAT 0x0c +#define TWSI_BAUD_RATE 0x0c +#define TWSI_SRST 0x1c + +#define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1))) +#define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */ +#define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */ + +#define TWSI_DEBUG +#undef TWSI_DEBUG + +#ifdef TWSI_DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +static int mv_twsi_probe(device_t); +static int mv_twsi_attach(device_t); + +static struct ofw_compat_data compat_data[] = { + { "mrvl,twsi", true }, + { "marvell,mv64xxx-i2c", true }, + { NULL, false } +}; + +static device_method_t mv_twsi_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, mv_twsi_probe), + DEVMETHOD(device_attach, mv_twsi_attach), + + { 0, 0 } +}; + +DEFINE_CLASS_1(twsi, mv_twsi_driver, mv_twsi_methods, + sizeof(struct twsi_softc), twsi_driver); + +static devclass_t mv_twsi_devclass; + +DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0); +DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); +MODULE_DEPEND(twsi, iicbus, 1, 1, 1); + +static int +mv_twsi_probe(device_t dev) +{ + struct twsi_softc *sc; + + sc = device_get_softc(dev); + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + sc->reg_data = TWSI_DATA; + sc->reg_slave_addr = TWSI_ADDR; + sc->reg_slave_ext_addr = TWSI_XADDR; + sc->reg_control = TWSI_CNTR; + sc->reg_status = TWSI_STAT; + sc->reg_baud_rate = TWSI_BAUD_RATE; + sc->reg_soft_reset = TWSI_SRST; + + device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); + return (BUS_PROBE_DEFAULT); +} + +#define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) +static void +mv_twsi_cal_baud_rate(const uint32_t target, struct twsi_baud_rate *rate) +{ + uint32_t clk, cur, diff, diff0; + int m, n, m0, n0; + + /* Calculate baud rate. */ + m0 = n0 = 4; /* Default values on reset */ + diff0 = 0xffffffff; + clk = get_tclk(); + + for (n = 0; n < 8; n++) { + for (m = 0; m < 16; m++) { + cur = TWSI_BAUD_RATE_RAW(clk,m,n); + diff = ABSSUB(target, cur); + if (diff < diff0) { + m0 = m; + n0 = n; + diff0 = diff; + } + } + } + rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0); + rate->param = TWSI_BAUD_RATE_PARAM(m0, n0); + rate->m = m0; + rate->n = n0; +} + +static int +mv_twsi_attach(device_t dev) +{ + struct twsi_softc *sc; + phandle_t child, iicbusnode; + device_t childdev; + struct iicbus_ivar *devi; + char dname[32]; /* 32 is taken from struct u_device */ + uint32_t paddr; + int len, error, ret; + + sc = device_get_softc(dev); + + mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]); + mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]); + if (bootverbose) + device_printf(dev, "calculated baud rates are:\n" + " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n" + " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n", + sc->baud_rate[IIC_SLOW].raw / 1000, + sc->baud_rate[IIC_SLOW].m, + sc->baud_rate[IIC_SLOW].n, + sc->baud_rate[IIC_FAST].raw / 1000, + sc->baud_rate[IIC_FAST].m, + sc->baud_rate[IIC_FAST].n); + + + ret = twsi_attach(dev); + if (ret != 0) + return (ret); + + iicbusnode = 0; + /* Find iicbus as the child devices in the device tree. */ + for (child = OF_child(ofw_bus_get_node(dev)); child != 0; + child = OF_peer(child)) { + len = OF_getproplen(child, "model"); + if (len <= 0 || len > sizeof(dname)) + continue; + error = OF_getprop(child, "model", &dname, len); + if (error == -1) + continue; + len = strlen(dname); + if (len == strlen(IICBUS_DEVNAME) && + strncasecmp(dname, IICBUS_DEVNAME, len) == 0) { + iicbusnode = child; + break; + } + } + if (iicbusnode == 0) + goto attach_end; + + /* Attach child devices onto iicbus. */ + for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) { + /* Get slave address. */ + error = OF_getprop(child, "i2c-address", &paddr, sizeof(paddr)); + if (error == -1) + error = OF_getprop(child, "reg", &paddr, sizeof(paddr)); + if (error == -1) + continue; + + /* Get device driver name. */ + len = OF_getproplen(child, "model"); + if (len <= 0 || len > sizeof(dname)) + continue; + OF_getprop(child, "model", &dname, len); + + if (bootverbose) + device_printf(dev, "adding a device %s at %d.\n", + dname, fdt32_to_cpu(paddr)); + childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1); + devi = IICBUS_IVAR(childdev); + devi->addr = fdt32_to_cpu(paddr); + } + +attach_end: + bus_generic_attach(sc->iicbus); + + return (0); +} diff --git a/sys/dev/iicbus/twsi/twsi.c b/sys/dev/iicbus/twsi/twsi.c index e0290a86121c..c02cb8153bb1 100644 --- a/sys/dev/iicbus/twsi/twsi.c +++ b/sys/dev/iicbus/twsi/twsi.c @@ -31,7 +31,7 @@ /* * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell - * SoCs. Supports master operation only, and works in polling mode. + * and Allwinner SoCs. Supports master operation only, and works in polling mode. * * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". @@ -62,156 +62,75 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include "iicbus_if.h" -#define MV_TWSI_NAME "twsi" -#define IICBUS_DEVNAME "iicbus" +#define TWSI_CONTROL_ACK (1 << 2) +#define TWSI_CONTROL_IFLG (1 << 3) +#define TWSI_CONTROL_STOP (1 << 4) +#define TWSI_CONTROL_START (1 << 5) +#define TWSI_CONTROL_TWSIEN (1 << 6) +#define TWSI_CONTROL_INTEN (1 << 7) -#define TWSI_SLAVE_ADDR 0x00 -#define TWSI_EXT_SLAVE_ADDR 0x10 -#define TWSI_DATA 0x04 +#define TWSI_STATUS_START 0x08 +#define TWSI_STATUS_RPTD_START 0x10 +#define TWSI_STATUS_ADDR_W_ACK 0x18 +#define TWSI_STATUS_DATA_WR_ACK 0x28 +#define TWSI_STATUS_ADDR_R_ACK 0x40 +#define TWSI_STATUS_DATA_RD_ACK 0x50 +#define TWSI_STATUS_DATA_RD_NOACK 0x58 -#define TWSI_CONTROL 0x08 -#define TWSI_CONTROL_ACK (1 << 2) -#define TWSI_CONTROL_IFLG (1 << 3) -#define TWSI_CONTROL_STOP (1 << 4) -#define TWSI_CONTROL_START (1 << 5) -#define TWSI_CONTROL_TWSIEN (1 << 6) -#define TWSI_CONTROL_INTEN (1 << 7) - -#define TWSI_STATUS 0x0c -#define TWSI_STATUS_START 0x08 -#define TWSI_STATUS_RPTD_START 0x10 -#define TWSI_STATUS_ADDR_W_ACK 0x18 -#define TWSI_STATUS_DATA_WR_ACK 0x28 -#define TWSI_STATUS_ADDR_R_ACK 0x40 -#define TWSI_STATUS_DATA_RD_ACK 0x50 -#define TWSI_STATUS_DATA_RD_NOACK 0x58 - -#define TWSI_BAUD_RATE 0x0c -#define TWSI_BAUD_RATE_PARAM(M,N) ((((M) << 3) | ((N) & 0x7)) & 0x7f) -#define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1))) -#define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */ -#define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */ - -#define TWSI_SOFT_RESET 0x1c - -#define TWSI_DEBUG +#define TWSI_DEBUG #undef TWSI_DEBUG -#ifdef TWSI_DEBUG -#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) +#ifdef TWSI_DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) #else -#define debugf(fmt, args...) +#define debugf(fmt, args...) #endif -struct mv_twsi_softc { - device_t dev; - struct resource *res[1]; /* SYS_RES_MEMORY */ - struct mtx mutex; - device_t iicbus; -}; - -static struct mv_twsi_baud_rate { - uint32_t raw; - int param; - int m; - int n; -} baud_rate[IIC_FASTEST + 1]; - -static int mv_twsi_probe(device_t); -static int mv_twsi_attach(device_t); -static int mv_twsi_detach(device_t); - -static int mv_twsi_reset(device_t dev, u_char speed, u_char addr, - u_char *oldaddr); -static int mv_twsi_repeated_start(device_t dev, u_char slave, int timeout); -static int mv_twsi_start(device_t dev, u_char slave, int timeout); -static int mv_twsi_stop(device_t dev); -static int mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, - int delay); -static int mv_twsi_write(device_t dev, const char *buf, int len, int *sent, - int timeout); - static struct resource_spec res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; -static struct ofw_compat_data compat_data[] = { - { "mrvl,twsi", true }, - { "marvell,mv64xxx-i2c", true }, - { NULL, false } -}; - -static device_method_t mv_twsi_methods[] = { - /* device interface */ - DEVMETHOD(device_probe, mv_twsi_probe), - DEVMETHOD(device_attach, mv_twsi_attach), - DEVMETHOD(device_detach, mv_twsi_detach), - - /* iicbus interface */ - DEVMETHOD(iicbus_callback, iicbus_null_callback), - DEVMETHOD(iicbus_repeated_start, mv_twsi_repeated_start), - DEVMETHOD(iicbus_start, mv_twsi_start), - DEVMETHOD(iicbus_stop, mv_twsi_stop), - DEVMETHOD(iicbus_write, mv_twsi_write), - DEVMETHOD(iicbus_read, mv_twsi_read), - DEVMETHOD(iicbus_reset, mv_twsi_reset), - DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), - { 0, 0 } -}; - -static devclass_t mv_twsi_devclass; - -static driver_t mv_twsi_driver = { - MV_TWSI_NAME, - mv_twsi_methods, - sizeof(struct mv_twsi_softc), -}; - -DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0); -DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); -MODULE_DEPEND(twsi, iicbus, 1, 1, 1); - static __inline uint32_t -TWSI_READ(struct mv_twsi_softc *sc, bus_size_t off) +TWSI_READ(struct twsi_softc *sc, bus_size_t off) { return (bus_read_4(sc->res[0], off)); } static __inline void -TWSI_WRITE(struct mv_twsi_softc *sc, bus_size_t off, uint32_t val) +TWSI_WRITE(struct twsi_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->res[0], off, val); } static __inline void -twsi_control_clear(struct mv_twsi_softc *sc, uint32_t mask) +twsi_control_clear(struct twsi_softc *sc, uint32_t mask) { uint32_t val; - val = TWSI_READ(sc, TWSI_CONTROL); + val = TWSI_READ(sc, sc->reg_control); val &= ~mask; - TWSI_WRITE(sc, TWSI_CONTROL, val); + TWSI_WRITE(sc, sc->reg_control, val); } static __inline void -twsi_control_set(struct mv_twsi_softc *sc, uint32_t mask) +twsi_control_set(struct twsi_softc *sc, uint32_t mask) { uint32_t val; - val = TWSI_READ(sc, TWSI_CONTROL); + val = TWSI_READ(sc, sc->reg_control); val |= mask; - TWSI_WRITE(sc, TWSI_CONTROL, val); + TWSI_WRITE(sc, sc->reg_control, val); } static __inline void -twsi_clear_iflg(struct mv_twsi_softc *sc) +twsi_clear_iflg(struct twsi_softc *sc) { DELAY(1000); @@ -227,11 +146,11 @@ twsi_clear_iflg(struct mv_twsi_softc *sc) * non-zero on timeout */ static int -twsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask) +twsi_poll_ctrl(struct twsi_softc *sc, int timeout, uint32_t mask) { timeout /= 10; - while (!(TWSI_READ(sc, TWSI_CONTROL) & mask)) { + while (!(TWSI_READ(sc, sc->reg_control) & mask)) { DELAY(10); if (--timeout < 0) return (timeout); @@ -247,7 +166,7 @@ twsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask) * or TWSI_STATUS_RPTD_START */ static int -twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, +twsi_locked_start(device_t dev, struct twsi_softc *sc, int32_t mask, u_char slave, int timeout) { int read_access, iflg_set = 0; @@ -257,7 +176,7 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, if (mask == TWSI_STATUS_RPTD_START) /* read IFLG to know if it should be cleared later; from NBSD */ - iflg_set = TWSI_READ(sc, TWSI_CONTROL) & TWSI_CONTROL_IFLG; + iflg_set = TWSI_READ(sc, sc->reg_control) & TWSI_CONTROL_IFLG; twsi_control_set(sc, TWSI_CONTROL_START); @@ -278,14 +197,14 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, return (IIC_ETIMEOUT); } - status = TWSI_READ(sc, TWSI_STATUS); + status = TWSI_READ(sc, sc->reg_status); if (status != mask) { debugf("wrong status (%02x) after sending %sSTART condition\n", status, mask == TWSI_STATUS_START ? "" : "repeated "); return (IIC_ESTATUS); } - TWSI_WRITE(sc, TWSI_DATA, slave); + TWSI_WRITE(sc, sc->reg_data, slave); DELAY(1000); twsi_clear_iflg(sc); @@ -295,7 +214,7 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, } read_access = (slave & 0x1) ? 1 : 0; - status = TWSI_READ(sc, TWSI_STATUS); + status = TWSI_READ(sc, sc->reg_status); if (status != (read_access ? TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) { debugf("no ACK (status: %02x) after sending slave address\n", @@ -306,172 +225,13 @@ twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, return (IIC_NOERR); } -static int -mv_twsi_probe(device_t dev) -{ - - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) - return (ENXIO); - - device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); - return (BUS_PROBE_DEFAULT); -} - -#define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) -static void -mv_twsi_cal_baud_rate(const uint32_t target, struct mv_twsi_baud_rate *rate) -{ - uint32_t clk, cur, diff, diff0; - int m, n, m0, n0; - - /* Calculate baud rate. */ - m0 = n0 = 4; /* Default values on reset */ - diff0 = 0xffffffff; - clk = get_tclk(); - - for (n = 0; n < 8; n++) { - for (m = 0; m < 16; m++) { - cur = TWSI_BAUD_RATE_RAW(clk,m,n); - diff = ABSSUB(target, cur); - if (diff < diff0) { - m0 = m; - n0 = n; - diff0 = diff; - } - } - } - rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0); - rate->param = TWSI_BAUD_RATE_PARAM(m0, n0); - rate->m = m0; - rate->n = n0; -} - -static int -mv_twsi_attach(device_t dev) -{ - struct mv_twsi_softc *sc; - phandle_t child, iicbusnode; - device_t childdev; - struct iicbus_ivar *devi; - char dname[32]; /* 32 is taken from struct u_device */ - uint32_t paddr; - int len, error; - - sc = device_get_softc(dev); - sc->dev = dev; - bzero(baud_rate, sizeof(baud_rate)); - - mtx_init(&sc->mutex, device_get_nameunit(dev), MV_TWSI_NAME, MTX_DEF); - - /* Allocate IO resources */ - if (bus_alloc_resources(dev, res_spec, sc->res)) { - device_printf(dev, "could not allocate resources\n"); - mv_twsi_detach(dev); - return (ENXIO); - } - - mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &baud_rate[IIC_SLOW]); - mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &baud_rate[IIC_FAST]); - if (bootverbose) - device_printf(dev, "calculated baud rates are:\n" - " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n" - " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n", - baud_rate[IIC_SLOW].raw / 1000, - baud_rate[IIC_SLOW].m, - baud_rate[IIC_SLOW].n, - baud_rate[IIC_FAST].raw / 1000, - baud_rate[IIC_FAST].m, - baud_rate[IIC_FAST].n); - - sc->iicbus = device_add_child(dev, IICBUS_DEVNAME, -1); - if (sc->iicbus == NULL) { - device_printf(dev, "could not add iicbus child\n"); - mv_twsi_detach(dev); - return (ENXIO); - } - /* Attach iicbus. */ - bus_generic_attach(dev); - - iicbusnode = 0; - /* Find iicbus as the child devices in the device tree. */ - for (child = OF_child(ofw_bus_get_node(dev)); child != 0; - child = OF_peer(child)) { - len = OF_getproplen(child, "model"); - if (len <= 0 || len > sizeof(dname)) - continue; - error = OF_getprop(child, "model", &dname, len); - if (error == -1) - continue; - len = strlen(dname); - if (len == strlen(IICBUS_DEVNAME) && - strncasecmp(dname, IICBUS_DEVNAME, len) == 0) { - iicbusnode = child; - break; - } - } - if (iicbusnode == 0) - goto attach_end; - - /* Attach child devices onto iicbus. */ - for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) { - /* Get slave address. */ - error = OF_getprop(child, "i2c-address", &paddr, sizeof(paddr)); - if (error == -1) - error = OF_getprop(child, "reg", &paddr, sizeof(paddr)); - if (error == -1) - continue; - - /* Get device driver name. */ - len = OF_getproplen(child, "model"); - if (len <= 0 || len > sizeof(dname)) - continue; - OF_getprop(child, "model", &dname, len); - - if (bootverbose) - device_printf(dev, "adding a device %s at %d.\n", - dname, fdt32_to_cpu(paddr)); - childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1); - devi = IICBUS_IVAR(childdev); - devi->addr = fdt32_to_cpu(paddr); - } - -attach_end: - bus_generic_attach(sc->iicbus); - - return (0); -} - -static int -mv_twsi_detach(device_t dev) -{ - struct mv_twsi_softc *sc; - int rv; - - sc = device_get_softc(dev); - - if ((rv = bus_generic_detach(dev)) != 0) - return (rv); - - if (sc->iicbus != NULL) - if ((rv = device_delete_child(dev, sc->iicbus)) != 0) - return (rv); - - bus_release_resources(dev, res_spec, sc->res); - - mtx_destroy(&sc->mutex); - return (0); -} - /* * Only slave mode supported, disregard [old]addr */ static int -mv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { - struct mv_twsi_softc *sc; + struct twsi_softc *sc; uint32_t param; sc = device_get_softc(dev); @@ -479,75 +239,30 @@ mv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) switch (speed) { case IIC_SLOW: case IIC_FAST: - param = baud_rate[speed].param; + param = sc->baud_rate[speed].param; break; case IIC_FASTEST: case IIC_UNKNOWN: default: - param = baud_rate[IIC_FAST].param; + param = sc->baud_rate[IIC_FAST].param; break; } mtx_lock(&sc->mutex); - TWSI_WRITE(sc, TWSI_SOFT_RESET, 0x0); + TWSI_WRITE(sc, sc->reg_soft_reset, 0x0); DELAY(2000); - TWSI_WRITE(sc, TWSI_BAUD_RATE, param); - TWSI_WRITE(sc, TWSI_CONTROL, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK); + TWSI_WRITE(sc, sc->reg_baud_rate, param); + TWSI_WRITE(sc, sc->reg_control, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK); DELAY(1000); mtx_unlock(&sc->mutex); return (0); } -/* - * timeout is given in us - */ static int -mv_twsi_repeated_start(device_t dev, u_char slave, int timeout) +twsi_stop(device_t dev) { - struct mv_twsi_softc *sc; - int rv; - - sc = device_get_softc(dev); - - mtx_lock(&sc->mutex); - rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave, - timeout); - mtx_unlock(&sc->mutex); - - if (rv) { - mv_twsi_stop(dev); - return (rv); - } else - return (IIC_NOERR); -} - -/* - * timeout is given in us - */ -static int -mv_twsi_start(device_t dev, u_char slave, int timeout) -{ - struct mv_twsi_softc *sc; - int rv; - - sc = device_get_softc(dev); - - mtx_lock(&sc->mutex); - rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout); - mtx_unlock(&sc->mutex); - - if (rv) { - mv_twsi_stop(dev); - return (rv); - } else - return (IIC_NOERR); -} - -static int -mv_twsi_stop(device_t dev) -{ - struct mv_twsi_softc *sc; + struct twsi_softc *sc; sc = device_get_softc(dev); @@ -560,10 +275,55 @@ mv_twsi_stop(device_t dev) return (IIC_NOERR); } +/* + * timeout is given in us + */ static int -mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) +twsi_repeated_start(device_t dev, u_char slave, int timeout) { - struct mv_twsi_softc *sc; + struct twsi_softc *sc; + int rv; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave, + timeout); + mtx_unlock(&sc->mutex); + + if (rv) { + twsi_stop(dev); + return (rv); + } else + return (IIC_NOERR); +} + +/* + * timeout is given in us + */ +static int +twsi_start(device_t dev, u_char slave, int timeout) +{ + struct twsi_softc *sc; + int rv; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout); + mtx_unlock(&sc->mutex); + + if (rv) { + twsi_stop(dev); + return (rv); + } else + return (IIC_NOERR); +} + +static int +twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) +{ + struct twsi_softc *sc; uint32_t status; int last_byte, rv; @@ -591,7 +351,7 @@ mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) goto out; } - status = TWSI_READ(sc, TWSI_STATUS); + status = TWSI_READ(sc, sc->reg_status); if (status != (last_byte ? TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) { debugf("wrong status (%02x) while reading\n", status); @@ -599,7 +359,7 @@ mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) goto out; } - *buf++ = TWSI_READ(sc, TWSI_DATA); + *buf++ = TWSI_READ(sc, sc->reg_data); (*read)++; } rv = IIC_NOERR; @@ -609,9 +369,9 @@ mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) } static int -mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) +twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) { - struct mv_twsi_softc *sc; + struct twsi_softc *sc; uint32_t status; int rv; @@ -620,7 +380,7 @@ mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) mtx_lock(&sc->mutex); *sent = 0; while (*sent < len) { - TWSI_WRITE(sc, TWSI_DATA, *buf++); + TWSI_WRITE(sc, sc->reg_data, *buf++); twsi_clear_iflg(sc); if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { @@ -629,7 +389,7 @@ mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) goto out; } - status = TWSI_READ(sc, TWSI_STATUS); + status = TWSI_READ(sc, sc->reg_status); if (status != TWSI_STATUS_DATA_WR_ACK) { debugf("wrong status (%02x) while writing\n", status); rv = IIC_ESTATUS; @@ -642,3 +402,81 @@ mv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) mtx_unlock(&sc->mutex); return (rv); } + +int +twsi_attach(device_t dev) +{ + struct twsi_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + mtx_init(&sc->mutex, device_get_nameunit(dev), "twsi", MTX_DEF); + + if (bus_alloc_resources(dev, res_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + twsi_detach(dev); + return (ENXIO); + } + + /* Attach the iicbus. */ + if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { + device_printf(dev, "could not allocate iicbus instance\n"); + twsi_detach(dev); + return (ENXIO); + } + bus_generic_attach(dev); + + return (0); +} + +int +twsi_detach(device_t dev) +{ + struct twsi_softc *sc; + int rv; + + sc = device_get_softc(dev); + + if ((rv = bus_generic_detach(dev)) != 0) + return (rv); + + if (sc->iicbus != NULL) + if ((rv = device_delete_child(dev, sc->iicbus)) != 0) + return (rv); + + bus_release_resources(dev, res_spec, sc->res); + + mtx_destroy(&sc->mutex); + return (0); +} + +static device_method_t twsi_methods[] = { + /* device interface */ + DEVMETHOD(device_detach, twsi_detach), + + /* Bus interface */ + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_repeated_start, twsi_repeated_start), + DEVMETHOD(iicbus_start, twsi_start), + DEVMETHOD(iicbus_stop, twsi_stop), + DEVMETHOD(iicbus_write, twsi_write), + DEVMETHOD(iicbus_read, twsi_read), + DEVMETHOD(iicbus_reset, twsi_reset), + DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), + { 0, 0 } +}; + +DEFINE_CLASS_0(twsi, twsi_driver, twsi_methods, + sizeof(struct twsi_softc)); diff --git a/sys/dev/iicbus/twsi/twsi.h b/sys/dev/iicbus/twsi/twsi.h new file mode 100644 index 000000000000..a95db825f2c8 --- /dev/null +++ b/sys/dev/iicbus/twsi/twsi.h @@ -0,0 +1,67 @@ +/*- + * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Developed by Semihalf. + * + * 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. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _TWSI_H_ +#define _TWSI_H_ + +struct twsi_baud_rate { + uint32_t raw; + int param; + int m; + int n; +}; + +struct twsi_softc { + device_t dev; + struct resource *res[1]; /* SYS_RES_MEMORY */ + struct mtx mutex; + device_t iicbus; + + bus_size_t reg_data; + bus_size_t reg_slave_addr; + bus_size_t reg_slave_ext_addr; + bus_size_t reg_control; + bus_size_t reg_status; + bus_size_t reg_baud_rate; + bus_size_t reg_soft_reset; + struct twsi_baud_rate baud_rate[IIC_FASTEST + 1]; +}; + +DECLARE_CLASS(twsi_driver); + +#define TWSI_BAUD_RATE_PARAM(M,N) ((((M) << 3) | ((N) & 0x7)) & 0x7f) + +int twsi_attach(device_t); +int twsi_detach(device_t); + +#endif /* _TWSI_H_ */