Index: sys/arm/allwinner/ac100.c =================================================================== --- /dev/null +++ sys/arm/allwinner/ac100.c @@ -0,0 +1,325 @@ +/*- + * Copyright (c) 2014 Jared D. McNeill + * 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 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "clock_if.h" +#include "iicbus_if.h" + +#define AC100_CHIP_AUDIO_RST_REG 0x00 +#define AC100_PLL_CTRL1_REG 0x02 +#define AC100_PLL_CTRL2_REG 0x03 +#define AC100_SYSCLK_CTRL_REG 0x04 +#define AC100_MOD_RST_CTRL_REG 0x05 +#define AC100_ADDA_SR_CTRL_REG 0x06 +#define AC100_I2S1_LCK_CTRL_REG 0x10 +#define AC100_I2S1_SDIN_CTRL_REG 0x11 +#define AC100_I2S1_SDOUT_CTRL_REG 0x12 +#define AC100_I2S1_DIG_MIXER_REG 0x13 +#define AC100_I2S1_VOL_CTRL1_REG 0x14 +#define AC100_I2S1_VOL_CTRL2_REG 0x15 +#define AC100_I2S1_VOL_CTRL3_REG 0x16 +#define AC100_I2S1_VOL_CTRL4_REG 0x17 +#define AC100_I2S1_MXR_GAIN_REG 0x18 +#define AC100_I2S2_CLK_CTRL_REG 0x20 +#define AC100_I2S2_SDIN_CTRL_REG 0x21 +#define AC100_I2S2_SDOUT_CTRL_REG 0x22 +#define AC100_I2S2_DIG_MIXER_REG 0x23 +#define AC100_I2S2_VOL_CTRL1_REG 0x24 +#define AC100_I2S2_VOL_CTRL2_REG 0x26 +#define AC100_I2S2_MXR_GAIN_REG 0x28 +#define AC100_I2S3_CLK_CTRL_REG 0x30 +#define AC100_I2S3_SDIN_CTRL_REG 0x31 +#define AC100_I2S3_SDOUT_CTRL_REG 0x32 +#define AC100_I2S3_SGP_CTRL_REG 0x33 +#define AC100_ADC_DIG_CTRL_REG 0x40 + +#define AC100_RTC_RESET_REG 0xc6 +#define AC100_RTC_CTRL_REG 0xc7 +#define AC100_RTC_SEC_REG 0xc8 +#define AC100_RTC_MIN_REG 0xc9 +#define AC100_RTC_HOU_REG 0xca +#define AC100_RTC_WEE_REG 0xcb +#define AC100_RTC_DAY_REG 0xcc +#define AC100_RTC_MON_REG 0xcd +#define AC100_RTC_YEA_REG 0xce +#define AC100_RTC_UPD_TRIG_REG 0xcf + +#define AC100_RTC_GP_REG(n) (0xe0 + (n)) + +#define AC100_RTC_CTRL_12H_24H_MODE (1 << 0) + +#define AC100_RTC_UPD_TRIG_WRITE (1 << 15) + +#define HALF_OF_SEC_NS 500000000 +#define RTC_RES_US 1000000 + +struct ac100_softc { + device_t dev; + struct resource *res; + + uint16_t addr; + phandle_t rtc; /* RTC fdt node */ +}; + + +static int ac100_rtc_gettime(device_t, struct timespec *); +static int ac100_rtc_settime(device_t, struct timespec *); + +static int ac100_read(device_t, uint8_t, uint8_t *, uint8_t); +static int ac100_write(device_t, uint8_t, uint8_t *, uint8_t); + +static int ac100_read_word(device_t, uint8_t, uint16_t *); +static int ac100_write_word(device_t, uint8_t, uint16_t); + +static int +ac100_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "x-powers,ac100") == 0) + return (ENXIO); + + device_set_desc(dev, "X-Powers AC100"); + return (BUS_PROBE_DEFAULT); +} + +static int +ac100_rtc_attach(device_t dev) +{ + struct ac100_softc *sc; + phandle_t rtc; + + sc = device_get_softc(dev); + rtc = ofw_bus_find_compatible(ofw_bus_get_node(dev), "x-powers,ac100-rtc"); + if (rtc == 0) + return (1); + + sc->rtc = rtc; + + ac100_write_word(dev, AC100_RTC_CTRL_REG, AC100_RTC_CTRL_12H_24H_MODE); + clock_register(dev, RTC_RES_US); + return (0); +} + +static int +ac100_attach(device_t dev) +{ + struct ac100_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->addr = iicbus_get_addr(dev); + sc->rtc = 0; + + if (ac100_rtc_attach(dev) > 0 && bootverbose) + device_printf(dev, "No RTC module\n"); + else if (bootverbose) + device_printf(dev, "RTC initialized\n"); + + return (0); +} + +static int +ac100_detach(device_t dev) +{ + struct ac100_softc *sc; + + sc = device_get_softc(dev); + /* Cannot detach if we have an RTC */ + if (sc->rtc > 0) + return (EBUSY); + + return (bus_generic_detach(dev)); +} + +static int +ac100_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) +{ + struct ac100_softc *sc; + struct iic_msg msg[2]; + + sc = device_get_softc(dev); + + msg[0].slave = sc->addr; + msg[0].flags = IIC_M_WR; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].slave = sc->addr; + msg[1].flags = IIC_M_RD; + msg[1].len = size; + msg[1].buf = data; + + return (iicbus_transfer(dev, msg, 2)); +} + +static int +ac100_read_word(device_t dev, uint8_t reg, uint16_t *data) +{ + return (ac100_read(dev, reg, (uint8_t *)data, 2)); +} + +static int +ac100_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) +{ + struct ac100_softc *sc; + struct iic_msg msg[2]; + + sc = device_get_softc(dev); + + msg[0].slave = sc->addr; + msg[0].flags = IIC_M_WR; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].slave = sc->addr; + msg[1].flags = IIC_M_WR; + msg[1].len = size; + msg[1].buf = data; + + return (iicbus_transfer(dev, msg, 2)); +} + +static int +ac100_write_word(device_t dev, uint8_t reg, uint16_t val) +{ + uint8_t data[2]; + + data[0] = val & 0xff; + data[1] = (val >> 8) & 0xff; + + return (ac100_write(dev, reg, data, 2)); +} + +static int +ac100_rtc_gettime(device_t dev, struct timespec *dt) +{ + struct ac100_softc *sc; + struct clocktime ct; + uint16_t sec, min, hou, wee, day, mon, yea; + + sc = device_get_softc(dev); + /* No supported RTC */ + if (sc->rtc == 0) + return (1); + ac100_read_word(dev, AC100_RTC_SEC_REG, &sec); + ac100_read_word(dev, AC100_RTC_MIN_REG, &min); + ac100_read_word(dev, AC100_RTC_HOU_REG, &hou); + ac100_read_word(dev, AC100_RTC_WEE_REG, &wee); + ac100_read_word(dev, AC100_RTC_DAY_REG, &day); + ac100_read_word(dev, AC100_RTC_MON_REG, &mon); + ac100_read_word(dev, AC100_RTC_YEA_REG, &yea); + + ct.year = POSIX_BASE_YEAR + bcd2bin(yea & 0xff); + ct.mon = bcd2bin(mon & 0x1f); + ct.day = bcd2bin(day & 0x3f); + ct.hour = bcd2bin(hou & 0x3f); + ct.min = bcd2bin(min & 0x7f); + ct.sec = bcd2bin(sec & 0x7f); + ct.dow = bcd2bin(wee & 0x7); + ct.nsec = 0; + + return (clock_ct_to_ts(&ct, dt)); +} + +static int +ac100_rtc_settime(device_t dev, struct timespec *dt) +{ + struct ac100_softc *sc; + struct clocktime ct; + + sc = device_get_softc(dev); + /* No supported RTC */ + if (sc->rtc == 0) + return (1); + if (dt->tv_nsec >= HALF_OF_SEC_NS) + dt->tv_sec++; + dt->tv_nsec = 0; + clock_ts_to_ct(dt, &ct); + + ac100_write_word(dev, AC100_RTC_WEE_REG, bin2bcd(ct.dow) & 0x7); + ac100_write_word(dev, AC100_RTC_SEC_REG, bin2bcd(ct.sec) & 0x7f); + ac100_write_word(dev, AC100_RTC_MIN_REG, bin2bcd(ct.min) & 0x7f); + ac100_write_word(dev, AC100_RTC_HOU_REG, bin2bcd(ct.hour) & 0x3f); + ac100_write_word(dev, AC100_RTC_DAY_REG, bin2bcd(ct.day) & 0x3f); + ac100_write_word(dev, AC100_RTC_MON_REG, bin2bcd(ct.mon) & 0x1f); + ac100_write_word(dev, AC100_RTC_YEA_REG, + bin2bcd(ct.year - POSIX_BASE_YEAR) & 0xff); + ac100_write_word(dev, AC100_RTC_UPD_TRIG_REG, + AC100_RTC_UPD_TRIG_WRITE); + + return (0); +} + +static device_method_t ac100_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ac100_probe), + DEVMETHOD(device_attach, ac100_attach), + DEVMETHOD(device_detach, ac100_detach), + + /* Clock interface */ + DEVMETHOD(clock_gettime, ac100_rtc_gettime), + DEVMETHOD(clock_settime, ac100_rtc_settime), + + DEVMETHOD_END +}; + +static driver_t ac100_driver = { + "ac100", + ac100_methods, + sizeof(struct ac100_softc), +}; + +static devclass_t ac100_devclass; + +EARLY_DRIVER_MODULE(ac100, iicbus, ac100_driver, ac100_devclass, 0, 0, + BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE); +MODULE_VERSION(ac100, 1); +MODULE_DEPEND(ac100, iicbus, 1, 1, 1); Index: sys/arm/allwinner/files.allwinner =================================================================== --- sys/arm/allwinner/files.allwinner +++ sys/arm/allwinner/files.allwinner @@ -13,6 +13,7 @@ arm/allwinner/aw_nmi.c optional intrng arm/allwinner/aw_if_dwc.c optional dwc arm/allwinner/aw_rsb.c optional rsb | p2wi +arm/allwinner/ac100.c optional ac100 arm/allwinner/aw_rtc.c standard arm/allwinner/aw_ts.c standard arm/allwinner/aw_wdog.c standard Index: sys/arm/conf/GENERIC =================================================================== --- sys/arm/conf/GENERIC +++ sys/arm/conf/GENERIC @@ -130,6 +130,7 @@ device twsi device rsb # Allwinner Reduced Serial Bus device p2wi # Allwinner Push-Pull Two Wire +device ac100 # AC100 RTC + Audio Codec device axp209 # AXP209 Power Management Unit device axp81x # AXP813/818 Power Management Unit device bcm2835_bsc