Index: head/sys/dev/iicbus/nxprtc.c =================================================================== --- head/sys/dev/iicbus/nxprtc.c (revision 321726) +++ head/sys/dev/iicbus/nxprtc.c (revision 321727) @@ -1,787 +1,801 @@ /*- * 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, no timer * - PCF2129 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, no timer * * 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. */ #include "opt_platform.h" #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_HOUR 0x3f +#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 */ /* * 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_BLF 0x04 /* Battery Low Flag bit */ /* * PCF8563-specific registers, bits, and masks. */ #define PCF8563_R_SECOND 0x02 /* Seconds */ #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 */ uint8_t secaddr; /* Address of seconds register */ uint8_t tmcaddr; /* Address of timer count register */ uint8_t slave_addr; /* PCF85xx slave address */ bool use_timer; /* Use timer for fractional sec */ }; #define SC_F_CPOL (1 << 0) /* Century bit means 19xx */ #define SC_F_AMPM (1 << 1) /* Use PM flag in hours reg */ #ifdef FDT static struct ofw_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}, }; #endif static int read_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t *val) { return (iicdev_readfrom(sc->dev, reg, val, sizeof(*val), IIC_WAIT)); } static int write_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t val) { return (iicdev_writeto(sc->dev, reg, &val, sizeof(val), IIC_WAIT)); } 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 = iicdev_readfrom(sc->dev, sc->secaddr, tregs, sizeof(*tregs), IIC_WAIT)) != 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), IIC_WAIT)); } static int pcf8523_start(struct nxprtc_softc *sc) { int err; uint8_t cs1, cs3, clkout; bool is2129; is2129 = (sc->chiptype == TYPE_PCA2129 || sc->chiptype == TYPE_PCF2129); /* Read and sanity-check the control registers. */ if ((err = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0) { device_printf(sc->dev, "cannot read RTC CS1 control\n"); return (err); } if ((err = read_reg(sc, PCF8523_R_CS3, &cs3)) != 0) { device_printf(sc->dev, "cannot read RTC CS3 control\n"); return (err); } /* * Do a full init (soft-reset) if... * - The chip is in battery-disable mode (fresh from the factory). * - The clock-increment STOP flag is set (this is just insane). * After reset, battery disable mode has to be overridden to "standard" * mode. Also, turn off clock output to save battery power. */ if ((cs3 & PCF8523_M_CS3_PM) == PCF8523_B_CS3_PM_NOBAT || (cs1 & PCF85xx_B_CS1_STOP)) { cs1 = PCF8523_B_CS1_SOFTRESET; if ((err = write_reg(sc, PCF85xx_R_CS1, cs1)) != 0) { device_printf(sc->dev, "cannot write CS1 control\n"); return (err); } cs3 = PCF8523_B_CS3_PM_STD; if ((err = write_reg(sc, PCF8523_R_CS3, cs3)) != 0) { device_printf(sc->dev, "cannot write CS3 control\n"); return (err); } /* * For 2129 series, trigger OTP refresh by forcing the OTPR bit * to zero then back to 1, then wait 100ms for the refresh, and * finally set the bit back to zero with the COF_HIGHZ write. */ if (is2129) { clkout = PCF2129_B_CLKOUT_HIGHZ; if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout)) != 0) { device_printf(sc->dev, "cannot write CLKOUT control\n"); return (err); } if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout | PCF2129_B_CLKOUT_OTPR)) != 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; if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout)) != 0) { device_printf(sc->dev, "cannot write CLKOUT control\n"); return (err); } device_printf(sc->dev, "first time startup, enabled RTC battery operation\n"); /* * Sleep briefly so the battery monitor can make a measurement, * then re-read CS3 so battery-low status can be reported below. */ pause_sbt("nxpbat", mstosbt(100), 0, 0); if ((err = read_reg(sc, PCF8523_R_CS3, &cs3)) != 0) { device_printf(sc->dev, "cannot read RTC CS3 control\n"); return (err); } } /* Let someone know if the battery is weak. */ if (cs3 & PCF8523_B_CS3_BLF) device_printf(sc->dev, "WARNING: RTC battery is low\n"); /* Remember whether we're running in AM/PM mode. */ if (is2129) { if (cs1 & PCF2129_B_CS1_12HR) sc->flags |= SC_F_AMPM; } else { if (cs1 & PCF8523_B_CS1_12HR) sc->flags |= SC_F_AMPM; } 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; /* See comment in pcf8523_start_timer(). */ 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_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; uint8_t sec; 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: if (pcf8523_start(sc) != 0) return; /* No timer to start */ break; case TYPE_PCF2127: if (pcf8523_start(sc) != 0) return; if (pcf2127_start_timer(sc) != 0) return; break; case TYPE_PCF8523: if (pcf8523_start(sc) != 0) return; if (pcf8523_start_timer(sc) != 0) return; break; case TYPE_PCA8565: case TYPE_PCF8563: if (pcf8563_start_timer(sc) != 0) return; break; default: device_printf(sc->dev, "missing init code for this chiptype\n"); return; } /* * Common init. Read the seconds register so we can check the * oscillator-stopped status bit in it. */ if (read_reg(sc, sc->secaddr, &sec) != 0) { device_printf(sc->dev, "cannot read RTC seconds\n"); return; } if ((sec & PCF85xx_B_SECOND_OS) != 0) { device_printf(sc->dev, "WARNING: RTC battery failed; time is invalid\n"); } /* * 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. */ 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); } static int nxprtc_gettime(device_t dev, struct timespec *ts) { struct clocktime ct; struct time_regs tregs; struct nxprtc_softc *sc; int err; - uint8_t cs1, tmrcount; + 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 = read_timeregs(sc, &tregs, &tmrcount)) != 0) { device_printf(dev, "cannot read RTC time\n"); return (err); } if ((err = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0) { device_printf(dev, "cannot read RTC time\n"); 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->flags & SC_F_AMPM) + hourmask = PCF85xx_M_12HOUR; + else + hourmask = PCF85xx_M_24HOUR; + ct.nsec = ((uint64_t)tmrcount * 1000000000) / TMR_TICKS_SEC; ct.sec = FROMBCD(tregs.sec & PCF85xx_M_SECOND); ct.min = FROMBCD(tregs.min & PCF85xx_M_MINUTE); - ct.hour = FROMBCD(tregs.hour & PCF85xx_M_HOUR); + ct.hour = FROMBCD(tregs.hour & hourmask); ct.day = FROMBCD(tregs.day & PCF85xx_M_DAY); ct.mon = FROMBCD(tregs.month & PCF85xx_M_MONTH); ct.year = FROMBCD(tregs.year & PCF85xx_M_YEAR); ct.year += 1900; if (ct.year < POSIX_BASE_YEAR) ct.year += 100; /* assume [1970, 2069] */ /* * 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 (ct.year >= 2000) sc->flags |= SC_F_CPOL; } else if (ct.year < 2000) sc->flags |= SC_F_CPOL; } /* If this chip is running in 12-hour/AMPM mode, deal with it. */ - if ((sc->flags & SC_F_AMPM) && (tregs.hour & PCF8523_B_HOUR_PM)) - ct.hour += 12; + if (sc->flags & SC_F_AMPM) { + if (ct.hour == 12) + ct.hour = 0; + if (tregs.hour & PCF8523_B_HOUR_PM) + ct.hour += 12; + } err = clock_ct_to_ts(&ct, ts); ts->tv_sec += utc_offset(); return (err); } static int nxprtc_settime(device_t dev, struct timespec *ts) { struct clocktime ct; struct time_regs tregs; struct nxprtc_softc *sc; long waitns; int err; uint8_t cflag, cs1, pmflag; 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, sleep until system time is halfway through the * current second (shoot for .495 to allow time for i2c operations). */ getnanotime(ts); waitns = 495000000 - ts->tv_nsec; if (waitns < 0) waitns += 1000000000; pause_sbt("nxpset", nstosbt(waitns), 0, C_PREL(31)); /* * 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_ct(ts, &ct); /* If the chip is in AMPM mode deal with the PM flag. */ pmflag = 0; - if ((sc->flags & SC_F_AMPM) && ct.hour > 12) { - ct.hour -= 12; - pmflag = PCF8523_B_HOUR_PM; + if (sc->flags & SC_F_AMPM) { + if (ct.hour >= 12) { + ct.hour -= 12; + pmflag = PCF8523_B_HOUR_PM; + } + if (ct.hour == 0) + ct.hour = 12; } /* 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 (ct.year >= 2000) cflag = PCF8563_B_MONTH_C; } else if (ct.year < 2000) cflag = PCF8563_B_MONTH_C; } 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); tregs.year = TOBCD(ct.year % 100) | cflag; tregs.wday = ct.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); errout: iicbus_release_bus(sc->busdev, sc->dev); if (err != 0) device_printf(dev, "cannot write RTC time\n"); return (err); } static int nxprtc_probe(device_t dev) { int chiptype; #ifdef FDT if (!ofw_bus_status_okay(dev)) return (ENXIO); chiptype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (chiptype == TYPE_NONE) return (ENXIO); #else /* Historically the non-FDT driver supports only PCF8563. */ chiptype = TYPE_PCF8563; #endif device_set_desc(dev, desc_strings[chiptype]); return (BUS_PROBE_DEFAULT); } 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); sc->slave_addr = iicbus_get_addr(dev); /* * We need to know what kind of chip we're driving. Historically the * non-FDT driver supported only PCF8563. There is no machine-readable * identifier in the chip so we would need a set of hints defined to use * the other chips on non-FDT systems. */ #ifdef FDT sc->chiptype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; #else sc->chiptype = TYPE_PCF8563; if (sc->slave_addr == 0) sc->slave_addr = PCF8563_ADDR; #endif /* The features and some register addresses vary by chip type. */ switch (sc->chiptype) { case TYPE_PCA2129: case TYPE_PCF2129: sc->secaddr = PCF8523_R_SECOND; sc->tmcaddr = 0; sc->use_timer = false; break; 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, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);