diff --git a/sys/conf/NOTES b/sys/conf/NOTES index a682258cfb6e..6bf416f4e282 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2524,8 +2524,8 @@ device iicoc # OpenCores I2C controller support # I2C peripheral devices # device ds1307 # Dallas DS1307 RTC and compatible -device ds133x # Dallas DS1337, DS1338 and DS1339 RTC device ds1374 # Dallas DS1374 RTC +device ds13rtc # All Dallas/Maxim ds13xx chips device ds1672 # Dallas DS1672 RTC device ds3231 # Dallas DS3231 RTC + temperature device icee # AT24Cxxx and compatible EEPROMs diff --git a/sys/conf/files b/sys/conf/files index f86b65fb54fd..f65a47bb346a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1752,8 +1752,8 @@ dev/ida/ida_disk.c optional ida dev/ida/ida_pci.c optional ida pci dev/iicbus/ad7418.c optional ad7418 dev/iicbus/ds1307.c optional ds1307 -dev/iicbus/ds133x.c optional ds133x dev/iicbus/ds1374.c optional ds1374 +dev/iicbus/ds13rtc.c optional ds13rtc | ds133x dev/iicbus/ds1672.c optional ds1672 dev/iicbus/ds3231.c optional ds3231 dev/iicbus/icee.c optional icee diff --git a/sys/dev/iicbus/ds133x.c b/sys/dev/iicbus/ds133x.c deleted file mode 100644 index 1f703dc895d0..000000000000 --- a/sys/dev/iicbus/ds133x.c +++ /dev/null @@ -1,363 +0,0 @@ -/*- - * Copyright (c) 2008 Stanislav Sedov , - * Rafal Jaworowski , - * Piotr Ziecik . - * 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 -__FBSDID("$FreeBSD$"); -/* - * Dallas Semiconductor DS133X RTC sitting on the I2C bus. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "iicbus_if.h" -#include "clock_if.h" - -#define DS133X_DEVNAME "ds133x_rtc" - -#define DS133X_ADDR 0xd0 /* slave address */ -#define DS133X_DATE_REG 0x0 -#define DS133X_CTRL_REG 0x0e -#define DS133X_OSCD_FLAG 0x80 -#define DS133X_OSF_FLAG 0x80 - -#define DS133X_24H_FLAG 0x40 /* 24 hours mode. */ -#define DS133X_PM_FLAG 0x20 /* AM/PM bit. */ -#define DS133X_CENT_FLAG 0x80 /* Century selector. */ -#define DS133X_CENT_SHIFT 7 - -#define DS1338_REG_CLOCK_HALT 0x00 -#define DS1338_REG_CONTROL 0x07 -#define DS1338_CLOCK_HALT (1 << 7) -#define DS1338_OSC_STOP (1 << 5) - -#define DS1339_REG_CONTROL 0x0E -#define DS1339_REG_STATUS 0x0F -#define DS1339_OSC_STOP (1 << 7) -#define DS1339_ENABLE_OSC (1 << 7) -#define DS1339_BBSQI (1 << 5) - -#define HALFSEC 500000000 /* 1/2 of second. */ - -#define MAX_IIC_DATA_SIZE 7 - -enum { - DS1337, - DS1338, - DS1339, -}; - -struct ds133x_softc { - int sc_type; - device_t sc_dev; -}; - -static int -ds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size) -{ - struct iic_msg msg[] = { - { DS133X_ADDR, IIC_M_WR, 1, &address }, - { DS133X_ADDR, IIC_M_RD, size, data }, - }; - - return (iicbus_transfer(dev, msg, 2)); -} - -static int -ds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size) -{ - uint8_t buffer[MAX_IIC_DATA_SIZE + 1]; - struct iic_msg msg[] = { - { DS133X_ADDR, IIC_M_WR, size + 1, buffer }, - }; - - if (size > MAX_IIC_DATA_SIZE) - return (ENOMEM); - - buffer[0] = address; - memcpy(buffer + 1, data, size); - - return (iicbus_transfer(dev, msg, 1)); -} - -static int -ds133x_detect(device_t dev, int *sc_type) -{ - int error; - uint8_t reg, orig; - - /* - * Check for DS1338. At address 0x0F this chip has RAM, however - * DS1337 and DS1339 have status register. Bits 6-2 in status - * register will be always read as 0. - */ - - if ((error = ds133x_read(dev, DS1339_REG_STATUS, ®, 1))) - return (error); - - orig = reg; - reg |= 0x7C; - - if ((error = ds133x_write(dev, DS1339_REG_STATUS, ®, 1))) - return (error); - - if ((error = ds133x_read(dev, DS1339_REG_STATUS, ®, 1))) - return (error); - - if ((reg & 0x7C) != 0) { - /* This is DS1338 */ - - if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1))) - return (error); - - *sc_type = DS1338; - - return (0); - } - - /* - * Now Check for DS1337. Bit 5 in control register of this chip will be - * always read as 0. In DS1339 changing of this bit is safe until - * chip is powered up. - */ - - if ((error = ds133x_read(dev, DS1339_REG_CONTROL, ®, 1))) - return (error); - - orig = reg; - reg |= DS1339_BBSQI; - - if ((error = ds133x_write(dev, DS1339_REG_CONTROL, ®, 1))) - return (error); - - if ((error = ds133x_read(dev, DS1339_REG_CONTROL, ®, 1))) - return (error); - - if ((reg & DS1339_BBSQI) != 0) { - /* This is DS1339 */ - - if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1))) - return (error); - - *sc_type = DS1339; - return (0); - } - - /* This is DS1337 */ - *sc_type = DS1337; - - return (0); -} - -static int -ds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg, - uint8_t osf_bit) -{ - int error; - uint8_t reg; - - if ((error = ds133x_read(dev, cs_reg, ®, 1))) - return (error); - - if (reg & cs_bit) { /* If clock is stopped - start it */ - reg &= ~cs_bit; - if ((error = ds133x_write(dev, cs_reg, ®, 1))) - return (error); - } - - if ((error = ds133x_read(dev, osf_reg, ®, 1))) - return (error); - - if (reg & osf_bit) { /* Clear oscillator stop flag */ - device_printf(dev, "RTC oscillator was stopped. Check system" - " time and RTC battery.\n"); - reg &= ~osf_bit; - if ((error = ds133x_write(dev, osf_reg, ®, 1))) - return (error); - } - - return (0); -} - - -static void -ds133x_identify(driver_t *driver, device_t parent) -{ - - if (device_find_child(parent, DS133X_DEVNAME, -1) == NULL) - BUS_ADD_CHILD(parent, 0, DS133X_DEVNAME, -1); -} - -static int -ds133x_probe(device_t dev) -{ - struct ds133x_softc *sc; - int error; - - sc = device_get_softc(dev); - - if ((error = ds133x_detect(dev, &sc->sc_type))) - return (error); - - switch (sc->sc_type) { - case DS1337: - device_set_desc(dev, "Dallas Semiconductor DS1337 RTC"); - break; - case DS1338: - device_set_desc(dev, "Dallas Semiconductor DS1338 RTC"); - break; - case DS1339: - device_set_desc(dev, "Dallas Semiconductor DS1339 RTC"); - break; - default: - break; - } - - return (0); -} - -static int -ds133x_attach(device_t dev) -{ - struct ds133x_softc *sc = device_get_softc(dev); - - sc->sc_dev = dev; - - if (sc->sc_type == DS1338) - ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT, - DS1338_REG_CONTROL, DS1338_OSC_STOP); - else - ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC, - DS1339_REG_STATUS, DS1339_OSC_STOP); - - clock_register(dev, 1000000); - - return (0); -} - -static uint8_t -ds133x_get_hours(uint8_t val) -{ - uint8_t ret; - - if (!(val & DS133X_24H_FLAG)) - ret = FROMBCD(val & 0x3f); - else if (!(val & DS133X_PM_FLAG)) - ret = FROMBCD(val & 0x1f); - else - ret = FROMBCD(val & 0x1f) + 12; - - return (ret); -} - -static int -ds133x_gettime(device_t dev, struct timespec *ts) -{ - struct ds133x_softc *sc = device_get_softc(dev); - struct clocktime ct; - uint8_t date[7]; - int error; - - error = ds133x_read(dev, DS133X_DATE_REG, date, 7); - if (error == 0) { - ct.nsec = 0; - ct.sec = FROMBCD(date[0] & 0x7f); - ct.min = FROMBCD(date[1] & 0x7f); - ct.hour = ds133x_get_hours(date[2]); - ct.dow = FROMBCD(date[3] & 0x07) - 1; - ct.day = FROMBCD(date[4] & 0x3f); - ct.mon = FROMBCD(date[5] & 0x1f); - - if (sc->sc_type == DS1338) - ct.year = 2000 + FROMBCD(date[6]); - else - ct.year = 1900 + FROMBCD(date[6]) + - ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100; - - error = clock_ct_to_ts(&ct, ts); - } - - return (error); -} - -static int -ds133x_settime(device_t dev, struct timespec *ts) -{ - struct ds133x_softc *sc = device_get_softc(dev); - struct clocktime ct; - uint8_t date[7]; - - clock_ts_to_ct(ts, &ct); - - date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f; - date[1] = TOBCD(ct.min) & 0x7f; - date[2] = TOBCD(ct.hour) & 0x3f; /* We use 24-hours mode. */ - date[3] = TOBCD(ct.dow + 1) & 0x07; - date[4] = TOBCD(ct.day) & 0x3f; - date[5] = TOBCD(ct.mon) & 0x1f; - if (sc->sc_type == DS1338) - date[6] = TOBCD(ct.year - 2000); - else if (ct.year >= 2000) { - date[5] |= DS133X_CENT_FLAG; - date[6] = TOBCD(ct.year - 2000); - } else - date[6] = TOBCD(ct.year - 1900); - - return (ds133x_write(dev, DS133X_DATE_REG, date, 7)); -} - -static device_method_t ds133x_methods[] = { - DEVMETHOD(device_identify, ds133x_identify), - DEVMETHOD(device_probe, ds133x_probe), - DEVMETHOD(device_attach, ds133x_attach), - - DEVMETHOD(clock_gettime, ds133x_gettime), - DEVMETHOD(clock_settime, ds133x_settime), - - DEVMETHOD_END -}; - -static driver_t ds133x_driver = { - DS133X_DEVNAME, - ds133x_methods, - sizeof(struct ds133x_softc), -}; - -static devclass_t ds133x_devclass; - -DRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0); -MODULE_VERSION(ds133x, 1); -MODULE_DEPEND(ds133x, iicbus, 1, 1, 1); diff --git a/sys/dev/iicbus/ds13rtc.c b/sys/dev/iicbus/ds13rtc.c new file mode 100644 index 000000000000..e2e6b0aca5e1 --- /dev/null +++ b/sys/dev/iicbus/ds13rtc.c @@ -0,0 +1,629 @@ +/*- + * Copyright (c) 2017 Ian Lepore + * 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 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$"); + +/* + * Driver for Dallas/Maxim DS13xx real-time clock/calendar chips: + * + * - DS1307 = Original/basic rtc + 56 bytes ram; 5v only. + * - DS1308 = Updated 1307, available in 1.8v-5v variations. + * - DS1337 = Like 1308, integrated xtal, 32khz output on at powerup. + * - DS1338 = Like 1308, integrated xtal. + * - DS1339 = Like 1337, integrated xtal, integrated trickle charger. + * - DS1340 = Like 1338, ST M41T00 compatible. + * - DS1341 = Like 1338, can slave-sync osc to external clock signal. + * - DS1342 = Like 1341 but requires different xtal. + * - DS1371 = 32-bit binary counter, watchdog timer. + * - DS1372 = 32-bit binary counter, 64-bit unique id in rom. + * - DS1374 = 32-bit binary counter, watchdog timer, trickle charger. + * - DS1375 = Like 1308 but only 16 bytes ram. + * - DS1388 = Rtc, watchdog timer, 512 bytes eeprom (not sram). + * + * This driver supports only basic timekeeping functions. It provides no access + * to or control over any other functionality provided by the chips. + */ + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef FDT +#include +#include +#include +#endif + +#include "clock_if.h" +#include "iicbus_if.h" + +/* + * I2C address 1101 000x + */ +#define DS13xx_ADDR 0xd0 + +/* + * Registers, bits within them, and masks for the various chip types. + */ + +#define DS13xx_R_NONE 0xff /* Placeholder */ + +#define DS130x_R_CONTROL 0x07 +#define DS133x_R_CONTROL 0x0e +#define DS1340_R_CONTROL 0x07 +#define DS1341_R_CONTROL 0x0e +#define DS1371_R_CONTROL 0x07 +#define DS1372_R_CONTROL 0x07 +#define DS1374_R_CONTROL 0x07 +#define DS1375_R_CONTROL 0x0e +#define DS1388_R_CONTROL 0x0c + +#define DS13xx_R_SECOND 0x00 +#define DS1388_R_SECOND 0x01 + +#define DS130x_R_STATUS DS13xx_R_NONE +#define DS133x_R_STATUS 0x0f +#define DS1340_R_STATUS 0x09 +#define DS137x_R_STATUS 0x08 +#define DS1388_R_STATUS 0x0b + +#define DS13xx_B_STATUS_OSF 0x80 /* OSF is 1<<7 in status and sec regs */ +#define DS13xx_B_HOUR_AMPM 0x40 /* AMPM mode is bit 1<<6 */ +#define DS13xx_B_HOUR_PM 0x20 /* PM hours indicated by 1<<5 */ +#define DS13xx_B_MONTH_CENTURY 0x80 /* 21st century indicated by 1<<7 */ + +#define DS13xx_M_SECOND 0x7f /* Masks for all BCD time regs... */ +#define DS13xx_M_MINUTE 0x7f +#define DS13xx_M_12HOUR 0x1f +#define DS13xx_M_24HOUR 0x3f +#define DS13xx_M_DAY 0x3f +#define DS13xx_M_MONTH 0x1f +#define DS13xx_M_YEAR 0xff + +/* + * The chip types we support. + */ +enum { + TYPE_NONE, + TYPE_DS1307, + TYPE_DS1308, + TYPE_DS1337, + TYPE_DS1338, + TYPE_DS1339, + TYPE_DS1340, + TYPE_DS1341, + TYPE_DS1342, + TYPE_DS1371, + TYPE_DS1372, + TYPE_DS1374, + TYPE_DS1375, + TYPE_DS1388, + + TYPE_COUNT +}; +static const char *desc_strings[] = { + "", + "Dallas/Maxim DS1307 RTC", + "Dallas/Maxim DS1308 RTC", + "Dallas/Maxim DS1337 RTC", + "Dallas/Maxim DS1338 RTC", + "Dallas/Maxim DS1339 RTC", + "Dallas/Maxim DS1340 RTC", + "Dallas/Maxim DS1341 RTC", + "Dallas/Maxim DS1342 RTC", + "Dallas/Maxim DS1371 RTC", + "Dallas/Maxim DS1372 RTC", + "Dallas/Maxim DS1374 RTC", + "Dallas/Maxim DS1375 RTC", + "Dallas/Maxim DS1388 RTC", +}; +CTASSERT(nitems(desc_strings) == TYPE_COUNT); + +/* + * The time registers in the order they are laid out in hardware. + */ +struct time_regs { + uint8_t sec, min, hour, wday, day, month, year; +}; + +struct ds13rtc_softc { + device_t dev; + device_t busdev; + u_int flags; /* SC_F_* flags */ + u_int chiptype; /* Type of DS13xx chip */ + uint8_t secaddr; /* Address of seconds register */ + uint8_t osfaddr; /* Address of register with OSF */ +}; + +#define SC_F_BINARY (1u << 0) /* Time is 32-bit binary counter */ +#define SC_F_AMPM (1u << 1) /* Use PM flag in hours reg */ +#define SC_F_CENTURY (1u << 2) /* Use century bit */ + +/* + * We use the compat_data table to look up hint strings in the non-FDT case, so + * define the struct locally when we don't get it from ofw_bus_subr.h. + */ +#ifdef FDT +typedef struct ofw_compat_data ds13_compat_data; +#else +typedef struct { + const char *ocd_str; + uintptr_t ocd_data; +} ds13_compat_data; +#endif + +static ds13_compat_data compat_data[] = { + {"dallas,ds1307", TYPE_DS1307}, + {"dallas,ds1308", TYPE_DS1308}, + {"dallas,ds1337", TYPE_DS1337}, + {"dallas,ds1338", TYPE_DS1338}, + {"dallas,ds1339", TYPE_DS1339}, + {"dallas,ds1340", TYPE_DS1340}, + {"dallas,ds1341", TYPE_DS1341}, + {"dallas,ds1342", TYPE_DS1342}, + {"dallas,ds1371", TYPE_DS1371}, + {"dallas,ds1372", TYPE_DS1372}, + {"dallas,ds1374", TYPE_DS1374}, + {"dallas,ds1375", TYPE_DS1375}, + {"dallas,ds1388", TYPE_DS1388}, + + {NULL, TYPE_NONE}, +}; + +static int +read_reg(struct ds13rtc_softc *sc, uint8_t reg, uint8_t *val) +{ + + return (iicdev_readfrom(sc->dev, reg, val, sizeof(*val), IIC_WAIT)); +} + +static int +write_reg(struct ds13rtc_softc *sc, uint8_t reg, uint8_t val) +{ + + return (iicdev_writeto(sc->dev, reg, &val, sizeof(val), IIC_WAIT)); +} + +static int +read_timeregs(struct ds13rtc_softc *sc, struct time_regs *tregs) +{ + int err; + + if ((err = iicdev_readfrom(sc->dev, sc->secaddr, tregs, + sizeof(*tregs), IIC_WAIT)) != 0) + return (err); + + return (err); +} + +static int +write_timeregs(struct ds13rtc_softc *sc, struct time_regs *tregs) +{ + + return (iicdev_writeto(sc->dev, sc->secaddr, tregs, + sizeof(*tregs), IIC_WAIT)); +} + +static int +read_timeword(struct ds13rtc_softc *sc, time_t *secs) +{ + int err; + uint8_t buf[4]; + + if ((err = iicdev_readfrom(sc->dev, sc->secaddr, buf, sizeof(buf), + IIC_WAIT)) == 0) + *secs = le32dec(buf); + + return (err); +} + +static int +write_timeword(struct ds13rtc_softc *sc, time_t secs) +{ + uint8_t buf[4]; + + le32enc(buf, (uint32_t)secs); + return (iicdev_writeto(sc->dev, sc->secaddr, buf, sizeof(buf), + IIC_WAIT)); +} + +static void +ds13rtc_start(void *arg) +{ + struct ds13rtc_softc *sc; + uint8_t ctlreg, statreg; + + sc = arg; + + /* + * Every chip in this family can be usefully initialized by writing 0 to + * the control register, except DS1375 which has an external oscillator + * controlled by values in the ctlreg that we know nothing about, so + * we'd best leave them alone. For all other chips, writing 0 enables + * the oscillator, disables signals/outputs in battery-backed mode + * (saves power) and disables features like watchdog timers and alarms. + */ + switch (sc->chiptype) { + case TYPE_DS1307: + case TYPE_DS1308: + case TYPE_DS1338: + case TYPE_DS1340: + case TYPE_DS1371: + case TYPE_DS1372: + case TYPE_DS1374: + ctlreg = DS130x_R_CONTROL; + break; + case TYPE_DS1337: + case TYPE_DS1339: + ctlreg = DS133x_R_CONTROL; + break; + case TYPE_DS1341: + case TYPE_DS1342: + ctlreg = DS1341_R_CONTROL; + break; + case TYPE_DS1375: + ctlreg = DS13xx_R_NONE; + break; + case TYPE_DS1388: + ctlreg = DS1388_R_CONTROL; + break; + default: + device_printf(sc->dev, "missing init code for this chiptype\n"); + return; + } + if (ctlreg != DS13xx_R_NONE) + write_reg(sc, ctlreg, 0); + + /* + * Common init. Read the OSF/CH status bit and report stopped clocks to + * the user. The status bit will be cleared the first time we write + * valid time to the chip (and must not be cleared before that). + */ + if (read_reg(sc, sc->osfaddr, &statreg) != 0) { + device_printf(sc->dev, "cannot read RTC clock status bit\n"); + return; + } + if (statreg & DS13xx_B_STATUS_OSF) { + device_printf(sc->dev, + "WARNING: RTC battery failed; time is invalid\n"); + } + + /* + * Figure out whether the chip is configured for AM/PM mode. On all + * chips that do AM/PM mode, the flag bit is in the hours register, + * which is secaddr+2. + */ + if ((sc->chiptype != TYPE_DS1340) && !(sc->flags & SC_F_BINARY)) { + if (read_reg(sc, sc->secaddr + 2, &statreg) != 0) { + device_printf(sc->dev, + "cannot read RTC clock AM/PM bit\n"); + return; + } + if (statreg & DS13xx_B_HOUR_AMPM) + sc->flags |= SC_F_AMPM; + } + + /* + * Everything looks good if we make it to here; register as an RTC. + * Schedule RTC updates to happen just after top-of-second. + */ + clock_register_flags(sc->dev, 1000000, CLOCKF_SETTIME_NO_ADJ); + clock_schedule(sc->dev, 1); +} + +static int +ds13rtc_gettime(device_t dev, struct timespec *ts) +{ + struct clocktime ct; + struct time_regs tregs; + struct ds13rtc_softc *sc; + int err; + uint8_t statreg, hourmask; + + sc = device_get_softc(dev); + + /* Read the OSF/CH bit; if the clock stopped we can't provide time. */ + if ((err = read_reg(sc, sc->osfaddr, &statreg)) != 0) { + return (err); + } + if (statreg & DS13xx_B_STATUS_OSF) + return (EINVAL); /* hardware is good, time is not. */ + + /* If the chip counts time in binary, we just read and return it. */ + if (sc->flags & SC_F_BINARY) { + if ((err = read_timeword(sc, &ts->tv_sec)) != 0) + return (err); + ts->tv_nsec = 0; + } + + /* + * Chip counts in BCD, read and decode it... + */ + if ((err = read_timeregs(sc, &tregs)) != 0) { + device_printf(dev, "cannot read RTC time\n"); + return (err); + } + + if (sc->flags & SC_F_AMPM) + hourmask = DS13xx_M_12HOUR; + else + hourmask = DS13xx_M_24HOUR; + + ct.sec = FROMBCD(tregs.sec & DS13xx_M_SECOND); + ct.min = FROMBCD(tregs.min & DS13xx_M_MINUTE); + ct.hour = FROMBCD(tregs.hour & hourmask); + ct.day = FROMBCD(tregs.day & DS13xx_M_DAY); + ct.mon = FROMBCD(tregs.month & DS13xx_M_MONTH); + ct.year = FROMBCD(tregs.year & DS13xx_M_YEAR); + ct.nsec = 0; + + if (sc->flags & SC_F_AMPM) { + if (ct.hour == 12) + ct.hour = 0; + if (tregs.hour & DS13xx_B_HOUR_PM) + ct.hour += 12; + } + + /* + * If this chip has a century bit, honor it. Otherwise let + * clock_ct_to_ts() infer the century from the 2-digit year. + */ + if (sc->flags & SC_F_CENTURY) + ct.year += (tregs.month & DS13xx_B_MONTH_CENTURY) ? 2000 : 1900; + + err = clock_ct_to_ts(&ct, ts); + + return (err); +} + +static int +ds13rtc_settime(device_t dev, struct timespec *ts) +{ + struct clocktime ct; + struct time_regs tregs; + struct ds13rtc_softc *sc; + int err; + uint8_t cflag, statreg, pmflag; + + sc = device_get_softc(dev); + + /* + * We request a timespec with no resolution-adjustment. That also + * disables utc adjustment, so apply that ourselves. + */ + ts->tv_sec -= utc_offset(); + + /* If the chip counts time in binary, store tv_sec and we're done. */ + if (sc->flags & SC_F_BINARY) + return (write_timeword(sc, ts->tv_sec)); + + clock_ts_to_ct(ts, &ct); + + /* If the chip is in AMPM mode deal with the PM flag. */ + pmflag = 0; + if (sc->flags & SC_F_AMPM) { + if (ct.hour >= 12) { + ct.hour -= 12; + pmflag = DS13xx_B_HOUR_PM; + } + if (ct.hour == 0) + ct.hour = 12; + } + + /* If the chip has a century bit, set it as needed. */ + cflag = 0; + if (sc->flags & SC_F_CENTURY) { + if (ct.year >= 2000) + cflag |= DS13xx_B_MONTH_CENTURY; + } + + tregs.sec = TOBCD(ct.sec); + tregs.min = TOBCD(ct.min); + tregs.hour = TOBCD(ct.hour) | pmflag; + tregs.day = TOBCD(ct.day); + tregs.month = TOBCD(ct.mon) | cflag; + tregs.year = TOBCD(ct.year % 100); + tregs.wday = ct.dow; + + /* + * Set the time. Reset the OSF bit if it is on and it is not part of + * the time registers (in which case writing time resets it). + */ + if ((err = write_timeregs(sc, &tregs)) != 0) + goto errout; + if (sc->osfaddr != sc->secaddr) { + if ((err = read_reg(sc, sc->osfaddr, &statreg)) != 0) + goto errout; + if (statreg & DS13xx_B_STATUS_OSF) { + statreg &= ~DS13xx_B_STATUS_OSF; + err = write_reg(sc, sc->osfaddr, statreg); + } + } + +errout: + + if (err != 0) + device_printf(dev, "cannot update RTC time\n"); + + return (err); +} + +static int +ds13rtc_get_chiptype(device_t dev) +{ +#ifdef FDT + + return (ofw_bus_search_compatible(dev, compat_data)->ocd_data); +#else + ds13_compat_data *cdata; + const char *htype; + + /* + * We can only attach if provided a chiptype hint string. + */ + if (resource_string_value(device_get_name(dev), + device_get_unit(dev), "chiptype", &htype) != 0) + return (TYPE_NONE); + + /* + * Loop through the ofw compat data comparing the hinted chip type to + * the compat strings. + */ + for (cdata = compat_data; cdata->ocd_str != NULL; ++cdata) { + if (strcmp(htype, cdata->ocd_str) == 0) + break; + } + return (cdata->ocd_data); +#endif +} + +static int +ds13rtc_probe(device_t dev) +{ + int chiptype, goodrv; + +#ifdef FDT + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + goodrv = BUS_PROBE_GENERIC; +#else + goodrv = BUS_PROBE_NOWILDCARD; +#endif + + chiptype = ds13rtc_get_chiptype(dev); + if (chiptype == TYPE_NONE) + return (ENXIO); + + device_set_desc(dev, desc_strings[chiptype]); + return (goodrv); +} + +static int +ds13rtc_attach(device_t dev) +{ + struct ds13rtc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->busdev = device_get_parent(dev); + + /* + * We need to know what kind of chip we're driving. + */ + if ((sc->chiptype = ds13rtc_get_chiptype(dev)) == TYPE_NONE) { + device_printf(dev, "impossible: cannot determine chip type\n"); + return (ENXIO); + } + + /* The seconds register is in the same place on all except DS1388. */ + if (sc->chiptype == TYPE_DS1388) + sc->secaddr = DS1388_R_SECOND; + else + sc->secaddr = DS13xx_R_SECOND; + + /* + * The OSF/CH (osc failed/clock-halted) bit appears in different + * registers for different chip types. The DS1375 has no OSF indicator + * because it has no internal oscillator; we just point to an always- + * zero bit in the status register for that chip. + */ + switch (sc->chiptype) { + case TYPE_DS1307: + case TYPE_DS1308: + case TYPE_DS1338: + sc->osfaddr = DS13xx_R_SECOND; + break; + case TYPE_DS1337: + case TYPE_DS1339: + case TYPE_DS1341: + case TYPE_DS1342: + case TYPE_DS1375: + sc->osfaddr = DS133x_R_STATUS; + sc->flags |= SC_F_CENTURY; + break; + case TYPE_DS1340: + sc->osfaddr = DS1340_R_STATUS; + break; + case TYPE_DS1371: + case TYPE_DS1372: + case TYPE_DS1374: + sc->osfaddr = DS137x_R_STATUS; + sc->flags |= SC_F_BINARY; + break; + case TYPE_DS1388: + sc->osfaddr = DS1388_R_STATUS; + break; + } + + /* + * We have to wait until interrupts are enabled. Sometimes I2C read + * and write only works when the interrupts are available. + */ + config_intrhook_oneshot(ds13rtc_start, sc); + + return (0); +} + +static int +ds13rtc_detach(device_t dev) +{ + + clock_unregister(dev); + return (0); +} + +static device_method_t ds13rtc_methods[] = { + DEVMETHOD(device_probe, ds13rtc_probe), + DEVMETHOD(device_attach, ds13rtc_attach), + DEVMETHOD(device_detach, ds13rtc_detach), + + DEVMETHOD(clock_gettime, ds13rtc_gettime), + DEVMETHOD(clock_settime, ds13rtc_settime), + + DEVMETHOD_END +}; + +static driver_t ds13rtc_driver = { + "ds13rtc", + ds13rtc_methods, + sizeof(struct ds13rtc_softc), +}; + +static devclass_t ds13rtc_devclass; + +DRIVER_MODULE(ds13rtc, iicbus, ds13rtc_driver, ds13rtc_devclass, NULL, NULL); +MODULE_VERSION(ds13rtc, 1); +MODULE_DEPEND(ds13rtc, iicbus, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); diff --git a/sys/modules/i2c/Makefile b/sys/modules/i2c/Makefile index c698cafa88ee..2ecfd852c8a9 100644 --- a/sys/modules/i2c/Makefile +++ b/sys/modules/i2c/Makefile @@ -4,6 +4,7 @@ SUBDIR = \ controllers \ cyapa \ ds1307 \ + ds13rtc \ ds3231 \ if_ic \ iic \ diff --git a/sys/modules/i2c/ds13rtc/Makefile b/sys/modules/i2c/ds13rtc/Makefile new file mode 100644 index 000000000000..e50bc2f72075 --- /dev/null +++ b/sys/modules/i2c/ds13rtc/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus +KMOD = ds13rtc +SRCS = ds13rtc.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h + +.include