Index: stable/11/sys/dev/iicbus/iiconf.c =================================================================== --- stable/11/sys/dev/iicbus/iiconf.c (revision 331496) +++ stable/11/sys/dev/iicbus/iiconf.c (revision 331497) @@ -1,519 +1,525 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1998 Nicolas Souchu * 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$"); #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" /* * Translate IIC_Exxxxx status values to vaguely-equivelent errno values. */ int iic2errno(int iic_status) { switch (iic_status) { case IIC_NOERR: return (0); case IIC_EBUSERR: return (EALREADY); case IIC_ENOACK: return (EIO); case IIC_ETIMEOUT: return (ETIMEDOUT); case IIC_EBUSBSY: return (EWOULDBLOCK); case IIC_ESTATUS: return (EPROTO); case IIC_EUNDERFLOW: return (EIO); case IIC_EOVERFLOW: return (EOVERFLOW); case IIC_ENOTSUPP: return (EOPNOTSUPP); case IIC_ENOADDR: return (EADDRNOTAVAIL); case IIC_ERESOURCE: return (ENOMEM); default: return (EIO); } } /* * iicbus_intr() */ void iicbus_intr(device_t bus, int event, char *buf) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); /* call owner's intr routine */ if (sc->owner) IICBUS_INTR(sc->owner, event, buf); return; } static int iicbus_poll(struct iicbus_softc *sc, int how) { int error; IICBUS_ASSERT_LOCKED(sc); - switch (how) { + switch (how & IIC_INTRWAIT) { case IIC_WAIT | IIC_INTR: error = mtx_sleep(sc, &sc->lock, IICPRI|PCATCH, "iicreq", 0); break; case IIC_WAIT | IIC_NOINTR: error = mtx_sleep(sc, &sc->lock, IICPRI, "iicreq", 0); break; default: return (IIC_EBUSBSY); } return (error); } /* * iicbus_request_bus() * * Allocate the device to perform transfers. * * how : IIC_WAIT or IIC_DONTWAIT */ int iicbus_request_bus(device_t bus, device_t dev, int how) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; IICBUS_LOCK(sc); - while (error == 0 && sc->owner != NULL && sc->owner != dev) - error = iicbus_poll(sc, how); + for (;;) { + if (sc->owner == NULL) + break; + if ((how & IIC_RECURSIVE) && sc->owner == dev) + break; + if ((error = iicbus_poll(sc, how)) != 0) + break; + } if (error == 0) { ++sc->owncount; if (sc->owner == NULL) { sc->owner = dev; /* * Drop the lock around the call to the bus driver, it * should be allowed to sleep in the IIC_WAIT case. * Drivers might also need to grab locks that would * cause a LOR if our lock is held. */ IICBUS_UNLOCK(sc); /* Ask the underlying layers if the request is ok */ error = IICBUS_CALLBACK(device_get_parent(bus), IIC_REQUEST_BUS, (caddr_t)&how); IICBUS_LOCK(sc); if (error != 0) { sc->owner = NULL; sc->owncount = 0; wakeup_one(sc); } } } IICBUS_UNLOCK(sc); return (error); } /* * iicbus_release_bus() * * Release the device allocated with iicbus_request_dev() */ int iicbus_release_bus(device_t bus, device_t dev) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); IICBUS_LOCK(sc); if (sc->owner != dev) { IICBUS_UNLOCK(sc); return (IIC_EBUSBSY); } if (--sc->owncount == 0) { /* Drop the lock while informing the low-level driver. */ IICBUS_UNLOCK(sc); IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); IICBUS_LOCK(sc); sc->owner = NULL; wakeup_one(sc); } IICBUS_UNLOCK(sc); return (0); } /* * iicbus_started() * * Test if the iicbus is started by the controller */ int iicbus_started(device_t bus) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); return (sc->started); } /* * iicbus_start() * * Send start condition to the slave addressed by 'slave' */ int iicbus_start(device_t bus, u_char slave, int timeout) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; if (sc->started) return (IIC_ESTATUS); /* protocol error, bus already started */ if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout))) sc->started = slave; else sc->started = 0; return (error); } /* * iicbus_repeated_start() * * Send start condition to the slave addressed by 'slave' */ int iicbus_repeated_start(device_t bus, u_char slave, int timeout) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; if (!sc->started) return (IIC_ESTATUS); /* protocol error, bus not started */ if (!(error = IICBUS_REPEATED_START(device_get_parent(bus), slave, timeout))) sc->started = slave; else sc->started = 0; return (error); } /* * iicbus_stop() * * Send stop condition to the bus */ int iicbus_stop(device_t bus) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; if (!sc->started) return (IIC_ESTATUS); /* protocol error, bus not started */ error = IICBUS_STOP(device_get_parent(bus)); /* refuse any further access */ sc->started = 0; return (error); } /* * iicbus_write() * * Write a block of data to the slave previously started by * iicbus_start() call */ int iicbus_write(device_t bus, const char *buf, int len, int *sent, int timeout) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); /* a slave must have been started for writing */ if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) != 0)) return (IIC_ESTATUS); return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout)); } /* * iicbus_read() * * Read a block of data from the slave previously started by * iicbus_read() call */ int iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay) { struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); /* a slave must have been started for reading */ if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) == 0)) return (IIC_ESTATUS); return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, delay)); } /* * iicbus_write_byte() * * Write a byte to the slave previously started by iicbus_start() call */ int iicbus_write_byte(device_t bus, char byte, int timeout) { struct iicbus_softc *sc = device_get_softc(bus); char data = byte; int sent; /* a slave must have been started for writing */ if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) != 0)) return (IIC_ESTATUS); return (iicbus_write(bus, &data, 1, &sent, timeout)); } /* * iicbus_read_byte() * * Read a byte from the slave previously started by iicbus_start() call */ int iicbus_read_byte(device_t bus, char *byte, int timeout) { struct iicbus_softc *sc = device_get_softc(bus); int read; /* a slave must have been started for reading */ if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) == 0)) return (IIC_ESTATUS); return (iicbus_read(bus, byte, 1, &read, IIC_LAST_READ, timeout)); } /* * iicbus_block_write() * * Write a block of data to slave ; start/stop protocol managed */ int iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent) { u_char addr = slave & ~LSB; int error; if ((error = iicbus_start(bus, addr, 0))) return (error); error = iicbus_write(bus, buf, len, sent, 0); iicbus_stop(bus); return (error); } /* * iicbus_block_read() * * Read a block of data from slave ; start/stop protocol managed */ int iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read) { u_char addr = slave | LSB; int error; if ((error = iicbus_start(bus, addr, 0))) return (error); error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0); iicbus_stop(bus); return (error); } /* * iicbus_transfer() * * Do an aribtrary number of transfers on the iicbus. We pass these * raw requests to the bridge driver. If the bridge driver supports * them directly, then it manages all the details. If not, it can use * the helper function iicbus_transfer_gen() which will do the * transfers at a low level. * * Pointers passed in as part of iic_msg must be kernel pointers. * Callers that have user addresses to manage must do so on their own. */ int iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs) { return (IICBUS_TRANSFER(device_get_parent(bus), msgs, nmsgs)); } int iicbus_transfer_excl(device_t dev, struct iic_msg *msgs, uint32_t nmsgs, int how) { device_t bus; int error; bus = device_get_parent(dev); error = iicbus_request_bus(bus, dev, how); if (error == 0) error = IICBUS_TRANSFER(bus, msgs, nmsgs); iicbus_release_bus(bus, dev); return (error); } /* * Generic version of iicbus_transfer that calls the appropriate * routines to accomplish this. See note above about acceptable * buffer addresses. */ int iicbus_transfer_gen(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { int i, error, lenread, lenwrote, nkid, rpstart, addr; device_t *children, bus; bool nostop, started; if ((error = device_get_children(dev, &children, &nkid)) != 0) return (IIC_ERESOURCE); if (nkid != 1) { free(children, M_TEMP); return (IIC_ENOTSUPP); } bus = children[0]; rpstart = 0; free(children, M_TEMP); nostop = iicbus_get_nostop(dev); started = false; for (i = 0, error = 0; i < nmsgs && error == 0; i++) { addr = msgs[i].slave; if (msgs[i].flags & IIC_M_RD) addr |= LSB; else addr &= ~LSB; if (!(msgs[i].flags & IIC_M_NOSTART)) { if (rpstart) error = iicbus_repeated_start(bus, addr, 0); else error = iicbus_start(bus, addr, 0); if (error != 0) break; started = true; } if (msgs[i].flags & IIC_M_RD) error = iicbus_read(bus, msgs[i].buf, msgs[i].len, &lenread, IIC_LAST_READ, 0); else error = iicbus_write(bus, msgs[i].buf, msgs[i].len, &lenwrote, 0); if (error != 0) break; if ((msgs[i].flags & IIC_M_NOSTOP) != 0 || (nostop && i + 1 < nmsgs)) { rpstart = 1; /* Next message gets repeated start */ } else { rpstart = 0; iicbus_stop(bus); } } if (error != 0 && started) iicbus_stop(bus); return (error); } int iicdev_readfrom(device_t slavedev, uint8_t regaddr, void *buffer, uint16_t buflen, int waithow) { struct iic_msg msgs[2]; uint8_t slaveaddr; /* * Two transfers back to back with a repeat-start between them; first we * write the address-within-device, then we read from the device. */ slaveaddr = iicbus_get_addr(slavedev); msgs[0].slave = slaveaddr; msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; msgs[0].len = 1; msgs[0].buf = ®addr; msgs[1].slave = slaveaddr; msgs[1].flags = IIC_M_RD; msgs[1].len = buflen; msgs[1].buf = buffer; return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); } int iicdev_writeto(device_t slavedev, uint8_t regaddr, void *buffer, uint16_t buflen, int waithow) { struct iic_msg msgs[2]; uint8_t slaveaddr; /* * Two transfers back to back with no stop or start between them; first * we write the address then we write the data to that address, all in a * single transfer from two scattered buffers. */ slaveaddr = iicbus_get_addr(slavedev); msgs[0].slave = slaveaddr; msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; msgs[0].len = 1; msgs[0].buf = ®addr; msgs[1].slave = slaveaddr; msgs[1].flags = IIC_M_WR | IIC_M_NOSTART; msgs[1].len = buflen; msgs[1].buf = buffer; return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); } Index: stable/11/sys/dev/iicbus/iiconf.h =================================================================== --- stable/11/sys/dev/iicbus/iiconf.h (revision 331496) +++ stable/11/sys/dev/iicbus/iiconf.h (revision 331497) @@ -1,161 +1,162 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1998, 2001 Nicolas Souchu * 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. * * $FreeBSD$ */ #ifndef __IICONF_H #define __IICONF_H #include #include #define IICPRI (PZERO+8) /* XXX sleep/wakeup queue priority */ #define LSB 0x1 /* - * How tsleep() is called in iic_request_bus(). + * Options affecting iicbus_request_bus() */ #define IIC_DONTWAIT 0 #define IIC_NOINTR 0 #define IIC_WAIT 0x1 #define IIC_INTR 0x2 #define IIC_INTRWAIT (IIC_INTR | IIC_WAIT) +#define IIC_RECURSIVE 0x4 /* * i2c modes */ #define IIC_MASTER 0x1 #define IIC_SLAVE 0x2 #define IIC_POLLED 0x4 /* * i2c speed */ #define IIC_UNKNOWN 0x0 #define IIC_SLOW 0x1 #define IIC_FAST 0x2 #define IIC_FASTEST 0x3 #define IIC_LAST_READ 0x1 /* * callback index */ #define IIC_REQUEST_BUS 0x1 #define IIC_RELEASE_BUS 0x2 /* * interrupt events */ #define INTR_GENERAL 0x1 /* general call received */ #define INTR_START 0x2 /* the I2C interface is addressed */ #define INTR_STOP 0x3 /* stop condition received */ #define INTR_RECEIVE 0x4 /* character received */ #define INTR_TRANSMIT 0x5 /* character to transmit */ #define INTR_ERROR 0x6 /* error */ #define INTR_NOACK 0x7 /* no ack from master receiver */ /* * adapter layer errors */ #define IIC_NOERR 0x0 /* no error occurred */ #define IIC_EBUSERR 0x1 /* bus error (hardware not in expected state) */ #define IIC_ENOACK 0x2 /* ack not received until timeout */ #define IIC_ETIMEOUT 0x3 /* timeout */ #define IIC_EBUSBSY 0x4 /* bus busy (reserved by another client) */ #define IIC_ESTATUS 0x5 /* status error */ #define IIC_EUNDERFLOW 0x6 /* slave ready for more data */ #define IIC_EOVERFLOW 0x7 /* too much data */ #define IIC_ENOTSUPP 0x8 /* request not supported */ #define IIC_ENOADDR 0x9 /* no address assigned to the interface */ #define IIC_ERESOURCE 0xa /* resources (memory, whatever) unavailable */ /* * Note that all iicbus functions return IIC_Exxxxx status values, * except iic2errno() (obviously) and iicbus_started() (returns bool). */ extern int iic2errno(int); extern int iicbus_request_bus(device_t, device_t, int); extern int iicbus_release_bus(device_t, device_t); extern device_t iicbus_alloc_bus(device_t); extern void iicbus_intr(device_t, int, char *); extern int iicbus_null_repeated_start(device_t, u_char); extern int iicbus_null_callback(device_t, int, caddr_t); #define iicbus_reset(bus,speed,addr,oldaddr) \ (IICBUS_RESET(device_get_parent(bus), speed, addr, oldaddr)) /* basic I2C operations */ extern int iicbus_started(device_t); extern int iicbus_start(device_t, u_char, int); extern int iicbus_stop(device_t); extern int iicbus_repeated_start(device_t, u_char, int); extern int iicbus_write(device_t, const char *, int, int *, int); extern int iicbus_read(device_t, char *, int, int *, int, int); /* single byte read/write functions, start/stop not managed */ extern int iicbus_write_byte(device_t, char, int); extern int iicbus_read_byte(device_t, char *, int); /* Read/write operations with start/stop conditions managed */ extern int iicbus_block_write(device_t, u_char, char *, int, int *); extern int iicbus_block_read(device_t, u_char, char *, int, int *); /* vectors of iic operations to pass to bridge */ int iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); int iicbus_transfer_excl(device_t bus, struct iic_msg *msgs, uint32_t nmsgs, int how); int iicbus_transfer_gen(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); /* * Simple register read/write routines, but the "register" can be any size. * The transfers are done with iicbus_transfer_excl(). Reads use a repeat-start * between sending the address and reading; writes use a single start/stop. */ int iicdev_readfrom(device_t _slavedev, uint8_t _regaddr, void *_buffer, uint16_t _buflen, int _waithow); int iicdev_writeto(device_t _slavedev, uint8_t _regaddr, void *_buffer, uint16_t _buflen, int _waithow); #define IICBUS_MODVER 1 #define IICBUS_MINVER 1 #define IICBUS_MAXVER 1 #define IICBUS_PREFVER IICBUS_MODVER extern driver_t iicbb_driver; extern devclass_t iicbb_devclass; #define IICBB_MODVER 1 #define IICBB_MINVER 1 #define IICBB_MAXVER 1 #define IICBB_PREFVER IICBB_MODVER #endif Index: stable/11/sys/dev/iicbus/isl12xx.c =================================================================== --- stable/11/sys/dev/iicbus/isl12xx.c (revision 331496) +++ stable/11/sys/dev/iicbus/isl12xx.c (revision 331497) @@ -1,354 +1,369 @@ /*- * 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 ``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, IIC_WAIT)); + 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, IIC_WAIT)); + 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 clocktime ct; struct time_regs tregs; int err; uint8_t hourmask, sreg; - /* If power failed, we can't provide valid time. */ - if ((err = isl12xx_read1(sc, ISL12XX_SR_REG, &sreg)) != 0) + /* + * 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); - /* Read the bcd time registers. */ - if ((err = iicdev_readfrom(sc->dev, ISL12XX_SC_REG, &tregs, sizeof(tregs), - IIC_WAIT)) != 0) - 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; } ct.nsec = 0; ct.sec = FROMBCD(tregs.sec); ct.min = FROMBCD(tregs.min); ct.hour = FROMBCD(tregs.hour & hourmask); ct.day = FROMBCD(tregs.day); ct.mon = FROMBCD(tregs.month); ct.year = FROMBCD(tregs.year); if (sc->use_ampm) { if (ct.hour == 12) ct.hour = 0; if (tregs.hour & ISL12XX_PM_FLAG) ct.hour += 12; } return (clock_ct_to_ts(&ct, ts)); } static int isl12xx_settime(device_t dev, struct timespec *ts) { struct isl12xx_softc *sc = device_get_softc(dev); struct clocktime ct; 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_ct(ts, &ct); /* If the chip is in AM/PM mode, adjust hour and set flags as needed. */ if (!sc->use_ampm) { ampmflags = ISL12XX_24HR_FLAG; } else { ampmflags = 0; if (ct.hour >= 12) { ct.hour -= 12; ampmflags |= ISL12XX_PM_FLAG; } if (ct.hour == 0) ct.hour = 12; } tregs.sec = TOBCD(ct.sec); tregs.min = TOBCD(ct.min); tregs.hour = TOBCD(ct.hour) | ampmflags; tregs.day = TOBCD(ct.day); tregs.month = TOBCD(ct.mon); tregs.year = TOBCD(ct.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), IIC_WAIT); + 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); Index: stable/11/sys/dev/iicbus/nxprtc.c =================================================================== --- stable/11/sys/dev/iicbus/nxprtc.c (revision 331496) +++ stable/11/sys/dev/iicbus/nxprtc.c (revision 331497) @@ -1,816 +1,826 @@ /*- * 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_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 */ bool use_timer; /* Use timer for fractional sec */ bool use_ampm; /* Chip is set to use am/pm mode */ }; #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 read_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t *val) { - return (iicdev_readfrom(sc->dev, reg, val, sizeof(*val), IIC_WAIT)); + return (iicdev_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), IIC_WAIT)); + 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 = iicdev_readfrom(sc->dev, sc->secaddr, tregs, - sizeof(*tregs), IIC_WAIT)) != 0) + 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), IIC_WAIT)); + sizeof(*tregs), WAITFLAGS)); } 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->use_ampm = true; } else { if (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; /* 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) { 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_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; } /* * 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. * 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 = read_timeregs(sc, &tregs, &tmrcount)) != 0) { - device_printf(dev, "cannot read RTC time\n"); - return (err); + 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 = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0) { - device_printf(dev, "cannot read RTC time\n"); + 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; } 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); /* 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); 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: 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); Index: stable/11 =================================================================== --- stable/11 (revision 331496) +++ stable/11 (revision 331497) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r328307,328311-328312