Index: sys/boot/fdt/dts/mips/fbsd-rt2880.dtsi =================================================================== --- /dev/null +++ sys/boot/fdt/dts/mips/fbsd-rt2880.dtsi @@ -0,0 +1,226 @@ +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ralink,rt2880-soc"; + + cpus { + cpu@0 { + compatible = "mips,mips4KEc"; + }; + }; + + chosen { + bootargs = "console=ttyS0,57600"; + }; + + aliases { + serial0 = &uartlite; + }; + + cpuintc: cpuintc@0 { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "mti,cpu-interrupt-controller"; + }; + + palmbus: palmbus@300000 { + compatible = "palmbus"; + reg = <0x300000 0x200000>; + ranges = <0x0 0x300000 0x1FFFFF>; + + #address-cells = <1>; + #size-cells = <1>; + + sysc: sysc@0 { + compatible = "ralink,rt2880-sysc"; + reg = <0x000 0x100>; + }; + + timer: timer@100 { + compatible = "ralink,rt2880-timer"; + reg = <0x100 0x20>; + + interrupt-parent = <&intc>; + interrupts = <1>; + + status = "disabled"; + }; + + watchdog: watchdog@120 { + compatible = "ralink,rt2880-wdt"; + reg = <0x120 0x10>; + }; + + intc: intc@200 { + compatible = "ralink,rt2880-intc"; + reg = <0x200 0x100>; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>; + }; + + memc: memc@300 { + compatible = "ralink,rt2880-memc"; + reg = <0x300 0x100>; + }; + + gpio0: gpio@600 { + compatible = "ralink,rt2880-gpio"; + reg = <0x600 0x34>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,gpio-base = <0>; + ralink,num-gpios = <24>; + ralink,register-map = [ 00 04 08 0c + 20 24 28 2c + 30 34 ]; + + interrupt-parent = <&intc>; + interrupts = <7>; + }; + + gpio1: gpio@638 { + compatible = "ralink,rt2880-gpio"; + reg = <0x638 0x24>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,gpio-base = <24>; + ralink,num-gpios = <16>; + ralink,register-map = [ 00 04 08 0c + 10 14 18 1c + 20 24 ]; + + status = "disabled"; + }; + + gpio2: gpio@660 { + compatible = "ralink,rt2880-gpio"; + reg = <0x660 0x24>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,gpio-base = <40>; + ralink,num-gpios = <32>; + ralink,register-map = [ 00 04 08 0c + 10 14 18 1c + 20 24 ]; + + status = "disabled"; + }; + + i2c: i2c@900 { + compatible = "ralink,rt2880-i2c"; + reg = <0x900 0x100>; + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + + status = "disabled"; + }; + + + uartlite: uartlite@c00 { + compatible = "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; + + interrupt-parent = <&intc>; + interrupts = <8>; + + reg-shift = <2>; + }; + }; + + pinctrl: pinctrl { + compatible = "ralink,rt2880-pinmux"; + + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinctrl0 { + sdram { + ralink,group = "sdram"; + ralink,function = "sdram"; + }; + }; + + spi_pins: spi { + spi { + ralink,group = "spi"; + ralink,function = "spi"; + }; + }; + + i2c_pins: i2c { + i2c { + ralink,group = "i2c"; + ralink,function = "i2c"; + }; + }; + + uartlite_pins: uartlite { + uart { + ralink,group = "uartlite"; + ralink,function = "uartlite"; + }; + }; + }; + + rstctrl: rstctrl { + compatible = "ralink,rt2880-reset"; + #reset-cells = <1>; + }; + + clkctrl: clkctrl { + compatible = "ralink,rt2880-clock"; + #clock-cells = <1>; + }; + + ethernet: ethernet@400000 { + compatible = "ralink,rt2880-eth"; + reg = <0x00400000 0x10000>; + + #address-cells = <1>; + #size-cells = <0>; + + resets = <&rstctrl 18>; + reset-names = "fe"; + + interrupt-parent = <&cpuintc>; + interrupts = <5>; + + status = "disabled"; + + port@0 { + compatible = "ralink,rt2880-port", "mediatek,eth-port"; + reg = <0>; + }; + }; + + mdio-bus { + compatible = "ralink,rt2880-mdio"; + reg = <0x00400000 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + wmac: wmac@480000 { + compatible = "ralink,rt2880-wmac"; + reg = <0x480000 0x40000>; + + interrupt-parent = <&cpuintc>; + interrupts = <6>; + + ralink,eeprom = "soc_wmac.eeprom"; + }; +}; Index: sys/mips/mediatek/files.mediatek =================================================================== --- sys/mips/mediatek/files.mediatek +++ sys/mips/mediatek/files.mediatek @@ -22,6 +22,7 @@ mips/mediatek/mtk_gpio_v1.c optional gpio mtk_gpio_v1 mips/mediatek/mtk_gpio_v2.c optional gpio mtk_gpio_v2 #mips/mediatek/mtk_mmc.c optional mmc +mips/mediatek/mtk_iic.c optional iic mtk_iic # Ralink/Mediatek Ethernet driver dev/rt/if_rt.c optional rt Index: sys/mips/mediatek/mtk_iic.h =================================================================== --- /dev/null +++ sys/mips/mediatek/mtk_iic.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2017 Hiroki Mori + * 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 unmodified, 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 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 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 _MTK_I2C_NEWVAR_H_ +#define _MTK_I2C_NEWVAR_H_ + +/* Helpers from NetBSD */ +/* __BIT(n): nth bit, where __BIT(0) == 0x1. */ +#define __BIT(__n) \ + (((__n) >= NBBY * sizeof(uintmax_t)) ? 0 : ((uintmax_t)1 << (__n))) + +/* + * I2C controller interface + * Copy from netbsd code + */ + +#define RA_I2C_CONFIG 0x00 +#define RA_I2C_CLKDIV 0x04 +#define RA_I2C_DEVADDR 0x08 +#define RA_I2C_ADDR 0x0C +#define RA_I2C_DATAOUT 0x10 +#define RA_I2C_DATAIN 0x14 +#define RA_I2C_STATUS 0x18 +#define RA_I2C_STARTXFR 0x1C +#define RA_I2C_BYTECNT 0x20 +#define I2C_CONFIG_ADDRLEN(x) (((x) & 0x7) << 5) +#define I2C_CONFIG_ADDRLEN_7 6 +#define I2C_CONFIG_ADDRLEN_8 7 +#define I2C_CONFIG_DEVADLEN(x) (((x) & 0x7) << 2) +#define I2C_CONFIG_DEVADLEN_6 5 +#define I2C_CONFIG_DEVADLEN_7 6 +#define I2C_CONFIG_ADDRDIS __BIT(1) +#define I2C_CONFIG_DEVDIS __BIT(0) +#define I2C_STATUS_STARTERR __BIT(4) +#define I2C_STATUS_ACKERR __BIT(3) +#define I2C_STATUS_DATARDY __BIT(2) +#define I2C_STATUS_SDOEMPTY __BIT(1) +#define I2C_STATUS_BUSY __BIT(0) + +#define I2C_OP_READ 1 +#define I2C_OP_WRITE 0 + +#define CLKDIV_VALUE 333 + +#define i2c_busy_loop (sc->clkdiv*30) +#define max_ee_busy_loop (sc->clkdiv*25) + +#endif /* _MTK_I2C_NEWVAR_H_ */ Index: sys/mips/mediatek/mtk_iic.c =================================================================== --- /dev/null +++ sys/mips/mediatek/mtk_iic.c @@ -0,0 +1,419 @@ +/*- + * Copyright (c) 2017 Hiroki Mori + * 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. Neither the name of RMI Corporation, nor the names of its contributors, + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 THE 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 "opt_platform.h" + +/* + * This code do not test + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "iicbus_if.h" + +#include +#include +#include + +#include + +/* + * register space access macros + */ +#define I2CREG_WRITE(sc, reg, val) do { \ + bus_write_4(sc->sc_mem_res, (reg), (val)); \ + } while (0) + +#define I2CREG_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) + +/* + * Device methods + */ +static int mtk_iic_probe(device_t); +static int mtk_iic_attach(device_t); +static int mtk_iic_detach(device_t); + +static int mtk_iic_start(device_t dev, u_char slave, int timeout); +static int mtk_iic_stop(device_t dev); +static int mtk_iic_read(device_t dev, char *buf, int len, int *read, int last, int delay); +static int mtk_iic_write(device_t dev, const char *buf, int len, int *sent, int timeout); +static int mtk_iic_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); +#ifdef notyet +static int mtk_iic_repeated_start(device_t dev, u_char slave, int timeout); +#endif + +static struct ofw_compat_data compat_data[] = { + { "ralink,rt2880-i2c", 1 }, + { "ralink,rt3050-i2c", 1 }, + { NULL, 0 } +}; + +struct mtk_iic_softc { + device_t sc_dev; + struct resource *sc_mem_res; + device_t iicbus; + unsigned int clkdiv; + int sc_started; + uint8_t i2cdev_addr; + struct mtx sc_mtx; + int last_slave; +}; + +static int +mtk_iic_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return(ENXIO); + + device_set_desc(dev, "MTK I2C Controller"); + + return (0); +} + +static int +mtk_iic_attach(device_t dev) +{ + int rid; + struct mtk_iic_softc *sc; + + sc = device_get_softc(dev); + + mtx_init(&sc->sc_mtx, "mtk_iic", "mtk_iic", MTX_DEF); + + sc->sc_dev = dev; + rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "Could not map memory\n"); + return (ENXIO); + } + + sc->clkdiv = CLKDIV_VALUE; + + I2CREG_WRITE(sc, RA_I2C_CLKDIV, sc->clkdiv); + + sc->iicbus = device_add_child(dev, "iicbus", -1); + if (sc->iicbus == NULL) { + device_printf(dev, "cannot add iicbus child device\n"); + return (ENXIO); + } + + sc->sc_started = 0; + sc->last_slave = -1; + + return (bus_generic_attach(dev)); +} + +static int +mtk_iic_detach(device_t dev) +{ + struct mtk_iic_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + + return (0); +} + +static int +mtk_iic_start(device_t dev, u_char slave, int timeout) +{ + int error; + struct mtk_iic_softc *sc; + uint32_t r; + int i; + + sc = device_get_softc(dev); + + if (sc->sc_started == 1) + return (IIC_EBUSERR); + + mtx_lock(&sc->sc_mtx); + error = IIC_NOERR; + sc->i2cdev_addr = (slave >> 1); + + /* This is work around for Ralink I2C device. + * Ralink I2C device can't send one byte. Most smoll is two byte. + * Then We send only first time dummy write + */ + + if (sc->last_slave != sc->i2cdev_addr) { + r = I2C_CONFIG_ADDRLEN(I2C_CONFIG_ADDRLEN_8) | + I2C_CONFIG_DEVADLEN(I2C_CONFIG_DEVADLEN_7) | + I2C_CONFIG_ADDRDIS; + I2CREG_WRITE(sc, RA_I2C_CONFIG, r); + + /* dmmy write */ + I2CREG_WRITE(sc, RA_I2C_DEVADDR, sc->i2cdev_addr); + I2CREG_WRITE(sc, RA_I2C_BYTECNT, 0); + I2CREG_WRITE(sc, RA_I2C_DATAOUT, slave); + I2CREG_WRITE(sc, RA_I2C_STARTXFR, I2C_OP_WRITE); + + for (i = 0; i < max_ee_busy_loop; i++) { + r = I2CREG_READ(sc, RA_I2C_STATUS); + if (r & I2C_STATUS_ACKERR) { + error = IIC_ENOACK; + } + if ((r & I2C_STATUS_BUSY) == 0) { + break; + } + } + if (i == max_ee_busy_loop) { + error = IIC_EBUSERR; + } + if (error == IIC_NOERR) { + sc->last_slave = sc->i2cdev_addr; + } + } + + if (error == IIC_NOERR) { + sc->sc_started = 1; + } else { + mtx_unlock(&sc->sc_mtx); + } + + + return (error); +} + +static int +mtk_iic_stop(device_t dev) +{ + int error; + struct mtk_iic_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_started) { + mtx_unlock(&sc->sc_mtx); + error = IIC_NOERR; + sc->sc_started = 0; + } else { + error = IIC_EBUSERR; + } + return error; + +} + +static inline void +mtk_iic_busy_wait(struct mtk_iic_softc *sc) +{ + int i; + uint32_t r; + + for (i = 0; i < i2c_busy_loop; ++i) { + r = I2CREG_READ(sc, RA_I2C_STATUS); + if ((r & I2C_STATUS_BUSY) == 0) + break; + } +} + +static int +mtk_iic_read(device_t dev, char *buf, int len, int *read, int last, + int delay) +{ + struct mtk_iic_softc *sc; + int i, j; + uint32_t r; + + sc = device_get_softc(dev); + + mtk_iic_busy_wait(sc); + + I2CREG_WRITE(sc, RA_I2C_DEVADDR, sc->i2cdev_addr); + I2CREG_WRITE(sc, RA_I2C_BYTECNT, len - 1); + I2CREG_WRITE(sc, RA_I2C_STARTXFR, I2C_OP_READ); + + for (i = 0; i < len; i++) { + for (j=0; j < max_ee_busy_loop; j++) { + r = I2CREG_READ(sc, RA_I2C_STATUS); + if((r & I2C_STATUS_DATARDY) != 0) { + buf[i] = I2CREG_READ(sc, RA_I2C_DATAIN); + break; + } + } + if (j == max_ee_busy_loop) { + return (IIC_ENOACK); + } + } + + *read = i; + + mtk_iic_busy_wait(sc); + return (IIC_NOERR); + +} + +static int +mtk_iic_write(device_t dev, const char *buf, int len, int *sent, + int timeout /* us */ ) +{ + struct mtk_iic_softc *sc; + int i, j; + uint32_t r; + int loopcount; + + sc = device_get_softc(dev); + + mtk_iic_busy_wait(sc); + + I2CREG_WRITE(sc, RA_I2C_DEVADDR, sc->i2cdev_addr); + I2CREG_WRITE(sc, RA_I2C_BYTECNT, len - 1); + I2CREG_WRITE(sc, RA_I2C_DATAOUT, buf[0]); + I2CREG_WRITE(sc, RA_I2C_STARTXFR, I2C_OP_WRITE); + + for (i = 1; i < len; i++) { + if (i == 1) /* first loop send 2 byte */ + loopcount = max_ee_busy_loop * 2; + else + loopcount = max_ee_busy_loop; + for (j=0; j < loopcount; j++) { + r = I2CREG_READ(sc, RA_I2C_STATUS); + if ((r & I2C_STATUS_SDOEMPTY) != 0) { + I2CREG_WRITE(sc, RA_I2C_DATAOUT, buf[i]); + break; + } + } + if (j == max_ee_busy_loop) { + return (IIC_ENOACK); + } + } + + *sent = i; + + mtk_iic_busy_wait(sc); + return (IIC_NOERR); +} + +#ifdef notyet +static int +mtk_iic_repeated_start(device_t dev, u_char slave, int timeout) +{ + return (IIC_NOERR); +} +#endif + +int +mtk_iic_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs) +{ + int i, error, lenread, lenwrote; + u_char addr; + + addr = msgs[0].slave | LSB; + error = mtk_iic_start(bus, addr, 0); + for (i = 0, error = IIC_NOERR; i < nmsgs && error == IIC_NOERR; i++) { + if (msgs[i].flags & IIC_M_RD) { + error = mtk_iic_read((bus), msgs[i].buf, msgs[i].len, + &lenread, IIC_LAST_READ, 0); + } + else { + error = mtk_iic_write((bus), msgs[i].buf, msgs[i].len, + &lenwrote, 0); + } + } + mtk_iic_stop(bus); + return (error); +} + +static int +mtk_iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + return (IIC_NOERR); +} + +static phandle_t +mtk_iic_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the I2C bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t mtk_iic_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, mtk_iic_probe), + DEVMETHOD(device_attach, mtk_iic_attach), + DEVMETHOD(device_detach, mtk_iic_detach), + + /* iicbus interface */ + DEVMETHOD(iicbus_start, mtk_iic_start), + DEVMETHOD(iicbus_stop, mtk_iic_stop), + DEVMETHOD(iicbus_write, mtk_iic_write), + DEVMETHOD(iicbus_read, mtk_iic_read), + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_reset, mtk_iic_reset), + DEVMETHOD(iicbus_transfer, mtk_iic_transfer), +#ifdef notyet + DEVMETHOD(iicbus_repeated_start, mtk_iic_repeated_start), +#endif + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, mtk_iic_get_node), + + DEVMETHOD_END +}; + +static driver_t mtk_iic_driver = { + "iichb", + mtk_iic_methods, + sizeof(struct mtk_iic_softc), +}; + +static devclass_t mtk_iic_devclass; + +DRIVER_MODULE(mtk_iic, simplebus, mtk_iic_driver, mtk_iic_devclass, 0, 0); +DRIVER_MODULE(iicbus, mtk_iic, iicbus_driver, iicbus_devclass, 0, 0); + +MODULE_DEPEND(mtk_iic, iicbus, 1, 1, 1); + +MODULE_VERSION(mtk_iic, 1);