Index: stable/12/sys/dev/iicbus/ads111x.c =================================================================== --- stable/12/sys/dev/iicbus/ads111x.c (revision 356023) +++ stable/12/sys/dev/iicbus/ads111x.c (revision 356024) @@ -1,602 +1,604 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * Copyright (c) 2019 Ian Lepore. * * 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. */ /* * Driver for Texas Instruments ADS101x and ADS111x family i2c ADC chips. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #endif #include #include #include "iicbus_if.h" /* * Chip registers, bit definitions, shifting and masking values. */ #define ADS111x_CONV 0 /* Reg 0: Latest sample (ro) */ #define ADS111x_CONF 1 /* Reg 1: Config (rw) */ #define ADS111x_CONF_OS_SHIFT 15 /* Operational state */ #define ADS111x_CONF_MUX_SHIFT 12 /* Input mux setting */ #define ADS111x_CONF_GAIN_SHIFT 9 /* Programmable gain amp */ #define ADS111x_CONF_MODE_SHIFT 8 /* Operational mode */ #define ADS111x_CONF_RATE_SHIFT 5 /* Sample rate */ #define ADS111x_CONF_COMP_DISABLE 3 /* Comparator disable */ #define ADS111x_LOTHRESH 2 /* Compare lo threshold (rw) */ #define ADS111x_HITHRESH 3 /* Compare hi threshold (rw) */ /* * On config write, the operational-state bit starts a measurement, on read it * indicates when the measurement process is complete/idle. */ #define ADS111x_CONF_MEASURE (1u << ADS111x_CONF_OS_SHIFT) #define ADS111x_CONF_IDLE (1u << ADS111x_CONF_OS_SHIFT) /* * The default values for config items that are not per-channel. Mostly, this * turns off the comparator on chips that have that feature, because this driver * doesn't support it directly. However, the user is allowed to enable the * comparator and we'll leave it alone if they do. That allows them connect the * alert pin to something and use the feature without any help from this driver. */ #define ADS111x_CONF_DEFAULT \ ((1 << ADS111x_CONF_MODE_SHIFT) | ADS111x_CONF_COMP_DISABLE) #define ADS111x_CONF_USERMASK 0x001f /* * Per-channel defaults. The chip only has one control register, and we load * per-channel values into it every time we make a measurement on that channel. * These are the default values for the control register from the datasheet, for * values we maintain on a per-channel basis. */ #define DEFAULT_GAINIDX 2 #define DEFAULT_RATEIDX 4 /* * Full-scale ranges for each available amplifier setting, in microvolts. The * ADS1x13 chips are fixed-range, the other chips contain a programmable gain * amplifier, and the full scale range is based on the amplifier setting. */ static const u_int fixedranges[8] = { 2048000, 2048000, 2048000, 2048000, 2048000, 2048000, 2048000, 2048000, }; static const u_int gainranges[8] = { 6144000, 4096000, 2048000, 1024000, 512000, 256000, 256000, 256000, }; /* The highest value for the ADS101x chip is 0x7ff0, for ADS111x it's 0x7fff. */ #define ADS101x_RANGEDIV ((1 << 15) - 15) #define ADS111x_RANGEDIV ((1 << 15) - 1) /* Samples per second; varies based on chip type. */ static const u_int rates101x[8] = {128, 250, 490, 920, 1600, 2400, 3300, 3300}; static const u_int rates111x[8] = { 8, 16, 32, 64, 128, 250, 475, 860}; struct ads111x_channel { u_int gainidx; /* Amplifier (full-scale range) config index */ u_int rateidx; /* Samples per second config index */ bool configured; /* Channel has been configured */ }; #define ADS111x_MAX_CHANNELS 8 struct ads111x_chipinfo { const char *name; const u_int *rangetab; const u_int *ratetab; u_int numchan; int rangediv; }; static struct ads111x_chipinfo ads111x_chip_infos[] = { { "ADS1013", fixedranges, rates101x, 1, ADS101x_RANGEDIV }, { "ADS1014", gainranges, rates101x, 1, ADS101x_RANGEDIV }, { "ADS1015", gainranges, rates101x, 8, ADS101x_RANGEDIV }, { "ADS1113", fixedranges, rates111x, 1, ADS111x_RANGEDIV }, { "ADS1114", gainranges, rates111x, 1, ADS111x_RANGEDIV }, { "ADS1115", gainranges, rates111x, 8, ADS111x_RANGEDIV }, }; #ifdef FDT static struct ofw_compat_data compat_data[] = { {"ti,ads1013", (uintptr_t)&ads111x_chip_infos[0]}, {"ti,ads1014", (uintptr_t)&ads111x_chip_infos[1]}, {"ti,ads1015", (uintptr_t)&ads111x_chip_infos[2]}, {"ti,ads1113", (uintptr_t)&ads111x_chip_infos[3]}, {"ti,ads1114", (uintptr_t)&ads111x_chip_infos[4]}, {"ti,ads1115", (uintptr_t)&ads111x_chip_infos[5]}, {NULL, (uintptr_t)NULL}, }; IICBUS_FDT_PNP_INFO(compat_data); #endif struct ads111x_softc { device_t dev; struct sx lock; int addr; int cfgword; const struct ads111x_chipinfo *chipinfo; struct ads111x_channel channels[ADS111x_MAX_CHANNELS]; }; static int ads111x_write_2(struct ads111x_softc *sc, int reg, int val) { uint8_t data[3]; struct iic_msg msgs[1]; uint8_t slaveaddr; slaveaddr = iicbus_get_addr(sc->dev); data[0] = reg; be16enc(&data[1], val); msgs[0].slave = slaveaddr; msgs[0].flags = IIC_M_WR; msgs[0].len = sizeof(data); msgs[0].buf = data; return (iicbus_transfer_excl(sc->dev, msgs, nitems(msgs), IIC_WAIT)); } static int ads111x_read_2(struct ads111x_softc *sc, int reg, int *val) { int err; uint8_t data[2]; err = iic2errno(iicdev_readfrom(sc->dev, reg, data, 2, IIC_WAIT)); if (err == 0) *val = (int16_t)be16dec(data); return (err); } static int ads111x_sample_voltage(struct ads111x_softc *sc, int channum, int *voltage) { struct ads111x_channel *chan; int err, cfgword, convword, rate, retries, waitns; int64_t fsrange; chan = &sc->channels[channum]; /* Ask the chip to do a one-shot measurement of the given channel. */ cfgword = sc->cfgword | (1 << ADS111x_CONF_OS_SHIFT) | (channum << ADS111x_CONF_MUX_SHIFT) | (chan->gainidx << ADS111x_CONF_GAIN_SHIFT) | (chan->rateidx << ADS111x_CONF_RATE_SHIFT); if ((err = ads111x_write_2(sc, ADS111x_CONF, cfgword)) != 0) return (err); /* * Calculate how long it will take to make the measurement at the * current sampling rate (round up). The measurement averaging time * ranges from 300us to 125ms, so we yield the cpu while waiting. */ rate = sc->chipinfo->ratetab[chan->rateidx]; waitns = (1000000000 + rate - 1) / rate; err = pause_sbt("ads111x", nstosbt(waitns), 0, C_PREL(2)); if (err != 0 && err != EWOULDBLOCK) return (err); /* * In theory the measurement should be available now; we waited long * enough. However, the chip times its averaging intervals using an * internal 1 MHz oscillator which likely isn't running at the same rate * as the system clock, so we have to double-check that the measurement * is complete before reading the result. If it's not ready yet, yield * the cpu again for 5% of the time we originally calculated. * * Unlike most i2c slaves, this device does not auto-increment the * register number on reads, so we can't read both status and * measurement in one operation. */ for (retries = 5; ; --retries) { if (retries == 0) return (EWOULDBLOCK); if ((err = ads111x_read_2(sc, ADS111x_CONF, &cfgword)) != 0) return (err); if (cfgword & ADS111x_CONF_IDLE) break; pause_sbt("ads111x", nstosbt(waitns / 20), 0, C_PREL(2)); } /* Retrieve the sample and convert it to microvolts. */ if ((err = ads111x_read_2(sc, ADS111x_CONV, &convword)) != 0) return (err); fsrange = sc->chipinfo->rangetab[chan->gainidx]; *voltage = (int)((convword * fsrange ) / sc->chipinfo->rangediv); return (err); } static int ads111x_sysctl_gainidx(SYSCTL_HANDLER_ARGS) { struct ads111x_softc *sc; int chan, err, gainidx; sc = arg1; chan = arg2; gainidx = sc->channels[chan].gainidx; err = sysctl_handle_int(oidp, &gainidx, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (gainidx < 0 || gainidx > 7) return (EINVAL); sx_xlock(&sc->lock); sc->channels[chan].gainidx = gainidx; sx_xunlock(&sc->lock); return (err); } static int ads111x_sysctl_rateidx(SYSCTL_HANDLER_ARGS) { struct ads111x_softc *sc; int chan, err, rateidx; sc = arg1; chan = arg2; rateidx = sc->channels[chan].rateidx; err = sysctl_handle_int(oidp, &rateidx, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (rateidx < 0 || rateidx > 7) return (EINVAL); sx_xlock(&sc->lock); sc->channels[chan].rateidx = rateidx; sx_xunlock(&sc->lock); return (err); } static int ads111x_sysctl_voltage(SYSCTL_HANDLER_ARGS) { struct ads111x_softc *sc; int chan, err, voltage; sc = arg1; chan = arg2; if (req->oldptr != NULL) { sx_xlock(&sc->lock); err = ads111x_sample_voltage(sc, chan, &voltage); sx_xunlock(&sc->lock); if (err != 0) { device_printf(sc->dev, "conversion read failed, error %d\n", err); return (err); } } err = sysctl_handle_int(oidp, &voltage, 0, req); return (err); } static int ads111x_sysctl_config(SYSCTL_HANDLER_ARGS) { struct ads111x_softc *sc; int config, err; sc = arg1; config = sc->cfgword & ADS111x_CONF_USERMASK; err = sysctl_handle_int(oidp, &config, 0, req); if (err != 0 || req->newptr == NULL) return (err); sx_xlock(&sc->lock); sc->cfgword = config & ADS111x_CONF_USERMASK; err = ads111x_write_2(sc, ADS111x_CONF, sc->cfgword); sx_xunlock(&sc->lock); return (err); } static int ads111x_sysctl_lothresh(SYSCTL_HANDLER_ARGS) { struct ads111x_softc *sc; int thresh, err; sc = arg1; if ((err = ads111x_read_2(sc, ADS111x_LOTHRESH, &thresh)) != 0) return (err); err = sysctl_handle_int(oidp, &thresh, 0, req); if (err != 0 || req->newptr == NULL) return (err); sx_xlock(&sc->lock); err = ads111x_write_2(sc, ADS111x_CONF, thresh); sx_xunlock(&sc->lock); return (err); } static int ads111x_sysctl_hithresh(SYSCTL_HANDLER_ARGS) { struct ads111x_softc *sc; int thresh, err; sc = arg1; if ((err = ads111x_read_2(sc, ADS111x_HITHRESH, &thresh)) != 0) return (err); err = sysctl_handle_int(oidp, &thresh, 0, req); if (err != 0 || req->newptr == NULL) return (err); sx_xlock(&sc->lock); err = ads111x_write_2(sc, ADS111x_CONF, thresh); sx_xunlock(&sc->lock); return (err); } static void ads111x_setup_channel(struct ads111x_softc *sc, int chan, int gainidx, int rateidx) { struct ads111x_channel *c; struct sysctl_ctx_list *ctx; struct sysctl_oid *chantree, *devtree; char chanstr[4]; c = &sc->channels[chan]; c->gainidx = gainidx; c->rateidx = rateidx; /* * If setting up the channel for the first time, create channel's * sysctl entries. We might have already configured the channel if * config data for it exists in both FDT and hints. */ if (c->configured) return; ctx = device_get_sysctl_ctx(sc->dev); devtree = device_get_sysctl_tree(sc->dev); snprintf(chanstr, sizeof(chanstr), "%d", chan); chantree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(devtree), OID_AUTO, chanstr, CTLFLAG_RD, NULL, "channel data"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chantree), OID_AUTO, "gain_index", CTLTYPE_INT | CTLFLAG_RWTUN, sc, chan, ads111x_sysctl_gainidx, "I", "programmable gain amp setting, 0-7"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chantree), OID_AUTO, "rate_index", CTLTYPE_INT | CTLFLAG_RWTUN, sc, chan, ads111x_sysctl_rateidx, "I", "sample rate setting, 0-7"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chantree), OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_SKIP, sc, chan, ads111x_sysctl_voltage, "I", "sampled voltage in microvolts"); c->configured = true; } static void ads111x_add_channels(struct ads111x_softc *sc) { const char *name; uint32_t chan, gainidx, num_added, rateidx, unit; bool found; #ifdef FDT phandle_t child, node; /* Configure any channels that have FDT data. */ num_added = 0; node = ofw_bus_get_node(sc->dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { if (OF_getencprop(child, "reg", &chan, sizeof(chan)) == -1) continue; if (chan >= ADS111x_MAX_CHANNELS) continue; gainidx = DEFAULT_GAINIDX; rateidx = DEFAULT_RATEIDX; OF_getencprop(child, "ti,gain", &gainidx, sizeof(gainidx)); OF_getencprop(child, "ti,datarate", &rateidx, sizeof(rateidx)); ads111x_setup_channel(sc, chan, gainidx, rateidx); ++num_added; } #else num_added = 0; #endif /* Configure any channels that have hint data. */ name = device_get_name(sc->dev); unit = device_get_unit(sc->dev); for (chan = 0; chan < sc->chipinfo->numchan; ++chan) { found = false; gainidx = DEFAULT_GAINIDX; rateidx = DEFAULT_RATEIDX; if (resource_int_value(name, unit, "gain_index", &gainidx) == 0) found = true; if (resource_int_value(name, unit, "rate_index", &gainidx) == 0) found = true; if (found) { ads111x_setup_channel(sc, chan, gainidx, rateidx); ++num_added; } } /* If any channels were configured via FDT or hints, we're done. */ if (num_added > 0) return; /* * No channel config; add all possible channels using default values, * and let the user configure the ones they want on the fly via sysctl. */ for (chan = 0; chan < sc->chipinfo->numchan; ++chan) { gainidx = DEFAULT_GAINIDX; rateidx = DEFAULT_RATEIDX; ads111x_setup_channel(sc, chan, gainidx, rateidx); } } static const struct ads111x_chipinfo * ads111x_find_chipinfo(device_t dev) { const struct ads111x_chipinfo *info; const char *chiptype; int i; #ifdef FDT if (ofw_bus_status_okay(dev)) { info = (struct ads111x_chipinfo*) ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (info != NULL) return (info); } #endif /* For hinted devices, we must be told the chip type. */ chiptype = NULL; resource_string_value(device_get_name(dev), device_get_unit(dev), "type", &chiptype); if (chiptype != NULL) { for (i = 0; i < nitems(ads111x_chip_infos); ++i) { info = &ads111x_chip_infos[i]; if (strcasecmp(chiptype, info->name) == 0) return (info); } } return (NULL); } static int ads111x_probe(device_t dev) { const struct ads111x_chipinfo *info; info = ads111x_find_chipinfo(dev); if (info != NULL) { device_set_desc(dev, info->name); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ads111x_attach(device_t dev) { struct ads111x_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; int err; sc = device_get_softc(dev); sc->dev = dev; sc->addr = iicbus_get_addr(dev); sc->cfgword = ADS111x_CONF_DEFAULT; sc->chipinfo = ads111x_find_chipinfo(sc->dev); if (sc->chipinfo == NULL) { device_printf(dev, "cannot get chipinfo (but it worked during probe)"); return (ENXIO); } /* Set the default chip config. */ if ((err = ads111x_write_2(sc, ADS111x_CONF, sc->cfgword)) != 0) { device_printf(dev, "cannot write chip config register\n"); return (err); } /* Add the sysctl handler to set the chip configuration register. */ ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "config", CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, ads111x_sysctl_config, "I", "configuration register word"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "lo_thresh", CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, ads111x_sysctl_lothresh, "I", "comparator low threshold"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "hi_thresh", CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, ads111x_sysctl_hithresh, "I", "comparator high threshold"); /* Set up channels based on metadata or default config. */ ads111x_add_channels(sc); sx_init(&sc->lock, "ads111x"); return (0); } static int ads111x_detach(device_t dev) { struct ads111x_softc *sc; sc = device_get_softc(dev); sx_destroy(&sc->lock); return (0); } static device_method_t ads111x_methods[] = { DEVMETHOD(device_probe, ads111x_probe), DEVMETHOD(device_attach, ads111x_attach), DEVMETHOD(device_detach, ads111x_detach), DEVMETHOD_END, }; static driver_t ads111x_driver = { "ads111x", ads111x_methods, sizeof(struct ads111x_softc), }; static devclass_t ads111x_devclass; DRIVER_MODULE(ads111x, iicbus, ads111x_driver, ads111x_devclass, NULL, NULL); MODULE_VERSION(ads111x, 1); MODULE_DEPEND(ads111x, iicbus, 1, 1, 1); Index: stable/12/sys/dev/iicbus/ds13rtc.c =================================================================== --- stable/12/sys/dev/iicbus/ds13rtc.c (revision 356023) +++ stable/12/sys/dev/iicbus/ds13rtc.c (revision 356024) @@ -1,620 +1,621 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * 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 chiptype; /* Type of DS13xx chip */ uint8_t secaddr; /* Address of seconds register */ uint8_t osfaddr; /* Address of register with OSF */ bool use_ampm; /* Use AM/PM mode. */ bool use_century; /* Use the Century bit. */ bool is_binary_counter; /* Chip has 32-bit binary counter. */ }; /* * 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->is_binary_counter) { 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->use_ampm = true; } /* * 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 bcd_clocktime bct; 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->is_binary_counter) { ts->tv_nsec = 0; return (read_timeword(sc, &ts->tv_sec)); } /* * 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->use_ampm) hourmask = DS13xx_M_12HOUR; else hourmask = DS13xx_M_24HOUR; bct.nsec = 0; bct.ispm = tregs.hour & DS13xx_B_HOUR_PM; bct.sec = tregs.sec & DS13xx_M_SECOND; bct.min = tregs.min & DS13xx_M_MINUTE; bct.hour = tregs.hour & hourmask; bct.day = tregs.day & DS13xx_M_DAY; bct.mon = tregs.month & DS13xx_M_MONTH; bct.year = tregs.year & DS13xx_M_YEAR; /* * 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->use_century) bct.year += (tregs.month & DS13xx_B_MONTH_CENTURY) ? 0x100 : 0; clock_dbgprint_bcd(sc->dev, CLOCK_DBG_READ, &bct); err = clock_bcd_to_ts(&bct, ts, sc->use_ampm); return (err); } static int ds13rtc_settime(device_t dev, struct timespec *ts) { struct bcd_clocktime bct; struct time_regs tregs; struct ds13rtc_softc *sc; int err; uint8_t cflag, statreg, pmflags; 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->is_binary_counter) return (write_timeword(sc, ts->tv_sec)); clock_ts_to_bcd(ts, &bct, sc->use_ampm); clock_dbgprint_bcd(sc->dev, CLOCK_DBG_WRITE, &bct); /* If the chip is in AMPM mode deal with the PM flag. */ pmflags = 0; if (sc->use_ampm) { pmflags = DS13xx_B_HOUR_AMPM; if (bct.ispm) pmflags |= DS13xx_B_HOUR_PM; } /* If the chip has a century bit, set it as needed. */ cflag = 0; if (sc->use_century) { if (bct.year >= 2000) cflag |= DS13xx_B_MONTH_CENTURY; } tregs.sec = bct.sec; tregs.min = bct.min; tregs.hour = bct.hour | pmflags; tregs.day = bct.day; tregs.month = bct.mon | cflag; tregs.year = bct.year & 0xff; tregs.wday = bct.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), "compatible", &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->use_century = true; 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->is_binary_counter = true; 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); IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/iic_recover_bus.c =================================================================== --- stable/12/sys/dev/iicbus/iic_recover_bus.c (revision 356023) +++ stable/12/sys/dev/iicbus/iic_recover_bus.c (revision 356024) @@ -1,124 +1,125 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * Copyright (c) 2017 Ian Lepore - * All rights reserved. * * Development sponsored by Microsemi, 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. * * 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$"); /* * Helper code to recover a hung i2c bus by bit-banging a recovery sequence. * * An i2c bus can be hung by a slave driving the clock (rare) or data lines low. * The most common cause is a partially-completed transaction such as rebooting * while a slave is sending a byte of data. Because i2c allows the clock to * freeze for any amount of time, the slave device will continue driving the * data line until power is removed, or the clock cycles enough times to * complete the current byte. After completing any partial byte, a START/STOP * sequence resets the slave and the bus is recovered. * * Any i2c driver which is able to manually set the level of the clock and data * lines can use this common code for bus recovery. On many SOCs that have * embedded i2c controllers, the i2c pins can be temporarily reassigned as gpio * pins to do the bus recovery, then can be assigned back to the i2c hardware. */ #include "opt_platform.h" #include #include #include #include #include int iic_recover_bus(struct iicrb_pin_access *pins) { const u_int timeout_us = 40000; const u_int delay_us = 500; int i; /* * Start with clock and data high. */ pins->setsda(pins->ctx, 1); pins->setscl(pins->ctx, 1); /* * At this point, SCL should be high. If it's not, some slave on the * bus is doing clock-stretching and we should wait a while. If that * slave is completely locked up there may be no way to recover at all. * We wait up to 40 milliseconds, a seriously pessimistic time (even a * cheap eeprom has a max post-write delay of only 10ms), and also long * enough to allow SMB slaves to timeout normally after 35ms. */ for (i = 0; i < timeout_us; i += delay_us) { if (pins->getscl(pins->ctx)) break; DELAY(delay_us); } if (i >= timeout_us) return (IIC_EBUSERR); /* * At this point we should be able to control the clock line. Some * slave may be part way through a byte transfer, and could be holding * the data line low waiting for more clock pulses to finish the byte. * Cycle the clock until we see the data line go high, but only up to 9 * times because if it's not free after 9 clocks we're never going to * win this battle. We do 9 max because that's a byte plus an ack/nack * bit, after which the slave must not be driving the data line anymore. */ for (i = 0; ; ++i) { if (pins->getsda(pins->ctx)) break; if (i == 9) return (IIC_EBUSERR); pins->setscl(pins->ctx, 0); DELAY(5); pins->setscl(pins->ctx, 1); DELAY(5); } /* * At this point we should be in control of both the clock and data * lines, and both lines should be high. To complete the reset of a * slave that was part way through a transaction, we need to do a * START/STOP sequence, which leaves both lines high at the end. * - START: SDA transitions high->low while SCL remains high. * - STOP: SDA transitions low->high while SCL remains high. * Note that even though the clock line remains high, we transition the * data line no faster than it would change state with a 100khz clock. */ pins->setsda(pins->ctx, 0); DELAY(5); pins->setsda(pins->ctx, 1); DELAY(5); return (0); } Index: stable/12/sys/dev/iicbus/iic_recover_bus.h =================================================================== --- stable/12/sys/dev/iicbus/iic_recover_bus.h (revision 356023) +++ stable/12/sys/dev/iicbus/iic_recover_bus.h (revision 356024) @@ -1,57 +1,58 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * Copyright (c) 2017 Ian Lepore - * All rights reserved. * * Development sponsored by Microsemi, 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. * * 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$ */ /* * Helper code to recover a hung i2c bus by bit-banging a recovery sequence. */ #ifndef _IICBUS_IIC_RECOVER_BUS_H_ #define _IICBUS_IIC_RECOVER_BUS_H_ struct iicrb_pin_access { void *ctx; int (*getsda)(void *ctx); void (*setsda)(void *ctx, int value); int (*getscl)(void *ctx); void (*setscl)(void *ctx, int value); }; /* * Drive the bus-recovery logic by manipulating the line states using the * caller-provided functions. This does not block or sleep or acquire any locks * (unless the provided pin access functions do so). It uses DELAY() to pace * bits on the bus. * * Returns 0 if the bus is functioning properly or IIC_EBUSERR if the recovery * attempt failed and some slave device is still driving the bus. */ int iic_recover_bus(struct iicrb_pin_access *pins); #endif Index: stable/12/sys/dev/iicbus/isl12xx.c =================================================================== --- stable/12/sys/dev/iicbus/isl12xx.c (revision 356023) +++ stable/12/sys/dev/iicbus/isl12xx.c (revision 356024) @@ -1,359 +1,361 @@ /*- - * Copyright (c) 2017 Ian Lepore. All rights reserved. + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2017 Ian Lepore. * * 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$"); /* * Driver for ISL12xx family i2c realtime clocks: * - ISL1209 = 2B sram, tamper/event timestamp * - ISL1218 = 8B sram, DS13xx pin compatible (but not software compatible) * - ISL1219 = 2B sram, tamper/event timestamp * - ISL1220 = 8B sram, separate Fout * - ISL1221 = 2B sram, separate Fout, tamper/event timestamp * * This driver supports only the basic RTC functionality in all these chips. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #ifdef FDT #include #include #endif #include #include #include "clock_if.h" #include "iicbus_if.h" /* * All register and bit names as found in the datasheet. When a bit name ends * in 'B' that stands for "bar" and it is an active-low signal; something named * "EVENB" implies 1=event-disable, 0=event-enable. */ #define ISL12XX_SC_REG 0x00 /* RTC Seconds */ #define ISL12XX_SR_REG 0x07 /* Status */ #define ISL12XX_SR_ARST (1u << 7) /* Auto-reset on status read */ #define ISL12XX_SR_XTOSCB (1u << 5) /* Osc disable (use ext osc) */ #define ISL12XX_SR_WRTC (1u << 4) /* Write RTC enable */ #define ISL12XX_SR_EVT (1u << 3) /* Event occurred (w0c) */ #define ISL12XX_SR_ALM (1u << 2) /* Alarm occurred (w0c) */ #define ISL12XX_SR_BAT (1u << 1) /* Running on battery (w0c) */ #define ISL12XX_SR_RTCF (1u << 0) /* RTC fail (power loss) */ #define ISL12XX_SR_W0C_BITS (ISL12XX_SR_BAT | ISL12XX_SR_ALM | ISL12XX_SR_EVT) #define ISL12XX_INT_REG 0x08 /* Interrupts */ #define ISL12XX_INT_IM (1u << 7) /* Alarm interrupt mode */ #define ISL12XX_INT_ALME (1u << 6) /* Alarm enable */ #define ISL12XX_INT_LPMODE (1u << 5) /* Low Power mode */ #define ISL12XX_INT_FOBATB (1u << 4) /* Fout/IRQ disabled on bat */ #define ISL12XX_INT_FO_SHIFT 0 /* Frequency output select */ #define ISL12XX_INT_FO_MASK 0x0f /* shift and mask. */ #define ISL12XX_EV_REG 0x09 /* Event */ #define ISL12XX_EV_EVIENB (1u << 7) /* Disable internal pullup */ #define ISL12XX_EV_EVBATB (1u << 6) /* Disable ev detect on bat */ #define ISL12XX_EV_RTCHLT (1u << 5) /* Halt RTC on event */ #define ISL12XX_EV_EVEN (1u << 4) /* Event detect enable */ #define ISL12XX_EV_EHYS_SHIFT 2 /* Event input hysteresis */ #define ISL12XX_EV_EHYS_MASK 0x03 /* selection; see datasheet */ #define ISL12XX_EV_ESMP_SHIFT 0 /* Event input sample rate */ #define ISL12XX_EV_ESMP_MASK 0x03 /* selection; see datasheet */ #define ISL12XX_ATR_REG 0x0a /* Analog trim (osc adjust) */ #define ISL12XX_DTR_REG 0x0b /* Digital trim (osc adjust) */ #define ISL12XX_SCA_REG 0x0c /* Alarm seconds */ #define ISL12XX_USR1_REG 0x12 /* User byte 1 */ #define ISL12XX_USR2_REG 0x13 /* User byte 2 */ #define ISL12XX_SCT_REG 0x14 /* Timestamp (event) seconds */ #define ISL12XX_24HR_FLAG (1u << 7) /* Hours register 24-hr mode */ #define ISL12XX_PM_FLAG (1u << 5) /* Hours register PM flag */ #define ISL12xx_12HR_MASK 0x1f /* Hours mask in AM/PM mode */ #define ISL12xx_24HR_MASK 0x3f /* Hours mask in 24-hr mode */ /* * A struct laid out in the same order as the time registers in the chip. */ struct time_regs { uint8_t sec, min, hour, day, month, year; }; struct isl12xx_softc { device_t dev; device_t busdev; struct intr_config_hook init_hook; bool use_ampm; }; #ifdef FDT static struct ofw_compat_data compat_data[] = { {"isil,isl1209", 1}, {"isil,isl1218", 1}, {"isil,isl1219", 1}, {"isil,isl1220", 1}, {"isil,isl1221", 1}, {NULL, 0}, }; #endif /* * When doing i2c IO, indicate that we need to wait for exclusive bus ownership, * but that we should not wait if we already own the bus. This lets us put * iicbus_acquire_bus() calls with a non-recursive wait at the entry of our API * functions to ensure that only one client at a time accesses the hardware for * the entire series of operations it takes to read or write the clock. */ #define WAITFLAGS (IIC_WAIT | IIC_RECURSIVE) static inline int isl12xx_read1(struct isl12xx_softc *sc, uint8_t reg, uint8_t *data) { return (iicdev_readfrom(sc->dev, reg, data, 1, WAITFLAGS)); } static inline int isl12xx_write1(struct isl12xx_softc *sc, uint8_t reg, uint8_t val) { return (iicdev_writeto(sc->dev, reg, &val, 1, WAITFLAGS)); } static void isl12xx_init(void *arg) { struct isl12xx_softc *sc = arg; uint8_t sreg; config_intrhook_disestablish(&sc->init_hook); /* * Check the clock-stopped/power-fail bit, just so we can report it to * the user at boot time. */ isl12xx_read1(sc, ISL12XX_SR_REG, &sreg); if (sreg & ISL12XX_SR_RTCF) { device_printf(sc->dev, "RTC clock stopped; check battery\n"); } /* * Register as a system realtime clock. */ clock_register_flags(sc->dev, 1000000, CLOCKF_SETTIME_NO_ADJ); clock_schedule(sc->dev, 1); } static int isl12xx_probe(device_t dev) { #ifdef FDT if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Intersil ISL12xx RTC"); return (BUS_PROBE_DEFAULT); } #endif return (ENXIO); } static int isl12xx_attach(device_t dev) { struct isl12xx_softc *sc = device_get_softc(dev); sc->dev = dev; sc->busdev = device_get_parent(sc->dev); /* * Chip init must wait until interrupts are enabled. Often i2c access * works only when the interrupts are available. */ sc->init_hook.ich_func = isl12xx_init; sc->init_hook.ich_arg = sc; if (config_intrhook_establish(&sc->init_hook) != 0) return (ENOMEM); return (0); } static int isl12xx_detach(device_t dev) { clock_unregister(dev); return (0); } static int isl12xx_gettime(device_t dev, struct timespec *ts) { struct isl12xx_softc *sc = device_get_softc(dev); struct bcd_clocktime bct; struct time_regs tregs; int err; uint8_t hourmask, sreg; /* * Read the status and time registers. */ if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) == 0) { if ((err = isl12xx_read1(sc, ISL12XX_SR_REG, &sreg)) == 0) { err = iicdev_readfrom(sc->dev, ISL12XX_SC_REG, &tregs, sizeof(tregs), WAITFLAGS); } iicbus_release_bus(sc->busdev, sc->dev); } if (err != 0) return (err); /* If power failed, we can't provide valid time. */ if (sreg & ISL12XX_SR_RTCF) return (EINVAL); /* If chip is in AM/PM mode remember that for when we set time. */ if (tregs.hour & ISL12XX_24HR_FLAG) { hourmask = ISL12xx_24HR_MASK; } else { sc->use_ampm = true; hourmask = ISL12xx_12HR_MASK; } bct.nsec = 0; bct.sec = tregs.sec; bct.min = tregs.min; bct.hour = tregs.hour & hourmask; bct.day = tregs.day; bct.mon = tregs.month; bct.year = tregs.year; bct.ispm = tregs.hour & ISL12XX_PM_FLAG; clock_dbgprint_bcd(sc->dev, CLOCK_DBG_READ, &bct); return (clock_bcd_to_ts(&bct, ts, sc->use_ampm)); } static int isl12xx_settime(device_t dev, struct timespec *ts) { struct isl12xx_softc *sc = device_get_softc(dev); struct bcd_clocktime bct; struct time_regs tregs; int err; uint8_t ampmflags, sreg; /* * We request a timespec with no resolution-adjustment. That also * disables utc adjustment, so apply that ourselves. */ ts->tv_sec -= utc_offset(); ts->tv_nsec = 0; clock_ts_to_bcd(ts, &bct, sc->use_ampm); clock_dbgprint_bcd(sc->dev, CLOCK_DBG_WRITE, &bct); /* If the chip is in AM/PM mode, set flags as needed. */ if (!sc->use_ampm) ampmflags = ISL12XX_24HR_FLAG; else ampmflags = bct.ispm ? ISL12XX_PM_FLAG : 0; tregs.sec = bct.sec; tregs.min = bct.min; tregs.hour = bct.hour | ampmflags; tregs.day = bct.day; tregs.month = bct.mon; tregs.year = bct.year % 100; /* * To set the time we have to set the WRTC enable bit in the control * register, then write the time regs, then clear the WRTC bit. While * doing so we have to be careful to not write a 0 to any sreg bit which * is "write 0 to clear". One of those bits could get set between * reading and writing the register. All those bits ignore attempts to * write a 1, so just always OR-in all the W0C bits to be sure we never * accidentally clear one. We hold ownership of the i2c bus for the * whole read-modify-write sequence. */ if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) != 0) return (err); if ((err = isl12xx_read1(sc, ISL12XX_SR_REG, &sreg)) == 0) { sreg |= ISL12XX_SR_WRTC | ISL12XX_SR_W0C_BITS; if ((err = isl12xx_write1(sc, ISL12XX_SR_REG, sreg)) == 0) { err = iicdev_writeto(sc->dev, ISL12XX_SC_REG, &tregs, sizeof(tregs), WAITFLAGS); sreg &= ~ISL12XX_SR_WRTC; isl12xx_write1(sc, ISL12XX_SR_REG, sreg); } } iicbus_release_bus(sc->busdev, sc->dev); return (err); } static device_method_t isl12xx_methods[] = { /* device_if methods */ DEVMETHOD(device_probe, isl12xx_probe), DEVMETHOD(device_attach, isl12xx_attach), DEVMETHOD(device_detach, isl12xx_detach), /* clock_if methods */ DEVMETHOD(clock_gettime, isl12xx_gettime), DEVMETHOD(clock_settime, isl12xx_settime), DEVMETHOD_END, }; static driver_t isl12xx_driver = { "isl12xx", isl12xx_methods, sizeof(struct isl12xx_softc), }; static devclass_t isl12xx_devclass; DRIVER_MODULE(isl12xx, iicbus, isl12xx_driver, isl12xx_devclass, NULL, NULL); MODULE_VERSION(isl12xx, 1); MODULE_DEPEND(isl12xx, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/nxprtc.c =================================================================== --- stable/12/sys/dev/iicbus/nxprtc.c (revision 356023) +++ stable/12/sys/dev/iicbus/nxprtc.c (revision 356024) @@ -1,1019 +1,1020 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * 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 NXP real-time clock/calendar chips: * - PCF8563 = low power, countdown timer * - PCA8565 = like PCF8563, automotive temperature range * - PCF8523 = low power, countdown timer, oscillator freq tuning, 2 timers * - PCF2127 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, 512B ram * - PCA2129 = like PCF8523, automotive, tcxo, tamper/ts, i2c & spi, (note 1) * - PCF2129 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, (note 1) * * Most chips have a countdown timer, ostensibly intended to generate periodic * interrupt signals on an output pin. The timer is driven from the same * divider chain that clocks the time of day registers, and they start counting * in sync when the STOP bit is cleared after the time and timer registers are * set. The timer register can also be read on the fly, so we use it to count * fractional seconds and get a resolution of ~15ms. * * [1] Note that the datasheets for the PCx2129 chips state that they have only * a watchdog timer, not a countdown timer. Empirical testing shows that the * countdown timer is actually there and it works fine, except that it can't * trigger an interrupt or toggle an output pin like it can on other chips. We * don't care about interrupts and output pins, we just read the timer register * to get better resolution. */ #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 1010 001x : PCA2129 PCF2127 PCF2129 PCF8563 PCF8565 * I2C address 1101 000x : PCF8523 */ #define PCF8563_ADDR 0xa2 #define PCF8523_ADDR 0xd0 /* * Registers, bits within them, and masks that are common to all chip types. */ #define PCF85xx_R_CS1 0x00 /* CS1 and CS2 control regs are in */ #define PCF85xx_R_CS2 0x01 /* the same location on all chips. */ #define PCF85xx_B_CS1_STOP 0x20 /* Stop time incrementing bit */ #define PCF85xx_B_SECOND_OS 0x80 /* Oscillator Stopped bit */ #define PCF85xx_M_SECOND 0x7f /* Masks for all BCD time regs... */ #define PCF85xx_M_MINUTE 0x7f #define PCF85xx_M_12HOUR 0x1f #define PCF85xx_M_24HOUR 0x3f #define PCF85xx_M_DAY 0x3f #define PCF85xx_M_MONTH 0x1f #define PCF85xx_M_YEAR 0xff /* * PCF2127-specific registers, bits, and masks. */ #define PCF2127_R_TMR_CTL 0x10 /* Timer/watchdog control */ #define PCF2127_M_TMR_CTRL 0xe3 /* Mask off undef bits */ #define PCF2127_B_TMR_CD 0x40 /* Run in countdown mode */ #define PCF2127_B_TMR_64HZ 0x01 /* Timer frequency 64Hz */ #define PCF2127_R_TS_CTL 0x12 /* Timestamp control */ #define PCF2127_B_TSOFF 0x40 /* Turn off timestamp function */ #define PCF2127_R_AGING_OFFSET 0x19 /* Frequency aging offset in PPM */ /* * PCA/PCF2129-specific registers, bits, and masks. */ #define PCF2129_B_CS1_12HR 0x04 /* Use 12-hour (AM/PM) mode bit */ #define PCF2129_B_CLKOUT_OTPR 0x20 /* OTP refresh command */ #define PCF2129_B_CLKOUT_HIGHZ 0x07 /* Clock Out Freq = disable */ /* * PCF8523-specific registers, bits, and masks. */ #define PCF8523_R_CS3 0x02 /* Control and status reg 3 */ #define PCF8523_R_SECOND 0x03 /* Seconds */ #define PCF8523_R_TMR_CLKOUT 0x0F /* Timer and clockout control */ #define PCF8523_R_TMR_A_FREQ 0x10 /* Timer A frequency control */ #define PCF8523_R_TMR_A_COUNT 0x11 /* Timer A count */ #define PCF8523_M_TMR_A_FREQ 0x07 /* Mask off undef bits */ #define PCF8523_B_HOUR_PM 0x20 /* PM bit */ #define PCF8523_B_CS1_SOFTRESET 0x58 /* Initiate Soft Reset bits */ #define PCF8523_B_CS1_12HR 0x08 /* Use 12-hour (AM/PM) mode bit */ #define PCF8523_B_CLKOUT_TACD 0x02 /* TimerA runs in CountDown mode */ #define PCF8523_B_CLKOUT_HIGHZ 0x38 /* Clock Out Freq = disable */ #define PCF8523_B_TMR_A_64HZ 0x01 /* Timer A freq 64Hz */ #define PCF8523_M_CS3_PM 0xE0 /* Power mode mask */ #define PCF8523_B_CS3_PM_NOBAT 0xE0 /* PM bits: no battery usage */ #define PCF8523_B_CS3_PM_STD 0x00 /* PM bits: standard */ #define PCF8523_B_CS3_PM_DSNBM 0xa0 /* PM bits: direct switch, no bat mon */ #define PCF8523_B_CS3_BLF 0x04 /* Battery Low Flag bit */ /* * PCF8563-specific registers, bits, and masks. */ #define PCF8563_R_SECOND 0x02 /* Seconds */ #define PCF8563_R_CLKOUT 0x0d /* Clock output control */ #define PCF8563_R_TMR_CTRL 0x0e /* Timer control */ #define PCF8563_R_TMR_COUNT 0x0f /* Timer count */ #define PCF8563_M_TMR_CTRL 0x93 /* Mask off undef bits */ #define PCF8563_B_TMR_ENABLE 0x80 /* Enable countdown timer */ #define PCF8563_B_TMR_64HZ 0x01 /* Timer frequency 64Hz */ #define PCF8563_B_MONTH_C 0x80 /* Century bit */ /* * We use the countdown timer for fractional seconds. We program it for 64 Hz, * the fastest available rate that doesn't roll over in less than a second. */ #define TMR_TICKS_SEC 64 #define TMR_TICKS_HALFSEC 32 /* * The chip types we support. */ enum { TYPE_NONE, TYPE_PCA2129, TYPE_PCA8565, TYPE_PCF2127, TYPE_PCF2129, TYPE_PCF8523, TYPE_PCF8563, TYPE_COUNT }; static const char *desc_strings[] = { "", "NXP PCA2129 RTC", "NXP PCA8565 RTC", "NXP PCF2127 RTC", "NXP PCF2129 RTC", "NXP PCF8523 RTC", "NXP PCF8563 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, day, wday, month, year; }; struct nxprtc_softc { device_t dev; device_t busdev; struct intr_config_hook config_hook; u_int flags; /* SC_F_* flags */ u_int chiptype; /* Type of PCF85xx chip */ time_t bat_time; /* Next time to check battery */ int freqadj; /* Current freq adj in PPM */ uint8_t secaddr; /* Address of seconds register */ uint8_t tmcaddr; /* Address of timer count register */ bool use_timer; /* Use timer for fractional sec */ bool use_ampm; /* Chip is set to use am/pm mode */ bool is212x; /* Chip type is 2127 or 2129 */ }; #define SC_F_CPOL (1 << 0) /* Century bit means 19xx */ /* * When doing i2c IO, indicate that we need to wait for exclusive bus ownership, * but that we should not wait if we already own the bus. This lets us put * iicbus_acquire_bus() calls with a non-recursive wait at the entry of our API * functions to ensure that only one client at a time accesses the hardware for * the entire series of operations it takes to read or write the clock. */ #define WAITFLAGS (IIC_WAIT | IIC_RECURSIVE) /* * 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 nxprtc_compat_data; #else typedef struct { const char *ocd_str; uintptr_t ocd_data; } nxprtc_compat_data; #endif static nxprtc_compat_data compat_data[] = { {"nxp,pca2129", TYPE_PCA2129}, {"nxp,pca8565", TYPE_PCA8565}, {"nxp,pcf2127", TYPE_PCF2127}, {"nxp,pcf2129", TYPE_PCF2129}, {"nxp,pcf8523", TYPE_PCF8523}, {"nxp,pcf8563", TYPE_PCF8563}, /* Undocumented compat strings known to exist in the wild... */ {"pcf8563", TYPE_PCF8563}, {"phg,pcf8563", TYPE_PCF8563}, {"philips,pcf8563", TYPE_PCF8563}, {NULL, TYPE_NONE}, }; static int nxprtc_readfrom(device_t slavedev, uint8_t regaddr, void *buffer, uint16_t buflen, int waithow) { struct iic_msg msg; int err; uint8_t slaveaddr; /* * Two transfers back to back with a stop and start between them; first we * write the address-within-device, then we read from the device. This * is used instead of the standard iicdev_readfrom() because some of the * chips we service don't support i2c repeat-start operations (grrrrr) * so we do two completely separate transfers with a full stop between. */ slaveaddr = iicbus_get_addr(slavedev); msg.slave = slaveaddr; msg.flags = IIC_M_WR; msg.len = 1; msg.buf = ®addr; if ((err = iicbus_transfer_excl(slavedev, &msg, 1, waithow)) != 0) return (err); msg.slave = slaveaddr; msg.flags = IIC_M_RD; msg.len = buflen; msg.buf = buffer; return (iicbus_transfer_excl(slavedev, &msg, 1, waithow)); } static int read_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t *val) { return (nxprtc_readfrom(sc->dev, reg, val, sizeof(*val), WAITFLAGS)); } static int write_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t val) { return (iicdev_writeto(sc->dev, reg, &val, sizeof(val), WAITFLAGS)); } static int read_timeregs(struct nxprtc_softc *sc, struct time_regs *tregs, uint8_t *tmr) { int err; uint8_t sec, tmr1, tmr2; /* * The datasheet says loop to read the same timer value twice because it * does not freeze while reading. To that we add our own logic that * the seconds register must be the same before and after reading the * timer, ensuring the fractional part is from the same second as tregs. */ do { if (sc->use_timer) { if ((err = read_reg(sc, sc->secaddr, &sec)) != 0) break; if ((err = read_reg(sc, sc->tmcaddr, &tmr1)) != 0) break; if ((err = read_reg(sc, sc->tmcaddr, &tmr2)) != 0) break; if (tmr1 != tmr2) continue; } if ((err = nxprtc_readfrom(sc->dev, sc->secaddr, tregs, sizeof(*tregs), WAITFLAGS)) != 0) break; } while (sc->use_timer && tregs->sec != sec); /* * If the timer value is greater than our hz rate (or is zero), * something is wrong. Maybe some other OS used the timer differently? * Just set it to zero. Likewise if we're not using the timer. After * the offset calc below, the zero turns into 32, the mid-second point, * which in effect performs 4/5 rounding, which is just the right thing * to do if we don't have fine-grained time. */ if (!sc->use_timer || tmr1 > TMR_TICKS_SEC) tmr1 = 0; /* * Turn the downcounter into an upcounter. The timer starts counting at * and rolls over at mid-second, so add half a second worth of ticks to * get its zero point back in sync with the tregs.sec rollover. */ *tmr = (TMR_TICKS_SEC - tmr1 + TMR_TICKS_HALFSEC) % TMR_TICKS_SEC; return (err); } static int write_timeregs(struct nxprtc_softc *sc, struct time_regs *tregs) { return (iicdev_writeto(sc->dev, sc->secaddr, tregs, sizeof(*tregs), WAITFLAGS)); } static int freqadj_sysctl(SYSCTL_HANDLER_ARGS) { struct nxprtc_softc *sc; int err, freqppm, newppm; sc = arg1; /* PPM range [-7,8] maps to reg value range [0,15] */ freqppm = newppm = 8 - sc->freqadj; err = sysctl_handle_int(oidp, &newppm, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (freqppm != newppm) { if (newppm < -7 || newppm > 8) return (EINVAL); sc->freqadj = 8 - newppm; err = write_reg(sc, PCF2127_R_AGING_OFFSET, sc->freqadj); } return (err); } static int pcf8523_battery_check(struct nxprtc_softc *sc) { struct timespec ts; int err; uint8_t cs3; /* We check the battery when starting up, and then only once a day. */ getnanouptime(&ts); if (ts.tv_sec < sc->bat_time) return (0); sc->bat_time = ts.tv_sec + (60 * 60 * 24); /* * The 8523, 2127, and 2129 chips have a "power manager" which includes * an optional battery voltage monitor and several choices for power * switching modes. The battery monitor uses a lot of current and it * remains active when running from battery, making it the "drain my * battery twice as fast" mode. So, we run the chip in direct-switching * mode with the battery monitor disabled, reducing the current draw * when running on battery from 1930nA to 880nA. While it's not clear * from the datasheets, empirical testing shows that both disabling the * battery monitor and using direct-switch mode are required to get the * full power savings. * * There isn't any need to continuously monitor the battery voltage, so * this function is used to periodically enable the monitor, check the * voltage, then return to the low-power monitor-disabled mode. */ err = write_reg(sc, PCF8523_R_CS3, PCF8523_B_CS3_PM_STD); if (err != 0) { device_printf(sc->dev, "cannot write CS3 reg\n"); return (err); } pause_sbt("nxpbat", mstosbt(10), 0, 0); if ((err = read_reg(sc, PCF8523_R_CS3, &cs3)) != 0) { device_printf(sc->dev, "cannot read CS3 reg\n"); return (err); } err = write_reg(sc, PCF8523_R_CS3, PCF8523_B_CS3_PM_DSNBM); if (err != 0) { device_printf(sc->dev, "cannot write CS3 reg\n"); return (err); } if (cs3 & PCF8523_B_CS3_BLF) device_printf(sc->dev, "WARNING: RTC battery is low\n"); return (0); } static int pcf8523_start(struct nxprtc_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; struct csr { uint8_t cs1; uint8_t cs2; uint8_t cs3; uint8_t sec; } csr; int err; uint8_t clkout, freqadj; /* Read the control and status registers. */ if ((err = nxprtc_readfrom(sc->dev, PCF85xx_R_CS1, &csr, sizeof(csr), WAITFLAGS)) != 0){ device_printf(sc->dev, "cannot read RTC control regs\n"); return (err); } /* * Do a full init if... * - The chip power manager is in battery-disable mode. * - The OS (oscillator stopped) bit is set (all power was lost). * - The clock-increment STOP flag is set (this is just insane). */ if ((csr.cs3 & PCF8523_M_CS3_PM) == PCF8523_B_CS3_PM_NOBAT || (csr.cs1 & PCF85xx_B_CS1_STOP) || (csr.sec & PCF85xx_B_SECOND_OS)) { device_printf(sc->dev, "WARNING: RTC battery failed; time is invalid\n"); /* * For 212x series... * - Turn off the POR-Override bit (used for mfg test only), * by writing zero to cs 1 (all other bits power on as zero). * - Turn off the timestamp option to save the power used to * monitor that input pin. * - Trigger OTP refresh by forcing the OTPR bit to zero then * back to 1, then wait 100ms for the refresh. */ if (sc->is212x) { err = write_reg(sc, PCF85xx_R_CS1, 0); if (err != 0) { device_printf(sc->dev, "cannot write CS1 reg\n"); return (err); } err = write_reg(sc, PCF2127_R_TS_CTL, PCF2127_B_TSOFF); if (err != 0) { device_printf(sc->dev, "cannot write timestamp control\n"); return (err); } clkout = PCF2129_B_CLKOUT_HIGHZ; err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout); if (err == 0) err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout | PCF2129_B_CLKOUT_OTPR); if (err != 0) { device_printf(sc->dev, "cannot write CLKOUT control\n"); return (err); } pause_sbt("nxpotp", mstosbt(100), mstosbt(10), 0); } else clkout = PCF8523_B_CLKOUT_HIGHZ; /* All chips: set clock output pin to high-z to save power */ if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout)) != 0) { device_printf(sc->dev, "cannot write CLKOUT control\n"); return (err); } } /* * Check the battery voltage and report if it's low. This also has the * side effect of (re-)initializing the power manager to low-power mode * when we come up after a power fail. */ pcf8523_battery_check(sc); /* * Remember whether we're running in AM/PM mode. The chip default is * 24-hour mode, but if we're co-existing with some other OS that * prefers AM/PM we can run that way too. * * Also, for 212x chips, retrieve the current frequency aging offset, * and set up the sysctl handler for reading/setting it. */ if (sc->is212x) { if (csr.cs1 & PCF2129_B_CS1_12HR) sc->use_ampm = true; err = read_reg(sc, PCF2127_R_AGING_OFFSET, &freqadj); if (err != 0) { device_printf(sc->dev, "cannot read AGINGOFFSET register\n"); return (err); } sc->freqadj = (int8_t)freqadj; ctx = device_get_sysctl_ctx(sc->dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "freqadj", CTLFLAG_RWTUN | CTLTYPE_INT | CTLFLAG_MPSAFE, sc, 0, freqadj_sysctl, "I", "Frequency adjust in PPM, range [-7,+8]"); } else { if (csr.cs1 & PCF8523_B_CS1_12HR) sc->use_ampm = true; } return (0); } static int pcf8523_start_timer(struct nxprtc_softc *sc) { int err; uint8_t clkout, stdclk, stdfreq, tmrfreq; /* * Read the timer control and frequency regs. If they don't have the * values we normally program into them then the timer count doesn't * contain a valid fractional second, so zero it to prevent using a bad * value. Then program the normal timer values so that on the first * settime call we'll begin to use fractional time. */ if ((err = read_reg(sc, PCF8523_R_TMR_A_FREQ, &tmrfreq)) != 0) return (err); if ((err = read_reg(sc, PCF8523_R_TMR_CLKOUT, &clkout)) != 0) return (err); stdfreq = PCF8523_B_TMR_A_64HZ; stdclk = PCF8523_B_CLKOUT_TACD | PCF8523_B_CLKOUT_HIGHZ; if (clkout != stdclk || (tmrfreq & PCF8523_M_TMR_A_FREQ) != stdfreq) { if ((err = write_reg(sc, sc->tmcaddr, 0)) != 0) return (err); if ((err = write_reg(sc, PCF8523_R_TMR_A_FREQ, stdfreq)) != 0) return (err); if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, stdclk)) != 0) return (err); } return (0); } static int pcf2127_start_timer(struct nxprtc_softc *sc) { int err; uint8_t stdctl, tmrctl; /* * Set up timer if it's not already in the mode we normally run it. See * the comment in pcf8523_start_timer() for more details. * * Note that the PCF2129 datasheet says it has no countdown timer, but * empirical testing shows that it works just fine for our purposes. */ if ((err = read_reg(sc, PCF2127_R_TMR_CTL, &tmrctl)) != 0) return (err); stdctl = PCF2127_B_TMR_CD | PCF8523_B_TMR_A_64HZ; if ((tmrctl & PCF2127_M_TMR_CTRL) != stdctl) { if ((err = write_reg(sc, sc->tmcaddr, 0)) != 0) return (err); if ((err = write_reg(sc, PCF2127_R_TMR_CTL, stdctl)) != 0) return (err); } return (0); } static int pcf8563_start(struct nxprtc_softc *sc) { struct csr { uint8_t cs1; uint8_t cs2; uint8_t sec; } csr; int err; /* Read the control and status registers. */ if ((err = nxprtc_readfrom(sc->dev, PCF85xx_R_CS1, &csr, sizeof(csr), WAITFLAGS)) != 0){ device_printf(sc->dev, "cannot read RTC control regs\n"); return (err); } /* * Do a full init if... * - The OS (oscillator stopped) bit is set (all power was lost). This * bit is called VL (Voltage Low) in the 8563 datasheet. * - The clock-increment STOP flag is set (this is just insane). */ if ((csr.cs1 & PCF85xx_B_CS1_STOP) || (csr.sec & PCF85xx_B_SECOND_OS)) { device_printf(sc->dev, "WARNING: RTC battery failed; time is invalid\n"); /* * - Turn off the POR-Override bit (used for mfg test only), by * writing zero to cs 1 (all other bits power on as zero). * - Turn off the clock output pin (to save battery power), by * writing zero to the clkout control reg. */ if ((err = write_reg(sc, PCF85xx_R_CS1, 0)) != 0) { device_printf(sc->dev, "cannot write CS1 reg\n"); return (err); } if ((err = write_reg(sc, PCF8563_R_CLKOUT, 0)) != 0) { device_printf(sc->dev, "cannot write CS1 reg\n"); return (err); } } return (0); } static int pcf8563_start_timer(struct nxprtc_softc *sc) { int err; uint8_t stdctl, tmrctl; /* See comment in pcf8523_start_timer(). */ if ((err = read_reg(sc, PCF8563_R_TMR_CTRL, &tmrctl)) != 0) return (err); stdctl = PCF8563_B_TMR_ENABLE | PCF8563_B_TMR_64HZ; if ((tmrctl & PCF8563_M_TMR_CTRL) != stdctl) { if ((err = write_reg(sc, sc->tmcaddr, 0)) != 0) return (err); if ((err = write_reg(sc, PCF8563_R_TMR_CTRL, stdctl)) != 0) return (err); } return (0); } static void nxprtc_start(void *dev) { struct nxprtc_softc *sc; int clockflags, resolution; sc = device_get_softc((device_t)dev); config_intrhook_disestablish(&sc->config_hook); /* First do chip-specific inits. */ switch (sc->chiptype) { case TYPE_PCA2129: case TYPE_PCF2129: case TYPE_PCF2127: sc->is212x = true; if (pcf8523_start(sc) != 0) return; if (pcf2127_start_timer(sc) != 0) { device_printf(sc->dev, "cannot set up timer\n"); return; } break; case TYPE_PCF8523: if (pcf8523_start(sc) != 0) return; if (pcf8523_start_timer(sc) != 0) { device_printf(sc->dev, "cannot set up timer\n"); return; } break; case TYPE_PCA8565: case TYPE_PCF8563: if (pcf8563_start(sc) != 0) return; if (pcf8563_start_timer(sc) != 0) { device_printf(sc->dev, "cannot set up timer\n"); return; } break; default: device_printf(sc->dev, "missing init code for this chiptype\n"); return; } /* * Everything looks good if we make it to here; register as an RTC. If * we're using the timer to count fractional seconds, our resolution is * 1e6/64, about 15.6ms. Without the timer we still align the RTC clock * when setting it so our error is an average .5s when reading it. * Schedule our clock_settime() method to be called at a .495ms offset * into the second, because the clock hardware resets the divider chain * to the mid-second point when you set the time and it takes about 5ms * of i2c bus activity to set the clock. */ resolution = sc->use_timer ? 1000000 / TMR_TICKS_SEC : 1000000 / 2; clockflags = CLOCKF_GETTIME_NO_ADJ | CLOCKF_SETTIME_NO_TS; clock_register_flags(sc->dev, resolution, clockflags); clock_schedule(sc->dev, 495000000); } static int nxprtc_gettime(device_t dev, struct timespec *ts) { struct bcd_clocktime bct; struct time_regs tregs; struct nxprtc_softc *sc; int err; uint8_t cs1, hourmask, tmrcount; sc = device_get_softc(dev); /* * Read the time, but before using it, validate that the oscillator- * stopped/power-fail bit is not set, and that the time-increment STOP * bit is not set in the control reg. The latter can happen if there * was an error when setting the time. */ if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) == 0) { if ((err = read_timeregs(sc, &tregs, &tmrcount)) == 0) { err = read_reg(sc, PCF85xx_R_CS1, &cs1); } iicbus_release_bus(sc->busdev, sc->dev); } if (err != 0) return (err); if ((tregs.sec & PCF85xx_B_SECOND_OS) || (cs1 & PCF85xx_B_CS1_STOP)) { device_printf(dev, "RTC clock not running\n"); return (EINVAL); /* hardware is good, time is not. */ } if (sc->use_ampm) hourmask = PCF85xx_M_12HOUR; else hourmask = PCF85xx_M_24HOUR; bct.nsec = ((uint64_t)tmrcount * 1000000000) / TMR_TICKS_SEC; bct.ispm = (tregs.hour & PCF8523_B_HOUR_PM) != 0; bct.sec = tregs.sec & PCF85xx_M_SECOND; bct.min = tregs.min & PCF85xx_M_MINUTE; bct.hour = tregs.hour & hourmask; bct.day = tregs.day & PCF85xx_M_DAY; bct.mon = tregs.month & PCF85xx_M_MONTH; bct.year = tregs.year & PCF85xx_M_YEAR; /* * Old PCF8563 datasheets recommended that the C bit be 1 for 19xx and 0 * for 20xx; newer datasheets don't recommend that. We don't care, * but we may co-exist with other OSes sharing the hardware. Determine * existing polarity on a read so that we can preserve it on a write. */ if (sc->chiptype == TYPE_PCF8563) { if (tregs.month & PCF8563_B_MONTH_C) { if (bct.year < 0x70) sc->flags |= SC_F_CPOL; } else if (bct.year >= 0x70) sc->flags |= SC_F_CPOL; } clock_dbgprint_bcd(sc->dev, CLOCK_DBG_READ, &bct); err = clock_bcd_to_ts(&bct, ts, sc->use_ampm); ts->tv_sec += utc_offset(); return (err); } static int nxprtc_settime(device_t dev, struct timespec *ts) { struct bcd_clocktime bct; struct time_regs tregs; struct nxprtc_softc *sc; int err; uint8_t cflag, cs1; sc = device_get_softc(dev); /* * We stop the clock, set the time, then restart the clock. Half a * second after restarting the clock it ticks over to the next second. * So to align the RTC, we schedule this function to be called when * system time is roughly halfway (.495) through the current second. * * Reserve use of the i2c bus and stop the RTC clock. Note that if * anything goes wrong from this point on, we leave the clock stopped, * because we don't really know what state it's in. */ if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) != 0) return (err); if ((err = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0) goto errout; cs1 |= PCF85xx_B_CS1_STOP; if ((err = write_reg(sc, PCF85xx_R_CS1, cs1)) != 0) goto errout; /* Grab a fresh post-sleep idea of what time it is. */ getnanotime(ts); ts->tv_sec -= utc_offset(); ts->tv_nsec = 0; clock_ts_to_bcd(ts, &bct, sc->use_ampm); clock_dbgprint_bcd(sc->dev, CLOCK_DBG_WRITE, &bct); /* On 8563 set the century based on the polarity seen when reading. */ cflag = 0; if (sc->chiptype == TYPE_PCF8563) { if ((sc->flags & SC_F_CPOL) != 0) { if (bct.year >= 0x2000) cflag = PCF8563_B_MONTH_C; } else if (bct.year < 0x2000) cflag = PCF8563_B_MONTH_C; } tregs.sec = bct.sec; tregs.min = bct.min; tregs.hour = bct.hour | (bct.ispm ? PCF8523_B_HOUR_PM : 0); tregs.day = bct.day; tregs.month = bct.mon; tregs.year = (bct.year & 0xff) | cflag; tregs.wday = bct.dow; /* * Set the time, reset the timer count register, then start the clocks. */ if ((err = write_timeregs(sc, &tregs)) != 0) goto errout; if ((err = write_reg(sc, sc->tmcaddr, TMR_TICKS_SEC)) != 0) return (err); cs1 &= ~PCF85xx_B_CS1_STOP; err = write_reg(sc, PCF85xx_R_CS1, cs1); /* * Check for battery-low. The actual check is throttled to only occur * once a day, mostly to avoid spamming the user with frequent warnings. */ pcf8523_battery_check(sc); errout: iicbus_release_bus(sc->busdev, sc->dev); if (err != 0) device_printf(dev, "cannot write RTC time\n"); return (err); } static int nxprtc_get_chiptype(device_t dev) { #ifdef FDT return (ofw_bus_search_compatible(dev, compat_data)->ocd_data); #else nxprtc_compat_data *cdata; const char *htype; int chiptype; /* * If given a chiptype hint string, loop through the ofw compat data * comparing the hinted chip type to the compat strings. The table end * marker ocd_data is TYPE_NONE. */ if (resource_string_value(device_get_name(dev), device_get_unit(dev), "compatible", &htype) == 0) { for (cdata = compat_data; cdata->ocd_str != NULL; ++cdata) { if (strcmp(htype, cdata->ocd_str) == 0) break; } chiptype = cdata->ocd_data; } else chiptype = TYPE_NONE; /* * On non-FDT systems the historical behavior of this driver was to * assume a PCF8563; keep doing that for compatibility. */ if (chiptype == TYPE_NONE) return (TYPE_PCF8563); else return (chiptype); #endif } static int nxprtc_probe(device_t dev) { int chiptype, rv; #ifdef FDT if (!ofw_bus_status_okay(dev)) return (ENXIO); rv = BUS_PROBE_GENERIC; #else rv = BUS_PROBE_NOWILDCARD; #endif if ((chiptype = nxprtc_get_chiptype(dev)) == TYPE_NONE) return (ENXIO); device_set_desc(dev, desc_strings[chiptype]); return (rv); } static int nxprtc_attach(device_t dev) { struct nxprtc_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. */ sc->chiptype = nxprtc_get_chiptype(dev); /* The features and some register addresses vary by chip type. */ switch (sc->chiptype) { case TYPE_PCA2129: case TYPE_PCF2129: case TYPE_PCF2127: case TYPE_PCF8523: sc->secaddr = PCF8523_R_SECOND; sc->tmcaddr = PCF8523_R_TMR_A_COUNT; sc->use_timer = true; break; case TYPE_PCA8565: case TYPE_PCF8563: sc->secaddr = PCF8563_R_SECOND; sc->tmcaddr = PCF8563_R_TMR_COUNT; sc->use_timer = true; break; default: device_printf(dev, "impossible: cannot determine chip type\n"); return (ENXIO); } /* * We have to wait until interrupts are enabled. Sometimes I2C read * and write only works when the interrupts are available. */ sc->config_hook.ich_func = nxprtc_start; sc->config_hook.ich_arg = dev; if (config_intrhook_establish(&sc->config_hook) != 0) return (ENOMEM); return (0); } static int nxprtc_detach(device_t dev) { clock_unregister(dev); return (0); } static device_method_t nxprtc_methods[] = { DEVMETHOD(device_probe, nxprtc_probe), DEVMETHOD(device_attach, nxprtc_attach), DEVMETHOD(device_detach, nxprtc_detach), DEVMETHOD(clock_gettime, nxprtc_gettime), DEVMETHOD(clock_settime, nxprtc_settime), DEVMETHOD_END }; static driver_t nxprtc_driver = { "nxprtc", nxprtc_methods, sizeof(struct nxprtc_softc), }; static devclass_t nxprtc_devclass; DRIVER_MODULE(nxprtc, iicbus, nxprtc_driver, nxprtc_devclass, NULL, NULL); MODULE_VERSION(nxprtc, 1); MODULE_DEPEND(nxprtc, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/rtc8583.c =================================================================== --- stable/12/sys/dev/iicbus/rtc8583.c (revision 356023) +++ stable/12/sys/dev/iicbus/rtc8583.c (revision 356024) @@ -1,303 +1,305 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * * Copyright (c) 2017 Hiroki Mori. All rights reserved. - * Copyright (c) 2017 Ian Lepore. All rights reserved. + * Copyright (c) 2017 Ian Lepore. * * 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. * * This code base on isl12xx.c */ #include __FBSDID("$FreeBSD$"); /* * Driver for realtime clock EPSON RTC-8583 */ #include "opt_platform.h" #include #include #include #include #include #include #include #ifdef FDT #include #include #endif #include #include #include "clock_if.h" #include "iicbus_if.h" #define RTC8583_SC_REG 0x01 /* RTC Seconds */ #define RTC8583_USERSRAM_REG 0x10 /* User SRAM register (first) */ #define MAX_TRANSFER 16 /* * A struct laid out in the same order as the time registers in the chip. */ struct time_regs { uint8_t msec, sec, min, hour, day, month; }; struct rtc8583_softc { device_t dev; device_t busdev; struct intr_config_hook init_hook; }; #ifdef FDT static struct ofw_compat_data compat_data[] = { {"epson,rtc8583", 1}, {NULL, 0}, }; #endif static void rtc8583_init(void *arg); static int rtc8583_probe(device_t dev); static int rtc8583_attach(device_t dev); static int rtc8583_detach(device_t dev); static int rtc8583_gettime(device_t dev, struct timespec *ts); static int rtc8583_settime(device_t dev, struct timespec *ts); static int rtc8583_writeto(device_t slavedev, uint8_t regaddr, void *buffer, uint16_t buflen, int waithow); /* Implementation */ static int rtc8583_writeto(device_t slavedev, uint8_t regaddr, void *buffer, uint16_t buflen, int waithow) { struct iic_msg msgs; uint8_t slaveaddr; uint8_t newbuf[MAX_TRANSFER]; slaveaddr = iicbus_get_addr(slavedev); newbuf[0] = regaddr; memcpy(newbuf + 1, buffer, buflen); msgs.slave = slaveaddr; msgs.flags = IIC_M_WR; msgs.len = 1 + buflen; msgs.buf = newbuf; return (iicbus_transfer_excl(slavedev, &msgs, 1, waithow)); } static inline int rtc8583_read1(struct rtc8583_softc *sc, uint8_t reg, uint8_t *data) { return (iicdev_readfrom(sc->dev, reg, data, 1, IIC_WAIT)); } static inline int rtc8583_write1(struct rtc8583_softc *sc, uint8_t reg, uint8_t val) { return (rtc8583_writeto(sc->dev, reg, &val, 1, IIC_WAIT)); } static void rtc8583_init(void *arg) { struct rtc8583_softc *sc; sc = (struct rtc8583_softc*)arg; config_intrhook_disestablish(&sc->init_hook); /* * Register as a system realtime clock. */ clock_register_flags(sc->dev, 1000000, CLOCKF_SETTIME_NO_ADJ); clock_schedule(sc->dev, 1); return; } static int rtc8583_probe(device_t dev) { #ifdef FDT if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "EPSON RTC-8583"); return (BUS_PROBE_DEFAULT); } #endif return (ENXIO); } static int rtc8583_attach(device_t dev) { struct rtc8583_softc *sc; sc = device_get_softc(dev); sc->dev = dev; sc->busdev = device_get_parent(sc->dev); /* * Chip init must wait until interrupts are enabled. Often i2c access * works only when the interrupts are available. */ sc->init_hook.ich_func = rtc8583_init; sc->init_hook.ich_arg = sc; if (config_intrhook_establish(&sc->init_hook) != 0) return (ENOMEM); return (0); } static int rtc8583_detach(device_t dev) { clock_unregister(dev); return (0); } static int rtc8583_gettime(device_t dev, struct timespec *ts) { struct rtc8583_softc *sc; struct bcd_clocktime bct; struct time_regs tregs; uint8_t y, ytmp, sreg; int err; sc = device_get_softc(dev); /* Read the bcd time registers. */ if ((err = iicdev_readfrom(sc->dev, RTC8583_SC_REG, &tregs, sizeof(tregs), IIC_WAIT)) != 0) return (err); y = tregs.day >> 6; /* Get year from user SRAM */ rtc8583_read1(sc, RTC8583_USERSRAM_REG, &sreg); /* * Check if year adjustment is required. * RTC has only 2 bits for year value (i.e. maximum is 4 years), so * full year value is stored in user SRAM and updated manually or * by this code. */ ytmp = sreg & 0x03; if (ytmp != y) { /* shift according to difference */ sreg += y - ytmp; /* check if overflow happened */ if (ytmp > y) sreg += 4; if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) != 0) return (err); rtc8583_write1(sc, RTC8583_USERSRAM_REG, sreg); iicbus_release_bus(sc->busdev, sc->dev); } if (!validbcd(tregs.msec)) return (EINVAL); /* The 'msec' reg is actually 1/100ths, in bcd. */ bct.nsec = bcd2bin(tregs.msec) * 10 * 1000 * 1000; bct.sec = tregs.sec; bct.min = tregs.min; bct.hour = tregs.hour & 0x3f; bct.day = tregs.day & 0x3f; bct.mon = tregs.month & 0x1f; bct.year = bin2bcd(sreg % 100); clock_dbgprint_bcd(sc->dev, CLOCK_DBG_READ, &bct); return (clock_bcd_to_ts(&bct, ts, false)); } static int rtc8583_settime(device_t dev, struct timespec *ts) { struct rtc8583_softc *sc; struct bcd_clocktime bct; struct time_regs tregs; uint8_t sreg; int err; sc = device_get_softc(dev); ts->tv_sec -= utc_offset(); clock_ts_to_bcd(ts, &bct, false); clock_dbgprint_bcd(sc->dev, CLOCK_DBG_WRITE, &bct); /* The 'msec' reg is actually 1/100ths, in bcd. */ tregs.msec = bin2bcd(ts->tv_nsec / (10 * 1000 * 1000)); tregs.sec = bct.sec; tregs.min = bct.min; tregs.hour = bct.hour; tregs.day = bct.day | (bct.year & 0x03 << 6); tregs.month = bct.mon; if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) != 0) return (err); err = rtc8583_writeto(sc->dev, RTC8583_SC_REG, &tregs, sizeof(tregs), IIC_WAIT); sreg = bcd2bin(bct.year & 0xff); /* save to year to sram */ rtc8583_write1(sc, RTC8583_USERSRAM_REG, sreg); iicbus_release_bus(sc->busdev, sc->dev); return (err); } static device_method_t rtc8583_methods[] = { /* device_if methods */ DEVMETHOD(device_probe, rtc8583_probe), DEVMETHOD(device_attach, rtc8583_attach), DEVMETHOD(device_detach, rtc8583_detach), /* clock_if methods */ DEVMETHOD(clock_gettime, rtc8583_gettime), DEVMETHOD(clock_settime, rtc8583_settime), DEVMETHOD_END, }; static driver_t rtc8583_driver = { "rtc8583", rtc8583_methods, sizeof(struct rtc8583_softc), }; static devclass_t rtc8583_devclass; DRIVER_MODULE(rtc8583, iicbus, rtc8583_driver, rtc8583_devclass, NULL, NULL); MODULE_VERSION(rtc8583, 1); MODULE_DEPEND(rtc8583, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12 =================================================================== --- stable/12 (revision 356023) +++ stable/12 (revision 356024) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r355685