Index: stable/12/sys/dev/flash/at45d.c =================================================================== --- stable/12/sys/dev/flash/at45d.c (revision 349313) +++ stable/12/sys/dev/flash/at45d.c (revision 349314) @@ -1,611 +1,611 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 M. Warner Losh * Copyright (c) 2011-2012 Ian Lepore * Copyright (c) 2012 Marius Strobl * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spibus_if.h" #include "opt_platform.h" #ifdef FDT #include #include #include static struct ofw_compat_data compat_data[] = { { "atmel,at45", 1 }, { "atmel,dataflash", 1 }, { NULL, 0 }, }; #endif /* This is the information returned by the MANUFACTURER_ID command. */ struct at45d_mfg_info { uint32_t jedec_id; /* Mfg ID, DevId1, DevId2, ExtLen */ uint16_t ext_id; /* ExtId1, ExtId2 */ }; /* * This is an entry in our table of metadata describing the chips. We match on * both jedec id and extended id info returned by the MANUFACTURER_ID command. */ struct at45d_flash_ident { const char *name; uint32_t jedec; uint16_t extid; uint16_t extmask; uint16_t pagecount; uint16_t pageoffset; uint16_t pagesize; uint16_t pagesize2n; }; struct at45d_softc { struct bio_queue_head bio_queue; struct mtx sc_mtx; struct disk *disk; struct proc *p; device_t dev; u_int taskstate; uint16_t pagecount; uint16_t pageoffset; uint16_t pagesize; void *dummybuf; }; #define TSTATE_STOPPED 0 #define TSTATE_STOPPING 1 #define TSTATE_RUNNING 2 #define AT45D_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define AT45D_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define AT45D_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ "at45d", MTX_DEF) #define AT45D_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define AT45D_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define AT45D_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); /* bus entry points */ static device_attach_t at45d_attach; static device_detach_t at45d_detach; static device_probe_t at45d_probe; /* disk routines */ static int at45d_close(struct disk *dp); static int at45d_open(struct disk *dp); static int at45d_getattr(struct bio *bp); static void at45d_strategy(struct bio *bp); static void at45d_task(void *arg); /* helper routines */ static void at45d_delayed_attach(void *xsc); static int at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp); static int at45d_get_status(device_t dev, uint8_t *status); static int at45d_wait_ready(device_t dev, uint8_t *status); #define PAGE_TO_BUFFER_TRANSFER 0x53 #define PAGE_TO_BUFFER_COMPARE 0x60 #define PROGRAM_THROUGH_BUFFER 0x82 #define MANUFACTURER_ID 0x9f #define STATUS_REGISTER_READ 0xd7 #define CONTINUOUS_ARRAY_READ 0xe8 #define STATUS_READY (1u << 7) #define STATUS_CMPFAIL (1u << 6) #define STATUS_PAGE2N (1u << 0) /* * Metadata for supported chips. * * The jedec id in this table includes the extended id length byte. A match is * based on both jedec id and extended id matching. The chip's extended id (not * present in most chips) is ANDed with ExtMask and the result is compared to * ExtId. If a chip only returns 1 ext id byte it will be in the upper 8 bits * of ExtId in this table. * * A sectorsize2n != 0 is used to indicate that a device optionally supports * 2^N byte pages. If support for the latter is enabled, the sector offset * has to be reduced by one. */ static const struct at45d_flash_ident at45d_flash_devices[] = { /* Part Name Jedec ID ExtId ExtMask PgCnt Offs PgSz PgSz2n */ { "AT45DB011B", 0x1f220000, 0x0000, 0x0000, 512, 9, 264, 256 }, { "AT45DB021B", 0x1f230000, 0x0000, 0x0000, 1024, 9, 264, 256 }, { "AT45DB041x", 0x1f240000, 0x0000, 0x0000, 2028, 9, 264, 256 }, { "AT45DB081B", 0x1f250000, 0x0000, 0x0000, 4096, 9, 264, 256 }, { "AT45DB161x", 0x1f260000, 0x0000, 0x0000, 4096, 10, 528, 512 }, { "AT45DB321x", 0x1f270000, 0x0000, 0x0000, 8192, 10, 528, 0 }, { "AT45DB321x", 0x1f270100, 0x0000, 0x0000, 8192, 10, 528, 512 }, { "AT45DB641E", 0x1f280001, 0x0000, 0xff00, 32768, 9, 264, 256 }, { "AT45DB642x", 0x1f280000, 0x0000, 0x0000, 8192, 11, 1056, 1024 }, }; static int at45d_get_status(device_t dev, uint8_t *status) { uint8_t rxBuf[8], txBuf[8]; struct spi_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); txBuf[0] = STATUS_REGISTER_READ; cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; cmd.rx_cmd_sz = cmd.tx_cmd_sz = 2; err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); *status = rxBuf[1]; return (err); } static int at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp) { uint8_t rxBuf[8], txBuf[8]; struct spi_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); txBuf[0] = MANUFACTURER_ID; cmd.tx_cmd = &txBuf; cmd.rx_cmd = &rxBuf; cmd.tx_cmd_sz = cmd.rx_cmd_sz = 7; err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); if (err) return (err); resp->jedec_id = be32dec(rxBuf + 1); resp->ext_id = be16dec(rxBuf + 5); return (0); } static int at45d_wait_ready(device_t dev, uint8_t *status) { struct timeval now, tout; int err; getmicrouptime(&tout); tout.tv_sec += 3; do { getmicrouptime(&now); if (now.tv_sec > tout.tv_sec) err = ETIMEDOUT; else err = at45d_get_status(dev, status); } while (err == 0 && !(*status & STATUS_READY)); return (err); } static int at45d_probe(device_t dev) { int rv; #ifdef FDT if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); rv = BUS_PROBE_DEFAULT; #else rv = BUS_PROBE_NOWILDCARD; #endif device_set_desc(dev, "AT45D Flash Family"); return (rv); } static int at45d_attach(device_t dev) { struct at45d_softc *sc; sc = device_get_softc(dev); sc->dev = dev; AT45D_LOCK_INIT(sc); config_intrhook_oneshot(at45d_delayed_attach, sc); return (0); } static int at45d_detach(device_t dev) { struct at45d_softc *sc; int err; sc = device_get_softc(dev); err = 0; AT45D_LOCK(sc); if (sc->taskstate == TSTATE_RUNNING) { sc->taskstate = TSTATE_STOPPING; wakeup(sc); while (err == 0 && sc->taskstate != TSTATE_STOPPED) { err = msleep(sc, &sc->sc_mtx, 0, "at45dt", hz * 3); if (err != 0) { sc->taskstate = TSTATE_RUNNING; device_printf(sc->dev, "Failed to stop queue task\n"); } } } AT45D_UNLOCK(sc); if (err == 0 && sc->taskstate == TSTATE_STOPPED) { if (sc->disk) { disk_destroy(sc->disk); bioq_flush(&sc->bio_queue, NULL, ENXIO); free(sc->dummybuf, M_DEVBUF); } AT45D_LOCK_DESTROY(sc); } return (err); } static void at45d_delayed_attach(void *xsc) { struct at45d_softc *sc; struct at45d_mfg_info mfginfo; const struct at45d_flash_ident *ident; u_int i; int sectorsize; uint32_t jedec; uint16_t pagesize; uint8_t status; sc = xsc; ident = NULL; jedec = 0; if (at45d_wait_ready(sc->dev, &status) != 0) { device_printf(sc->dev, "Error waiting for device-ready.\n"); return; } if (at45d_get_mfg_info(sc->dev, &mfginfo) != 0) { device_printf(sc->dev, "Failed to get ID.\n"); return; } for (i = 0; i < nitems(at45d_flash_devices); i++) { ident = &at45d_flash_devices[i]; if (mfginfo.jedec_id == ident->jedec && (mfginfo.ext_id & ident->extmask) == ident->extid) { break; } } if (i == nitems(at45d_flash_devices)) { device_printf(sc->dev, "JEDEC 0x%x not in list.\n", jedec); return; } sc->pagecount = ident->pagecount; sc->pageoffset = ident->pageoffset; if (ident->pagesize2n != 0 && (status & STATUS_PAGE2N)) { sc->pageoffset -= 1; pagesize = ident->pagesize2n; } else pagesize = ident->pagesize; sc->pagesize = pagesize; /* * By default we set up a disk with a sector size that matches the * device page size. If there is a device hint or fdt property * requesting a different size, use that, as long as it is a multiple of * the device page size). */ sectorsize = pagesize; #ifdef FDT { pcell_t size; if (OF_getencprop(ofw_bus_get_node(sc->dev), "freebsd,sectorsize", &size, sizeof(size)) > 0) sectorsize = size; } #endif resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev), "sectorsize", §orsize); if ((sectorsize % pagesize) != 0) { device_printf(sc->dev, "Invalid sectorsize %d, " "must be a multiple of %d\n", sectorsize, pagesize); return; } sc->dummybuf = malloc(pagesize, M_DEVBUF, M_WAITOK | M_ZERO); sc->disk = disk_alloc(); sc->disk->d_open = at45d_open; sc->disk->d_close = at45d_close; sc->disk->d_strategy = at45d_strategy; sc->disk->d_getattr = at45d_getattr; sc->disk->d_name = "flash/at45d"; sc->disk->d_drv1 = sc; sc->disk->d_maxsize = DFLTPHYS; sc->disk->d_sectorsize = sectorsize; sc->disk->d_mediasize = pagesize * ident->pagecount; sc->disk->d_unit = device_get_unit(sc->dev); disk_create(sc->disk, DISK_VERSION); bioq_init(&sc->bio_queue); kproc_create(&at45d_task, sc, &sc->p, 0, 0, "task: at45d flash"); sc->taskstate = TSTATE_RUNNING; device_printf(sc->dev, "%s, %d bytes per page, %d pages; %d KBytes; disk sector size %d\n", ident->name, pagesize, ident->pagecount, (pagesize * ident->pagecount) / 1024, sectorsize); } static int at45d_open(struct disk *dp) { return (0); } static int at45d_close(struct disk *dp) { return (0); } static int at45d_getattr(struct bio *bp) { struct at45d_softc *sc; /* * This function exists to support geom_flashmap and fdt_slicer. */ if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) return (ENXIO); if (strcmp(bp->bio_attribute, "SPI::device") != 0) return (-1); sc = bp->bio_disk->d_drv1; if (bp->bio_length != sizeof(sc->dev)) return (EFAULT); bcopy(&sc->dev, bp->bio_data, sizeof(sc->dev)); return (0); } static void at45d_strategy(struct bio *bp) { struct at45d_softc *sc; sc = (struct at45d_softc *)bp->bio_disk->d_drv1; AT45D_LOCK(sc); bioq_disksort(&sc->bio_queue, bp); wakeup(sc); AT45D_UNLOCK(sc); } static void at45d_task(void *arg) { uint8_t rxBuf[8], txBuf[8]; struct at45d_softc *sc; struct bio *bp; struct spi_command cmd; device_t dev, pdev; caddr_t buf; u_long len, resid; u_int addr, berr, err, offset, page; uint8_t status; sc = (struct at45d_softc*)arg; dev = sc->dev; pdev = device_get_parent(dev); memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; for (;;) { AT45D_LOCK(sc); do { if (sc->taskstate == TSTATE_STOPPING) { sc->taskstate = TSTATE_STOPPED; AT45D_UNLOCK(sc); wakeup(sc); kproc_exit(0); } bp = bioq_takefirst(&sc->bio_queue); if (bp == NULL) msleep(sc, &sc->sc_mtx, PRIBIO, "at45dq", 0); } while (bp == NULL); AT45D_UNLOCK(sc); berr = 0; buf = bp->bio_data; len = resid = bp->bio_bcount; page = bp->bio_offset / sc->pagesize; offset = bp->bio_offset % sc->pagesize; switch (bp->bio_cmd) { case BIO_READ: txBuf[0] = CONTINUOUS_ARRAY_READ; cmd.tx_cmd_sz = cmd.rx_cmd_sz = 8; cmd.tx_data = sc->dummybuf; cmd.rx_data = buf; break; case BIO_WRITE: cmd.tx_cmd_sz = cmd.rx_cmd_sz = 4; cmd.tx_data = buf; cmd.rx_data = sc->dummybuf; if (resid + offset > sc->pagesize) len = sc->pagesize - offset; break; default: berr = EINVAL; goto out; } /* * NB: for BIO_READ, this loop is only traversed once. */ while (resid > 0) { if (page > sc->pagecount) { berr = EINVAL; goto out; } addr = page << sc->pageoffset; if (bp->bio_cmd == BIO_WRITE) { /* * If writing less than a full page, transfer * the existing page to the buffer, so that our * PROGRAM_THROUGH_BUFFER below will preserve * the parts of the page we're not writing. */ if (len != sc->pagesize) { txBuf[0] = PAGE_TO_BUFFER_TRANSFER; txBuf[1] = ((addr >> 16) & 0xff); txBuf[2] = ((addr >> 8) & 0xff); txBuf[3] = 0; cmd.tx_data_sz = cmd.rx_data_sz = 0; err = SPIBUS_TRANSFER(pdev, dev, &cmd); if (err == 0) err = at45d_wait_ready(dev, &status); if (err != 0) { berr = EIO; goto out; } } txBuf[0] = PROGRAM_THROUGH_BUFFER; } addr += offset; txBuf[1] = ((addr >> 16) & 0xff); txBuf[2] = ((addr >> 8) & 0xff); txBuf[3] = (addr & 0xff); cmd.tx_data_sz = cmd.rx_data_sz = len; err = SPIBUS_TRANSFER(pdev, dev, &cmd); if (err == 0 && bp->bio_cmd != BIO_READ) err = at45d_wait_ready(dev, &status); if (err != 0) { berr = EIO; goto out; } if (bp->bio_cmd == BIO_WRITE) { addr = page << sc->pageoffset; txBuf[0] = PAGE_TO_BUFFER_COMPARE; txBuf[1] = ((addr >> 16) & 0xff); txBuf[2] = ((addr >> 8) & 0xff); txBuf[3] = 0; cmd.tx_data_sz = cmd.rx_data_sz = 0; err = SPIBUS_TRANSFER(pdev, dev, &cmd); if (err == 0) err = at45d_wait_ready(dev, &status); if (err != 0 || (status & STATUS_CMPFAIL)) { device_printf(dev, "comparing page " "%d failed (status=0x%x)\n", page, status); berr = EIO; goto out; } } page++; buf += len; offset = 0; resid -= len; if (resid > sc->pagesize) len = sc->pagesize; else len = resid; if (bp->bio_cmd == BIO_READ) cmd.rx_data = buf; else cmd.tx_data = buf; } out: if (berr != 0) { bp->bio_flags |= BIO_ERROR; bp->bio_error = berr; } bp->bio_resid = resid; biodone(bp); } } static devclass_t at45d_devclass; static device_method_t at45d_methods[] = { /* Device interface */ DEVMETHOD(device_probe, at45d_probe), DEVMETHOD(device_attach, at45d_attach), DEVMETHOD(device_detach, at45d_detach), DEVMETHOD_END }; static driver_t at45d_driver = { "at45d", at45d_methods, sizeof(struct at45d_softc), }; DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, NULL, NULL); MODULE_DEPEND(at45d, spibus, 1, 1, 1); #ifdef FDT MODULE_DEPEND(at45d, fdt_slicer, 1, 1, 1); -SPIBUS_PNP_INFO(compat_data); +SPIBUS_FDT_PNP_INFO(compat_data); #endif Index: stable/12/sys/dev/flash/mx25l.c =================================================================== --- stable/12/sys/dev/flash/mx25l.c (revision 349313) +++ stable/12/sys/dev/flash/mx25l.c (revision 349314) @@ -1,690 +1,690 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 M. Warner Losh. All rights reserved. * Copyright (c) 2009 Oleksandr Tymoshenko. All rights reserved. * Copyright (c) 2018 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$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include "spibus_if.h" #include #define FL_NONE 0x00 #define FL_ERASE_4K 0x01 #define FL_ERASE_32K 0x02 #define FL_ENABLE_4B_ADDR 0x04 #define FL_DISABLE_4B_ADDR 0x08 /* * Define the sectorsize to be a smaller size rather than the flash * sector size. Trying to run FFS off of a 64k flash sector size * results in a completely un-usable system. */ #define MX25L_SECTORSIZE 512 struct mx25l_flash_ident { const char *name; uint8_t manufacturer_id; uint16_t device_id; unsigned int sectorsize; unsigned int sectorcount; unsigned int flags; }; struct mx25l_softc { device_t sc_dev; device_t sc_parent; uint8_t sc_manufacturer_id; uint16_t sc_device_id; unsigned int sc_erasesize; struct mtx sc_mtx; struct disk *sc_disk; struct proc *sc_p; struct bio_queue_head sc_bio_queue; unsigned int sc_flags; unsigned int sc_taskstate; uint8_t sc_dummybuf[FLASH_PAGE_SIZE]; }; #define TSTATE_STOPPED 0 #define TSTATE_STOPPING 1 #define TSTATE_RUNNING 2 #define M25PXX_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define M25PXX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define M25PXX_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ "mx25l", MTX_DEF) #define M25PXX_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define M25PXX_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define M25PXX_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); /* disk routines */ static int mx25l_open(struct disk *dp); static int mx25l_close(struct disk *dp); static int mx25l_ioctl(struct disk *, u_long, void *, int, struct thread *); static void mx25l_strategy(struct bio *bp); static int mx25l_getattr(struct bio *bp); static void mx25l_task(void *arg); static struct mx25l_flash_ident flash_devices[] = { { "en25f32", 0x1c, 0x3116, 64 * 1024, 64, FL_NONE }, { "en25p32", 0x1c, 0x2016, 64 * 1024, 64, FL_NONE }, { "en25p64", 0x1c, 0x2017, 64 * 1024, 128, FL_NONE }, { "en25q32", 0x1c, 0x3016, 64 * 1024, 64, FL_NONE }, { "en25q64", 0x1c, 0x3017, 64 * 1024, 128, FL_ERASE_4K }, { "m25p32", 0x20, 0x2016, 64 * 1024, 64, FL_NONE }, { "m25p64", 0x20, 0x2017, 64 * 1024, 128, FL_NONE }, { "mx25l1606e", 0xc2, 0x2015, 64 * 1024, 32, FL_ERASE_4K}, { "mx25ll32", 0xc2, 0x2016, 64 * 1024, 64, FL_NONE }, { "mx25ll64", 0xc2, 0x2017, 64 * 1024, 128, FL_NONE }, { "mx25ll128", 0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K }, { "mx25ll256", 0xc2, 0x2019, 64 * 1024, 512, FL_ERASE_4K | FL_ERASE_32K | FL_ENABLE_4B_ADDR }, { "s25fl032", 0x01, 0x0215, 64 * 1024, 64, FL_NONE }, { "s25fl064", 0x01, 0x0216, 64 * 1024, 128, FL_NONE }, { "s25fl128", 0x01, 0x2018, 64 * 1024, 256, FL_NONE }, { "s25fl256s", 0x01, 0x0219, 64 * 1024, 512, FL_NONE }, { "SST25VF010A", 0xbf, 0x2549, 4 * 1024, 32, FL_ERASE_4K | FL_ERASE_32K }, { "SST25VF032B", 0xbf, 0x254a, 64 * 1024, 64, FL_ERASE_4K | FL_ERASE_32K }, /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x32", 0xef, 0x3016, 64 * 1024, 64, FL_ERASE_4K }, { "w25x64", 0xef, 0x3017, 64 * 1024, 128, FL_ERASE_4K }, { "w25q32", 0xef, 0x4016, 64 * 1024, 64, FL_ERASE_4K }, { "w25q64", 0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K }, { "w25q64bv", 0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K }, { "w25q128", 0xef, 0x4018, 64 * 1024, 256, FL_ERASE_4K }, { "w25q256", 0xef, 0x4019, 64 * 1024, 512, FL_ERASE_4K }, /* Atmel */ { "at25df641", 0x1f, 0x4800, 64 * 1024, 128, FL_ERASE_4K }, /* GigaDevice */ { "gd25q64", 0xc8, 0x4017, 64 * 1024, 128, FL_ERASE_4K }, }; static int mx25l_wait_for_device_ready(struct mx25l_softc *sc) { uint8_t txBuf[2], rxBuf[2]; struct spi_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); do { txBuf[0] = CMD_READ_STATUS; cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; cmd.rx_cmd_sz = 2; cmd.tx_cmd_sz = 2; err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd); } while (err == 0 && (rxBuf[1] & STATUS_WIP)); return (err); } static struct mx25l_flash_ident* mx25l_get_device_ident(struct mx25l_softc *sc) { uint8_t txBuf[8], rxBuf[8]; struct spi_command cmd; uint8_t manufacturer_id; uint16_t dev_id; int err, i; memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); txBuf[0] = CMD_READ_IDENT; cmd.tx_cmd = &txBuf; cmd.rx_cmd = &rxBuf; /* * Some compatible devices has extended two-bytes ID * We'll use only manufacturer/deviceid atm */ cmd.tx_cmd_sz = 4; cmd.rx_cmd_sz = 4; err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd); if (err) return (NULL); manufacturer_id = rxBuf[1]; dev_id = (rxBuf[2] << 8) | (rxBuf[3]); for (i = 0; i < nitems(flash_devices); i++) { if ((flash_devices[i].manufacturer_id == manufacturer_id) && (flash_devices[i].device_id == dev_id)) return &flash_devices[i]; } device_printf(sc->sc_dev, "Unknown SPI flash device. Vendor: %02x, device id: %04x\n", manufacturer_id, dev_id); return (NULL); } static int mx25l_set_writable(struct mx25l_softc *sc, int writable) { uint8_t txBuf[1], rxBuf[1]; struct spi_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); txBuf[0] = writable ? CMD_WRITE_ENABLE : CMD_WRITE_DISABLE; cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; cmd.rx_cmd_sz = 1; cmd.tx_cmd_sz = 1; err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd); return (err); } static int mx25l_erase_cmd(struct mx25l_softc *sc, off_t sector) { uint8_t txBuf[5], rxBuf[5]; struct spi_command cmd; int err; if ((err = mx25l_set_writable(sc, 1)) != 0) return (err); memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; if (sc->sc_flags & FL_ERASE_4K) txBuf[0] = CMD_BLOCK_4K_ERASE; else if (sc->sc_flags & FL_ERASE_32K) txBuf[0] = CMD_BLOCK_32K_ERASE; else txBuf[0] = CMD_SECTOR_ERASE; if (sc->sc_flags & FL_ENABLE_4B_ADDR) { cmd.rx_cmd_sz = 5; cmd.tx_cmd_sz = 5; txBuf[1] = ((sector >> 24) & 0xff); txBuf[2] = ((sector >> 16) & 0xff); txBuf[3] = ((sector >> 8) & 0xff); txBuf[4] = (sector & 0xff); } else { cmd.rx_cmd_sz = 4; cmd.tx_cmd_sz = 4; txBuf[1] = ((sector >> 16) & 0xff); txBuf[2] = ((sector >> 8) & 0xff); txBuf[3] = (sector & 0xff); } if ((err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd)) != 0) return (err); err = mx25l_wait_for_device_ready(sc); return (err); } static int mx25l_write(struct mx25l_softc *sc, off_t offset, caddr_t data, off_t count) { uint8_t txBuf[8], rxBuf[8]; struct spi_command cmd; off_t bytes_to_write; int err = 0; if (sc->sc_flags & FL_ENABLE_4B_ADDR) { cmd.tx_cmd_sz = 5; cmd.rx_cmd_sz = 5; } else { cmd.tx_cmd_sz = 4; cmd.rx_cmd_sz = 4; } /* * Writes must be aligned to the erase sectorsize, since blocks are * fully erased before they're written to. */ if (count % sc->sc_erasesize != 0 || offset % sc->sc_erasesize != 0) return (EIO); /* * Maximum write size for CMD_PAGE_PROGRAM is FLASH_PAGE_SIZE, so loop * to write chunks of FLASH_PAGE_SIZE bytes each. */ while (count != 0) { /* If we crossed a sector boundary, erase the next sector. */ if (((offset) % sc->sc_erasesize) == 0) { err = mx25l_erase_cmd(sc, offset); if (err) break; } txBuf[0] = CMD_PAGE_PROGRAM; if (sc->sc_flags & FL_ENABLE_4B_ADDR) { txBuf[1] = (offset >> 24) & 0xff; txBuf[2] = (offset >> 16) & 0xff; txBuf[3] = (offset >> 8) & 0xff; txBuf[4] = offset & 0xff; } else { txBuf[1] = (offset >> 16) & 0xff; txBuf[2] = (offset >> 8) & 0xff; txBuf[3] = offset & 0xff; } bytes_to_write = MIN(FLASH_PAGE_SIZE, count); cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; cmd.tx_data = data; cmd.rx_data = sc->sc_dummybuf; cmd.tx_data_sz = (uint32_t)bytes_to_write; cmd.rx_data_sz = (uint32_t)bytes_to_write; /* * Each completed write operation resets WEL (write enable * latch) to disabled state, so we re-enable it here. */ if ((err = mx25l_wait_for_device_ready(sc)) != 0) break; if ((err = mx25l_set_writable(sc, 1)) != 0) break; err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd); if (err != 0) break; err = mx25l_wait_for_device_ready(sc); if (err) break; data += bytes_to_write; offset += bytes_to_write; count -= bytes_to_write; } return (err); } static int mx25l_read(struct mx25l_softc *sc, off_t offset, caddr_t data, off_t count) { uint8_t txBuf[8], rxBuf[8]; struct spi_command cmd; int err = 0; /* * Enforce that reads are aligned to the disk sectorsize, not the * erase sectorsize. In this way, smaller read IO is possible, * dramatically speeding up filesystem/geom_compress access. */ if (count % sc->sc_disk->d_sectorsize != 0 || offset % sc->sc_disk->d_sectorsize != 0) return (EIO); txBuf[0] = CMD_FAST_READ; if (sc->sc_flags & FL_ENABLE_4B_ADDR) { cmd.tx_cmd_sz = 6; cmd.rx_cmd_sz = 6; txBuf[1] = (offset >> 24) & 0xff; txBuf[2] = (offset >> 16) & 0xff; txBuf[3] = (offset >> 8) & 0xff; txBuf[4] = offset & 0xff; /* Dummy byte */ txBuf[5] = 0; } else { cmd.tx_cmd_sz = 5; cmd.rx_cmd_sz = 5; txBuf[1] = (offset >> 16) & 0xff; txBuf[2] = (offset >> 8) & 0xff; txBuf[3] = offset & 0xff; /* Dummy byte */ txBuf[4] = 0; } cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; cmd.tx_data = data; cmd.rx_data = data; cmd.tx_data_sz = count; cmd.rx_data_sz = count; err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd); return (err); } static int mx25l_set_4b_mode(struct mx25l_softc *sc, uint8_t command) { uint8_t txBuf[1], rxBuf[1]; struct spi_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); memset(txBuf, 0, sizeof(txBuf)); memset(rxBuf, 0, sizeof(rxBuf)); cmd.tx_cmd_sz = cmd.rx_cmd_sz = 1; cmd.tx_cmd = txBuf; cmd.rx_cmd = rxBuf; txBuf[0] = command; if ((err = SPIBUS_TRANSFER(sc->sc_parent, sc->sc_dev, &cmd)) == 0) err = mx25l_wait_for_device_ready(sc); return (err); } #ifdef FDT static struct ofw_compat_data compat_data[] = { { "st,m25p", 1 }, { "jedec,spi-nor", 1 }, { NULL, 0 }, }; #endif static int mx25l_probe(device_t dev) { #ifdef FDT int i; if (!ofw_bus_status_okay(dev)) return (ENXIO); /* First try to match the compatible property to the compat_data */ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 1) goto found; /* * Next, try to find a compatible device using the names in the * flash_devices structure */ for (i = 0; i < nitems(flash_devices); i++) if (ofw_bus_is_compatible(dev, flash_devices[i].name)) goto found; return (ENXIO); found: #endif device_set_desc(dev, "M25Pxx Flash Family"); return (0); } static int mx25l_attach(device_t dev) { struct mx25l_softc *sc; struct mx25l_flash_ident *ident; int err; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_parent = device_get_parent(sc->sc_dev); M25PXX_LOCK_INIT(sc); ident = mx25l_get_device_ident(sc); if (ident == NULL) return (ENXIO); if ((err = mx25l_wait_for_device_ready(sc)) != 0) return (err); sc->sc_flags = ident->flags; if (sc->sc_flags & FL_ERASE_4K) sc->sc_erasesize = 4 * 1024; else if (sc->sc_flags & FL_ERASE_32K) sc->sc_erasesize = 32 * 1024; else sc->sc_erasesize = ident->sectorsize; if (sc->sc_flags & FL_ENABLE_4B_ADDR) { if ((err = mx25l_set_4b_mode(sc, CMD_ENTER_4B_MODE)) != 0) return (err); } else if (sc->sc_flags & FL_DISABLE_4B_ADDR) { if ((err = mx25l_set_4b_mode(sc, CMD_EXIT_4B_MODE)) != 0) return (err); } sc->sc_disk = disk_alloc(); sc->sc_disk->d_open = mx25l_open; sc->sc_disk->d_close = mx25l_close; sc->sc_disk->d_strategy = mx25l_strategy; sc->sc_disk->d_getattr = mx25l_getattr; sc->sc_disk->d_ioctl = mx25l_ioctl; sc->sc_disk->d_name = "flash/spi"; sc->sc_disk->d_drv1 = sc; sc->sc_disk->d_maxsize = DFLTPHYS; sc->sc_disk->d_sectorsize = MX25L_SECTORSIZE; sc->sc_disk->d_mediasize = ident->sectorsize * ident->sectorcount; sc->sc_disk->d_stripesize = sc->sc_erasesize; sc->sc_disk->d_unit = device_get_unit(sc->sc_dev); sc->sc_disk->d_dump = NULL; /* NB: no dumps */ strlcpy(sc->sc_disk->d_descr, ident->name, sizeof(sc->sc_disk->d_descr)); disk_create(sc->sc_disk, DISK_VERSION); bioq_init(&sc->sc_bio_queue); kproc_create(&mx25l_task, sc, &sc->sc_p, 0, 0, "task: mx25l flash"); sc->sc_taskstate = TSTATE_RUNNING; device_printf(sc->sc_dev, "device type %s, size %dK in %d sectors of %dK, erase size %dK\n", ident->name, ident->sectorcount * ident->sectorsize / 1024, ident->sectorcount, ident->sectorsize / 1024, sc->sc_erasesize / 1024); return (0); } static int mx25l_detach(device_t dev) { struct mx25l_softc *sc; int err; sc = device_get_softc(dev); err = 0; M25PXX_LOCK(sc); if (sc->sc_taskstate == TSTATE_RUNNING) { sc->sc_taskstate = TSTATE_STOPPING; wakeup(sc); while (err == 0 && sc->sc_taskstate != TSTATE_STOPPED) { err = msleep(sc, &sc->sc_mtx, 0, "mx25dt", hz * 3); if (err != 0) { sc->sc_taskstate = TSTATE_RUNNING; device_printf(sc->sc_dev, "Failed to stop queue task\n"); } } } M25PXX_UNLOCK(sc); if (err == 0 && sc->sc_taskstate == TSTATE_STOPPED) { disk_destroy(sc->sc_disk); bioq_flush(&sc->sc_bio_queue, NULL, ENXIO); M25PXX_LOCK_DESTROY(sc); } return (err); } static int mx25l_open(struct disk *dp) { return (0); } static int mx25l_close(struct disk *dp) { return (0); } static int mx25l_ioctl(struct disk *dp, u_long cmd, void *data, int fflag, struct thread *td) { return (EINVAL); } static void mx25l_strategy(struct bio *bp) { struct mx25l_softc *sc; sc = (struct mx25l_softc *)bp->bio_disk->d_drv1; M25PXX_LOCK(sc); bioq_disksort(&sc->sc_bio_queue, bp); wakeup(sc); M25PXX_UNLOCK(sc); } static int mx25l_getattr(struct bio *bp) { struct mx25l_softc *sc; device_t dev; if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) return (ENXIO); sc = bp->bio_disk->d_drv1; dev = sc->sc_dev; if (strcmp(bp->bio_attribute, "SPI::device") == 0) { if (bp->bio_length != sizeof(dev)) return (EFAULT); bcopy(&dev, bp->bio_data, sizeof(dev)); } else return (-1); return (0); } static void mx25l_task(void *arg) { struct mx25l_softc *sc = (struct mx25l_softc*)arg; struct bio *bp; device_t dev; for (;;) { dev = sc->sc_dev; M25PXX_LOCK(sc); do { if (sc->sc_taskstate == TSTATE_STOPPING) { sc->sc_taskstate = TSTATE_STOPPED; M25PXX_UNLOCK(sc); wakeup(sc); kproc_exit(0); } bp = bioq_first(&sc->sc_bio_queue); if (bp == NULL) msleep(sc, &sc->sc_mtx, PRIBIO, "mx25jq", 0); } while (bp == NULL); bioq_remove(&sc->sc_bio_queue, bp); M25PXX_UNLOCK(sc); switch (bp->bio_cmd) { case BIO_READ: bp->bio_error = mx25l_read(sc, bp->bio_offset, bp->bio_data, bp->bio_bcount); break; case BIO_WRITE: bp->bio_error = mx25l_write(sc, bp->bio_offset, bp->bio_data, bp->bio_bcount); break; default: bp->bio_error = EINVAL; } biodone(bp); } } static devclass_t mx25l_devclass; static device_method_t mx25l_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mx25l_probe), DEVMETHOD(device_attach, mx25l_attach), DEVMETHOD(device_detach, mx25l_detach), { 0, 0 } }; static driver_t mx25l_driver = { "mx25l", mx25l_methods, sizeof(struct mx25l_softc), }; DRIVER_MODULE(mx25l, spibus, mx25l_driver, mx25l_devclass, 0, 0); MODULE_DEPEND(mx25l, spibus, 1, 1, 1); #ifdef FDT MODULE_DEPEND(mx25l, fdt_slicer, 1, 1, 1); -SPIBUS_PNP_INFO(compat_data); +SPIBUS_FDT_PNP_INFO(compat_data); #endif Index: stable/12/sys/dev/iicbus/ds1307.c =================================================================== --- stable/12/sys/dev/iicbus/ds1307.c (revision 349313) +++ stable/12/sys/dev/iicbus/ds1307.c (revision 349314) @@ -1,437 +1,438 @@ /*- * Copyright (c) 2015 Luiz Otavio O Souza * 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 Maxim DS1307 I2C real-time clock/calendar. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include "clock_if.h" #include "iicbus_if.h" struct ds1307_softc { device_t sc_dev; struct intr_config_hook enum_hook; uint8_t sc_ctrl; bool sc_mcp7941x; bool sc_use_ampm; }; static void ds1307_start(void *); #ifdef FDT static const struct ofw_compat_data ds1307_compat_data[] = { {"dallas,ds1307", (uintptr_t)"Dallas DS1307 RTC"}, {"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"}, {"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"}, { NULL, 0 } }; #endif static int ds1307_read1(device_t dev, uint8_t reg, uint8_t *data) { return (iicdev_readfrom(dev, reg, data, 1, IIC_INTRWAIT)); } static int ds1307_write1(device_t dev, uint8_t reg, uint8_t data) { return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT)); } static int ds1307_ctrl_read(struct ds1307_softc *sc) { int error; sc->sc_ctrl = 0; error = ds1307_read1(sc->sc_dev, DS1307_CONTROL, &sc->sc_ctrl); if (error) { device_printf(sc->sc_dev, "cannot read from RTC.\n"); return (error); } return (0); } static int ds1307_ctrl_write(struct ds1307_softc *sc) { int error; uint8_t ctrl; ctrl = sc->sc_ctrl & DS1307_CTRL_MASK; error = ds1307_write1(sc->sc_dev, DS1307_CONTROL, ctrl); if (error != 0) device_printf(sc->sc_dev, "cannot write to RTC.\n"); return (error); } static int ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS) { int sqwe, error, newv, sqwe_bit; struct ds1307_softc *sc; sc = (struct ds1307_softc *)arg1; error = ds1307_ctrl_read(sc); if (error != 0) return (error); if (sc->sc_mcp7941x) sqwe_bit = MCP7941X_CTRL_SQWE; else sqwe_bit = DS1307_CTRL_SQWE; sqwe = newv = (sc->sc_ctrl & sqwe_bit) ? 1 : 0; error = sysctl_handle_int(oidp, &newv, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (sqwe != newv) { sc->sc_ctrl &= ~sqwe_bit; if (newv) sc->sc_ctrl |= sqwe_bit; error = ds1307_ctrl_write(sc); if (error != 0) return (error); } return (error); } static int ds1307_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS) { int ds1307_sqw_freq[] = { 1, 4096, 8192, 32768 }; int error, freq, i, newf, tmp; struct ds1307_softc *sc; sc = (struct ds1307_softc *)arg1; error = ds1307_ctrl_read(sc); if (error != 0) return (error); tmp = (sc->sc_ctrl & DS1307_CTRL_RS_MASK); if (tmp >= nitems(ds1307_sqw_freq)) tmp = nitems(ds1307_sqw_freq) - 1; freq = ds1307_sqw_freq[tmp]; error = sysctl_handle_int(oidp, &freq, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (freq != ds1307_sqw_freq[tmp]) { newf = 0; for (i = 0; i < nitems(ds1307_sqw_freq); i++) if (freq >= ds1307_sqw_freq[i]) newf = i; sc->sc_ctrl &= ~DS1307_CTRL_RS_MASK; sc->sc_ctrl |= newf; error = ds1307_ctrl_write(sc); if (error != 0) return (error); } return (error); } static int ds1307_sqw_out_sysctl(SYSCTL_HANDLER_ARGS) { int sqwe, error, newv; struct ds1307_softc *sc; sc = (struct ds1307_softc *)arg1; error = ds1307_ctrl_read(sc); if (error != 0) return (error); sqwe = newv = (sc->sc_ctrl & DS1307_CTRL_OUT) ? 1 : 0; error = sysctl_handle_int(oidp, &newv, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (sqwe != newv) { sc->sc_ctrl &= ~DS1307_CTRL_OUT; if (newv) sc->sc_ctrl |= DS1307_CTRL_OUT; error = ds1307_ctrl_write(sc); if (error != 0) return (error); } return (error); } static int ds1307_probe(device_t dev) { #ifdef FDT const struct ofw_compat_data *compat; if (!ofw_bus_status_okay(dev)) return (ENXIO); compat = ofw_bus_search_compatible(dev, ds1307_compat_data); if (compat->ocd_str == NULL) return (ENXIO); device_set_desc(dev, (const char *)compat->ocd_data); return (BUS_PROBE_DEFAULT); #else device_set_desc(dev, "Maxim DS1307 RTC"); return (BUS_PROBE_NOWILDCARD); #endif } static int ds1307_attach(device_t dev) { struct ds1307_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; sc->enum_hook.ich_func = ds1307_start; sc->enum_hook.ich_arg = dev; #ifdef FDT if (ofw_bus_is_compatible(dev, "microchip,mcp7941x")) sc->sc_mcp7941x = 1; #endif /* * We have to wait until interrupts are enabled. Usually I2C read * and write only works when the interrupts are available. */ if (config_intrhook_establish(&sc->enum_hook) != 0) return (ENOMEM); return (0); } static int ds1307_detach(device_t dev) { clock_unregister(dev); return (0); } static void ds1307_start(void *xdev) { device_t dev; struct ds1307_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; uint8_t secs; uint8_t osc_en; dev = (device_t)xdev; sc = device_get_softc(dev); ctx = device_get_sysctl_ctx(dev); tree_node = device_get_sysctl_tree(dev); tree = SYSCTL_CHILDREN(tree_node); config_intrhook_disestablish(&sc->enum_hook); /* Check if the oscillator is disabled. */ if (ds1307_read1(sc->sc_dev, DS1307_SECS, &secs) != 0) { device_printf(sc->sc_dev, "cannot read from RTC.\n"); return; } if (sc->sc_mcp7941x) osc_en = 0x80; else osc_en = 0x00; if (((secs & DS1307_SECS_CH) ^ osc_en) != 0) { device_printf(sc->sc_dev, "WARNING: RTC clock stopped, check the battery.\n"); } /* Configuration parameters. */ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqwe", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, ds1307_sqwe_sysctl, "IU", "DS1307 square-wave enable"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_freq", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, ds1307_sqw_freq_sysctl, "IU", "DS1307 square-wave output frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_out", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, ds1307_sqw_out_sysctl, "IU", "DS1307 square-wave output state"); /* * Register as a clock with 1 second resolution. Schedule the * clock_settime() method to be called just after top-of-second; * resetting the time resets top-of-second in the hardware. */ clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_ADJ); clock_schedule(dev, 1); } static int ds1307_gettime(device_t dev, struct timespec *ts) { int error; struct bcd_clocktime bct; struct ds1307_softc *sc; uint8_t data[7], hourmask, st_mask; sc = device_get_softc(dev); error = iicdev_readfrom(sc->sc_dev, DS1307_SECS, data, sizeof(data), IIC_INTRWAIT); if (error != 0) { device_printf(dev, "cannot read from RTC.\n"); return (error); } /* If the clock halted, we don't have good data. */ if (sc->sc_mcp7941x) st_mask = 0x80; else st_mask = 0x00; if (((data[DS1307_SECS] & DS1307_SECS_CH) ^ st_mask) != 0) return (EINVAL); /* If chip is in AM/PM mode remember that. */ if (data[DS1307_HOUR] & DS1307_HOUR_USE_AMPM) { sc->sc_use_ampm = true; hourmask = DS1307_HOUR_MASK_12HR; } else hourmask = DS1307_HOUR_MASK_24HR; bct.nsec = 0; bct.ispm = (data[DS1307_HOUR] & DS1307_HOUR_IS_PM) != 0; bct.sec = data[DS1307_SECS] & DS1307_SECS_MASK; bct.min = data[DS1307_MINS] & DS1307_MINS_MASK; bct.hour = data[DS1307_HOUR] & hourmask; bct.day = data[DS1307_DATE] & DS1307_DATE_MASK; bct.mon = data[DS1307_MONTH] & DS1307_MONTH_MASK; bct.year = data[DS1307_YEAR] & DS1307_YEAR_MASK; clock_dbgprint_bcd(sc->sc_dev, CLOCK_DBG_READ, &bct); return (clock_bcd_to_ts(&bct, ts, sc->sc_use_ampm)); } static int ds1307_settime(device_t dev, struct timespec *ts) { struct bcd_clocktime bct; struct ds1307_softc *sc; int error, year; uint8_t data[7]; uint8_t 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(); clock_ts_to_bcd(ts, &bct, sc->sc_use_ampm); clock_dbgprint_bcd(sc->sc_dev, CLOCK_DBG_WRITE, &bct); /* If the chip is in AM/PM mode, adjust hour and set flags as needed. */ if (sc->sc_use_ampm) { pmflags = DS1307_HOUR_USE_AMPM; if (bct.ispm) pmflags |= DS1307_HOUR_IS_PM; } else pmflags = 0; data[DS1307_SECS] = bct.sec; data[DS1307_MINS] = bct.min; data[DS1307_HOUR] = bct.hour | pmflags; data[DS1307_DATE] = bct.day; data[DS1307_WEEKDAY] = bct.dow; data[DS1307_MONTH] = bct.mon; data[DS1307_YEAR] = bct.year & 0xff; if (sc->sc_mcp7941x) { data[DS1307_SECS] |= MCP7941X_SECS_ST; data[DS1307_WEEKDAY] |= MCP7941X_WEEKDAY_VBATEN; year = bcd2bin(bct.year >> 8) * 100 + bcd2bin(bct.year & 0xff); if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) data[DS1307_MONTH] |= MCP7941X_MONTH_LPYR; } /* Write the time back to RTC. */ error = iicdev_writeto(sc->sc_dev, DS1307_SECS, data, sizeof(data), IIC_INTRWAIT); if (error != 0) device_printf(dev, "cannot write to RTC.\n"); return (error); } static device_method_t ds1307_methods[] = { DEVMETHOD(device_probe, ds1307_probe), DEVMETHOD(device_attach, ds1307_attach), DEVMETHOD(device_detach, ds1307_detach), DEVMETHOD(clock_gettime, ds1307_gettime), DEVMETHOD(clock_settime, ds1307_settime), DEVMETHOD_END }; static driver_t ds1307_driver = { "ds1307", ds1307_methods, sizeof(struct ds1307_softc), }; static devclass_t ds1307_devclass; DRIVER_MODULE(ds1307, iicbus, ds1307_driver, ds1307_devclass, NULL, NULL); MODULE_VERSION(ds1307, 1); MODULE_DEPEND(ds1307, iicbus, 1, 1, 1); +IICBUS_FDT_PNP_INFO(ds1307_compat_data); Index: stable/12/sys/dev/iicbus/ds13rtc.c =================================================================== --- stable/12/sys/dev/iicbus/ds13rtc.c (revision 349313) +++ stable/12/sys/dev/iicbus/ds13rtc.c (revision 349314) @@ -1,619 +1,620 @@ /*- * 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/icee.c =================================================================== --- stable/12/sys/dev/iicbus/icee.c (revision 349313) +++ stable/12/sys/dev/iicbus/icee.c (revision 349314) @@ -1,394 +1,395 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 M. Warner Losh. * * 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$"); /* * Generic IIC eeprom support, modeled after the AT24C family of products. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #endif #include #include #include "iicbus_if.h" /* * AT24 parts have a "write page size" that differs per-device, and a "read page * size" that is always equal to the full device size. We define maximum values * here to limit how long we occupy the bus with a single transfer, and because * there are temporary buffers of these sizes allocated on the stack. */ #define MAX_RD_SZ 256 /* Largest read size we support */ #define MAX_WR_SZ 256 /* Largest write size we support */ struct icee_softc { device_t dev; /* Myself */ struct cdev *cdev; /* user interface */ int addr; /* Slave address on the bus */ int size; /* How big am I? */ int type; /* What address type 8 or 16 bit? */ int wr_sz; /* What's the write page size */ }; #ifdef FDT struct eeprom_desc { int type; int size; int wr_sz; const char *name; }; static struct eeprom_desc type_desc[] = { { 8, 128, 8, "AT24C01"}, { 8, 256, 8, "AT24C02"}, { 8, 512, 16, "AT24C04"}, { 8, 1024, 16, "AT24C08"}, { 8, 2 * 1024, 16, "AT24C16"}, {16, 4 * 1024, 32, "AT24C32"}, {16, 8 * 1024, 32, "AT24C64"}, {16, 16 * 1024, 64, "AT24C128"}, {16, 32 * 1024, 64, "AT24C256"}, {16, 64 * 1024, 128, "AT24C512"}, {16, 128 * 1024, 256, "AT24CM01"}, }; static struct ofw_compat_data compat_data[] = { {"atmel,24c01", (uintptr_t)(&type_desc[0])}, {"atmel,24c02", (uintptr_t)(&type_desc[1])}, {"atmel,24c04", (uintptr_t)(&type_desc[2])}, {"atmel,24c08", (uintptr_t)(&type_desc[3])}, {"atmel,24c16", (uintptr_t)(&type_desc[4])}, {"atmel,24c32", (uintptr_t)(&type_desc[5])}, {"atmel,24c64", (uintptr_t)(&type_desc[6])}, {"atmel,24c128", (uintptr_t)(&type_desc[7])}, {"atmel,24c256", (uintptr_t)(&type_desc[8])}, {"atmel,24c512", (uintptr_t)(&type_desc[9])}, {"atmel,24c1024", (uintptr_t)(&type_desc[10])}, {NULL, (uintptr_t)NULL}, }; #endif #define CDEV2SOFTC(dev) ((dev)->si_drv1) /* cdev routines */ static d_open_t icee_open; static d_close_t icee_close; static d_read_t icee_read; static d_write_t icee_write; static struct cdevsw icee_cdevsw = { .d_version = D_VERSION, .d_flags = D_TRACKCLOSE, .d_open = icee_open, .d_close = icee_close, .d_read = icee_read, .d_write = icee_write }; #ifdef FDT static int icee_probe(device_t dev) { struct eeprom_desc *d; if (!ofw_bus_status_okay(dev)) return (ENXIO); d = (struct eeprom_desc *) ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (d == NULL) return (ENXIO); device_set_desc(dev, d->name); return (BUS_PROBE_DEFAULT); } static void icee_init(struct icee_softc *sc) { struct eeprom_desc *d; d = (struct eeprom_desc *) ofw_bus_search_compatible(sc->dev, compat_data)->ocd_data; if (d == NULL) return; /* attach will see sc->size == 0 and return error */ sc->size = d->size; sc->type = d->type; sc->wr_sz = d->wr_sz; } #else /* !FDT */ static int icee_probe(device_t dev) { device_set_desc(dev, "I2C EEPROM"); return (BUS_PROBE_NOWILDCARD); } static void icee_init(struct icee_softc *sc) { const char *dname; int dunit; dname = device_get_name(sc->dev); dunit = device_get_unit(sc->dev); resource_int_value(dname, dunit, "size", &sc->size); resource_int_value(dname, dunit, "type", &sc->type); resource_int_value(dname, dunit, "wr_sz", &sc->wr_sz); } #endif /* FDT */ static int icee_attach(device_t dev) { struct icee_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; sc->dev = dev; sc->addr = iicbus_get_addr(dev); icee_init(sc); if (sc->size == 0 || sc->type == 0 || sc->wr_sz == 0) { device_printf(sc->dev, "Missing config data, " "these cannot be zero: size %d type %d wr_sz %d\n", sc->size, sc->type, sc->wr_sz); return (EINVAL); } if (bootverbose) device_printf(dev, "size: %d bytes, addressing: %d-bits\n", sc->size, sc->type); sc->cdev = make_dev(&icee_cdevsw, device_get_unit(dev), UID_ROOT, GID_WHEEL, 0600, "icee%d", device_get_unit(dev)); if (sc->cdev == NULL) { return (ENOMEM); } sc->cdev->si_drv1 = sc; ctx = device_get_sysctl_ctx(dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "address_size", CTLFLAG_RD, &sc->type, 0, "Memory array address size in bits"); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "device_size", CTLFLAG_RD, &sc->size, 0, "Memory array capacity in bytes"); SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "write_size", CTLFLAG_RD, &sc->wr_sz, 0, "Memory array page write size in bytes"); return (0); } static int icee_detach(device_t dev) { struct icee_softc *sc = device_get_softc(dev); destroy_dev(sc->cdev); return (0); } static int icee_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct icee_softc *sc; sc = CDEV2SOFTC(dev); if (device_get_state(sc->dev) < DS_BUSY) device_busy(sc->dev); return (0); } static int icee_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct icee_softc *sc; sc = CDEV2SOFTC(dev); device_unbusy(sc->dev); return (0); } static int icee_read(struct cdev *dev, struct uio *uio, int ioflag) { struct icee_softc *sc; uint8_t addr[2]; uint8_t data[MAX_RD_SZ]; int error, i, len, slave; struct iic_msg msgs[2] = { { 0, IIC_M_WR, 1, addr }, { 0, IIC_M_RD, 0, data }, }; sc = CDEV2SOFTC(dev); if (uio->uio_offset == sc->size) return (0); if (uio->uio_offset > sc->size) return (EIO); if (sc->type != 8 && sc->type != 16) return (EINVAL); slave = error = 0; while (uio->uio_resid > 0) { if (uio->uio_offset >= sc->size) break; len = MIN(MAX_RD_SZ - (uio->uio_offset & (MAX_RD_SZ - 1)), uio->uio_resid); switch (sc->type) { case 8: slave = (uio->uio_offset >> 7) | sc->addr; msgs[0].len = 1; msgs[1].len = len; addr[0] = uio->uio_offset & 0xff; break; case 16: slave = sc->addr | (uio->uio_offset >> 15); msgs[0].len = 2; msgs[1].len = len; addr[0] = (uio->uio_offset >> 8) & 0xff; addr[1] = uio->uio_offset & 0xff; break; } for (i = 0; i < 2; i++) msgs[i].slave = slave; error = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT); if (error) { error = iic2errno(error); break; } error = uiomove(data, len, uio); if (error) break; } return (error); } /* * Write to the part. We use three transfers here since we're actually * doing a write followed by a read to make sure that the write finished. * It is easier to encode the dummy read here than to break things up * into smaller chunks... */ static int icee_write(struct cdev *dev, struct uio *uio, int ioflag) { struct icee_softc *sc; int error, len, slave, waitlimit; uint8_t data[MAX_WR_SZ + 2]; struct iic_msg wr[1] = { { 0, IIC_M_WR, 0, data }, }; struct iic_msg rd[1] = { { 0, IIC_M_RD, 1, data }, }; sc = CDEV2SOFTC(dev); if (uio->uio_offset >= sc->size) return (EIO); if (sc->type != 8 && sc->type != 16) return (EINVAL); slave = error = 0; while (uio->uio_resid > 0) { if (uio->uio_offset >= sc->size) break; len = MIN(sc->wr_sz - (uio->uio_offset & (sc->wr_sz - 1)), uio->uio_resid); switch (sc->type) { case 8: slave = (uio->uio_offset >> 7) | sc->addr; wr[0].len = 1 + len; data[0] = uio->uio_offset & 0xff; break; case 16: slave = sc->addr | (uio->uio_offset >> 15); wr[0].len = 2 + len; data[0] = (uio->uio_offset >> 8) & 0xff; data[1] = uio->uio_offset & 0xff; break; } wr[0].slave = slave; error = uiomove(data + sc->type / 8, len, uio); if (error) break; error = iicbus_transfer_excl(sc->dev, wr, 1, IIC_INTRWAIT); if (error) { error = iic2errno(error); break; } /* Read after write to wait for write-done. */ waitlimit = 10000; rd[0].slave = slave; do { error = iicbus_transfer_excl(sc->dev, rd, 1, IIC_INTRWAIT); } while (waitlimit-- > 0 && error != 0); if (error) { error = iic2errno(error); break; } } return error; } static device_method_t icee_methods[] = { DEVMETHOD(device_probe, icee_probe), DEVMETHOD(device_attach, icee_attach), DEVMETHOD(device_detach, icee_detach), DEVMETHOD_END }; static driver_t icee_driver = { "icee", icee_methods, sizeof(struct icee_softc), }; static devclass_t icee_devclass; DRIVER_MODULE(icee, iicbus, icee_driver, icee_devclass, 0, 0); MODULE_VERSION(icee, 1); MODULE_DEPEND(icee, iicbus, 1, 1, 1); +IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/iicbus.h =================================================================== --- stable/12/sys/dev/iicbus/iicbus.h (revision 349313) +++ stable/12/sys/dev/iicbus/iicbus.h (revision 349314) @@ -1,83 +1,89 @@ /*- * 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. * * $FreeBSD$ * */ #ifndef __IICBUS_H #define __IICBUS_H #include #include #define IICBUS_IVAR(d) (struct iicbus_ivar *) device_get_ivars(d) #define IICBUS_SOFTC(d) (struct iicbus_softc *) device_get_softc(d) struct iicbus_softc { device_t dev; /* Myself */ device_t owner; /* iicbus owner device structure */ u_int owncount; /* iicbus ownership nesting count */ u_char started; /* address of the 'started' slave * 0 if no start condition succeeded */ u_char strict; /* deny operations that violate the * I2C protocol */ struct mtx lock; u_int bus_freq; /* Configured bus Hz. */ }; struct iicbus_ivar { uint32_t addr; struct resource_list rl; bool nostop; }; enum { IICBUS_IVAR_ADDR, /* Address or base address */ IICBUS_IVAR_NOSTOP, /* nostop defaults */ }; #define IICBUS_ACCESSOR(A, B, T) \ __BUS_ACCESSOR(iicbus, A, IICBUS, B, T) IICBUS_ACCESSOR(addr, ADDR, uint32_t) IICBUS_ACCESSOR(nostop, NOSTOP, bool) #define IICBUS_LOCK(sc) mtx_lock(&(sc)->lock) #define IICBUS_UNLOCK(sc) mtx_unlock(&(sc)->lock) #define IICBUS_ASSERT_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED) +#ifdef FDT +#define IICBUS_FDT_PNP_INFO(t) FDTCOMPAT_PNP_INFO(t, iicbus) +#else +#define IICBUS_FDT_PNP_INFO(t) +#endif + int iicbus_generic_intr(device_t dev, int event, char *buf); void iicbus_init_frequency(device_t dev, u_int bus_freq); extern driver_t iicbus_driver; extern devclass_t iicbus_devclass; extern driver_t ofw_iicbus_driver; extern devclass_t ofw_iicbus_devclass; #endif Index: stable/12/sys/dev/iicbus/isl12xx.c =================================================================== --- stable/12/sys/dev/iicbus/isl12xx.c (revision 349313) +++ stable/12/sys/dev/iicbus/isl12xx.c (revision 349314) @@ -1,358 +1,359 @@ /*- * 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, 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 349313) +++ stable/12/sys/dev/iicbus/nxprtc.c (revision 349314) @@ -1,828 +1,829 @@ /*- * 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), 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 = iicdev_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 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 = 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); 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, 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 349313) +++ stable/12/sys/dev/iicbus/rtc8583.c (revision 349314) @@ -1,302 +1,303 @@ /*- * Copyright (c) 2017 Hiroki Mori. All rights reserved. * 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. * * 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/sys/dev/iicbus/sy8106a.c =================================================================== --- stable/12/sys/dev/iicbus/sy8106a.c (revision 349313) +++ stable/12/sys/dev/iicbus/sy8106a.c (revision 349314) @@ -1,302 +1,303 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE 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. * * $FreeBSD$ */ /* * Silergy Corp. SY8106A buck regulator */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include "regdev_if.h" #define VOUT1_SEL 0x01 #define SEL_GO (1 << 7) #define SEL_VOLTAGE_MASK 0x7f #define SEL_VOLTAGE_BASE 680000 /* uV */ #define SEL_VOLTAGE_STEP 10000 /* uV */ #define VOUT_COM 0x02 #define COM_DISABLE (1 << 0) #define SYS_STATUS 0x06 static struct ofw_compat_data compat_data[] = { { "silergy,sy8106a", 1 }, { NULL, 0 } }; struct sy8106a_reg_sc { struct regnode *regnode; device_t base_dev; phandle_t xref; struct regnode_std_param *param; }; struct sy8106a_softc { uint16_t addr; /* Regulator */ struct sy8106a_reg_sc *reg; }; static int sy8106a_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { struct sy8106a_softc *sc; struct iic_msg msg[2]; sc = device_get_softc(dev); msg[0].slave = sc->addr; msg[0].flags = IIC_M_WR; msg[0].len = 1; msg[0].buf = ® msg[1].slave = sc->addr; msg[1].flags = IIC_M_RD; msg[1].len = size; msg[1].buf = data; return (iicbus_transfer(dev, msg, 2)); } static int sy8106a_write(device_t dev, uint8_t reg, uint8_t val) { struct sy8106a_softc *sc; struct iic_msg msg; uint8_t buffer[2]; sc = device_get_softc(dev); buffer[0] = reg; buffer[1] = val; msg.slave = sc->addr; msg.flags = IIC_M_WR; msg.len = 2; msg.buf = buffer; return (iicbus_transfer(dev, &msg, 1)); } static int sy8106a_regnode_init(struct regnode *regnode) { return (0); } static int sy8106a_regnode_enable(struct regnode *regnode, bool enable, int *udelay) { struct sy8106a_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); sy8106a_read(sc->base_dev, VOUT_COM, &val, 1); if (enable) val &= ~COM_DISABLE; else val |= COM_DISABLE; sy8106a_write(sc->base_dev, VOUT_COM, val); *udelay = sc->param->ramp_delay; return (0); } static int sy8106a_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct sy8106a_reg_sc *sc; int cur_uvolt; uint8_t val, oval; sc = regnode_get_softc(regnode); /* Get current voltage */ sy8106a_read(sc->base_dev, VOUT1_SEL, &oval, 1); cur_uvolt = (oval & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + SEL_VOLTAGE_BASE; /* Set new voltage */ val = SEL_GO | ((min_uvolt - SEL_VOLTAGE_BASE) / SEL_VOLTAGE_STEP); sy8106a_write(sc->base_dev, VOUT1_SEL, val); /* Time to delay is based on the number of voltage steps */ *udelay = sc->param->ramp_delay * (abs(cur_uvolt - min_uvolt) / SEL_VOLTAGE_STEP); return (0); } static int sy8106a_regnode_get_voltage(struct regnode *regnode, int *uvolt) { struct sy8106a_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); sy8106a_read(sc->base_dev, VOUT1_SEL, &val, 1); *uvolt = (val & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + SEL_VOLTAGE_BASE; return (0); } static regnode_method_t sy8106a_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, sy8106a_regnode_init), REGNODEMETHOD(regnode_enable, sy8106a_regnode_enable), REGNODEMETHOD(regnode_set_voltage, sy8106a_regnode_set_voltage), REGNODEMETHOD(regnode_get_voltage, sy8106a_regnode_get_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(sy8106a_regnode, sy8106a_regnode_class, sy8106a_regnode_methods, sizeof(struct sy8106a_reg_sc), regnode_class); static struct sy8106a_reg_sc * sy8106a_reg_attach(device_t dev, phandle_t node) { struct sy8106a_reg_sc *reg_sc; struct regnode_init_def initdef; struct regnode *regnode; memset(&initdef, 0, sizeof(initdef)); regulator_parse_ofw_stdparam(dev, node, &initdef); initdef.id = 0; initdef.ofw_node = node; regnode = regnode_create(dev, &sy8106a_regnode_class, &initdef); if (regnode == NULL) { device_printf(dev, "cannot create regulator\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); reg_sc->regnode = regnode; reg_sc->base_dev = dev; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); regnode_register(regnode); return (reg_sc); } static int sy8106a_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *num) { struct sy8106a_softc *sc; sc = device_get_softc(dev); if (sc->reg->xref != xref) return (ENXIO); *num = 0; return (0); } static int sy8106a_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Silergy SY8106A regulator"); return (BUS_PROBE_DEFAULT); } static int sy8106a_attach(device_t dev) { struct sy8106a_softc *sc; phandle_t node; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->addr = iicbus_get_addr(dev); sc->reg = sy8106a_reg_attach(dev, node); if (sc->reg == NULL) { device_printf(dev, "cannot attach regulator\n"); return (ENXIO); } return (0); } static device_method_t sy8106a_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sy8106a_probe), DEVMETHOD(device_attach, sy8106a_attach), /* Regdev interface */ DEVMETHOD(regdev_map, sy8106a_regdev_map), DEVMETHOD_END }; static driver_t sy8106a_driver = { "sy8106a", sy8106a_methods, sizeof(struct sy8106a_softc), }; static devclass_t sy8106a_devclass; EARLY_DRIVER_MODULE(sy8106a, iicbus, sy8106a_driver, sy8106a_devclass, 0, 0, BUS_PASS_RESOURCE); MODULE_VERSION(sy8106a, 1); MODULE_DEPEND(sy8106a, iicbus, 1, 1, 1); +IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/syr827.c =================================================================== --- stable/12/sys/dev/iicbus/syr827.c (revision 349313) +++ stable/12/sys/dev/iicbus/syr827.c (revision 349314) @@ -1,354 +1,355 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2016 Jared McNeill * Copyright (c) 2018 Emmanuel Vadot * * 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 #include #include #include "iicbus_if.h" #include "regdev_if.h" #define VSEL0 0x00 #define VSEL1 0x01 #define VSEL_BUCK_EN (1 << 7) #define VSEL_NSEL_MASK 0x3F #define VSEL_VOLTAGE_BASE 712500 /* uV */ #define VSEL_VOLTAGE_STEP 12500 /* uV */ #define ID1 0x03 #define ID1_VENDOR_MASK 0xE0 #define ID1_VENDOR_SHIFT 5 #define ID1_DIE_MASK 0xF #define ID2 0x4 #define ID2_DIE_REV_MASK 0xF static struct ofw_compat_data compat_data[] = { { "silergy,syr827", 1 }, { NULL, 0 } }; struct syr827_reg_sc { struct regnode *regnode; device_t base_dev; phandle_t xref; struct regnode_std_param *param; int volt_reg; int suspend_reg; }; struct syr827_softc { uint16_t addr; struct intr_config_hook intr_hook; /* Regulator */ struct syr827_reg_sc *reg; }; static int syr827_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { struct syr827_softc *sc; struct iic_msg msg[2]; sc = device_get_softc(dev); msg[0].slave = sc->addr; msg[0].flags = IIC_M_WR; msg[0].len = 1; msg[0].buf = ® msg[1].slave = sc->addr; msg[1].flags = IIC_M_RD; msg[1].len = size; msg[1].buf = data; return (iicbus_transfer(dev, msg, 2)); } static int syr827_write(device_t dev, uint8_t reg, uint8_t val) { struct syr827_softc *sc; struct iic_msg msg; uint8_t buffer[2]; sc = device_get_softc(dev); buffer[0] = reg; buffer[1] = val; msg.slave = sc->addr; msg.flags = IIC_M_WR; msg.len = 2; msg.buf = buffer; return (iicbus_transfer(dev, &msg, 1)); } static int syr827_regnode_init(struct regnode *regnode) { return (0); } static int syr827_regnode_enable(struct regnode *regnode, bool enable, int *udelay) { struct syr827_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); syr827_read(sc->base_dev, sc->volt_reg, &val, 1); if (enable) val &= ~VSEL_BUCK_EN; else val |= VSEL_BUCK_EN; syr827_write(sc->base_dev, sc->volt_reg, val); *udelay = sc->param->ramp_delay; return (0); } static int syr827_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct syr827_reg_sc *sc; int cur_uvolt; uint8_t val; sc = regnode_get_softc(regnode); /* Get current voltage */ syr827_read(sc->base_dev, sc->volt_reg, &val, 1); cur_uvolt = (val & VSEL_NSEL_MASK) * VSEL_VOLTAGE_STEP + VSEL_VOLTAGE_BASE; /* Set new voltage */ val &= ~VSEL_NSEL_MASK; val |= ((min_uvolt - VSEL_VOLTAGE_BASE) / VSEL_VOLTAGE_STEP); syr827_write(sc->base_dev, sc->volt_reg, val); /* Time to delay is based on the number of voltage steps */ *udelay = sc->param->ramp_delay * (abs(cur_uvolt - min_uvolt) / VSEL_VOLTAGE_STEP); return (0); } static int syr827_regnode_get_voltage(struct regnode *regnode, int *uvolt) { struct syr827_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); syr827_read(sc->base_dev, sc->volt_reg, &val, 1); *uvolt = (val & VSEL_NSEL_MASK) * VSEL_VOLTAGE_STEP + VSEL_VOLTAGE_BASE; return (0); } static regnode_method_t syr827_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, syr827_regnode_init), REGNODEMETHOD(regnode_enable, syr827_regnode_enable), REGNODEMETHOD(regnode_set_voltage, syr827_regnode_set_voltage), REGNODEMETHOD(regnode_get_voltage, syr827_regnode_get_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(syr827_regnode, syr827_regnode_class, syr827_regnode_methods, sizeof(struct syr827_reg_sc), regnode_class); static struct syr827_reg_sc * syr827_reg_attach(device_t dev, phandle_t node) { struct syr827_reg_sc *reg_sc; struct regnode_init_def initdef; struct regnode *regnode; int suspend_reg; memset(&initdef, 0, sizeof(initdef)); regulator_parse_ofw_stdparam(dev, node, &initdef); initdef.id = 0; initdef.ofw_node = node; regnode = regnode_create(dev, &syr827_regnode_class, &initdef); if (regnode == NULL) { device_printf(dev, "cannot create regulator\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); reg_sc->regnode = regnode; reg_sc->base_dev = dev; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); if (OF_getencprop(node, "fcs,suspend-voltage-selector", &suspend_reg, sizeof(uint32_t)) <= 0) suspend_reg = 0; switch (suspend_reg) { case 0: reg_sc->suspend_reg = VSEL0; reg_sc->volt_reg = VSEL1; break; case 1: reg_sc->suspend_reg = VSEL1; reg_sc->volt_reg = VSEL0; break; } regnode_register(regnode); return (reg_sc); } static int syr827_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *num) { struct syr827_softc *sc; sc = device_get_softc(dev); if (sc->reg->xref != xref) return (ENXIO); *num = 0; return (0); } static int syr827_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Silergy SYR827 regulator"); return (BUS_PROBE_DEFAULT); } static void syr827_start(void *pdev) { struct syr827_softc *sc; device_t dev; uint8_t val; dev = pdev; sc = device_get_softc(dev); if (bootverbose) { syr827_read(dev, ID1, &val, 1); device_printf(dev, "Vendor ID: %x, DIE ID: %x\n", (val & ID1_VENDOR_MASK) >> ID1_VENDOR_SHIFT, val & ID1_DIE_MASK); syr827_read(dev, ID2, &val, 1); device_printf(dev, "DIE Rev: %x\n", val & ID2_DIE_REV_MASK); } config_intrhook_disestablish(&sc->intr_hook); } static int syr827_attach(device_t dev) { struct syr827_softc *sc; phandle_t node; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->addr = iicbus_get_addr(dev); sc->intr_hook.ich_func = syr827_start; sc->intr_hook.ich_arg = dev; if (config_intrhook_establish(&sc->intr_hook) != 0) return (ENOMEM); sc->reg = syr827_reg_attach(dev, node); if (sc->reg == NULL) { device_printf(dev, "cannot attach regulator\n"); return (ENXIO); } return (0); } static device_method_t syr827_methods[] = { /* Device interface */ DEVMETHOD(device_probe, syr827_probe), DEVMETHOD(device_attach, syr827_attach), /* Regdev interface */ DEVMETHOD(regdev_map, syr827_regdev_map), DEVMETHOD_END }; static driver_t syr827_driver = { "syr827", syr827_methods, sizeof(struct syr827_softc), }; static devclass_t syr827_devclass; EARLY_DRIVER_MODULE(syr827, iicbus, syr827_driver, syr827_devclass, 0, 0, BUS_PASS_RESOURCE); MODULE_VERSION(syr827, 1); MODULE_DEPEND(syr827, iicbus, 1, 1, 1); +IICBUS_FDT_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/twsi/a10_twsi.c =================================================================== --- stable/12/sys/dev/iicbus/twsi/a10_twsi.c (revision 349313) +++ stable/12/sys/dev/iicbus/twsi/a10_twsi.c (revision 349314) @@ -1,160 +1,161 @@ /*- * Copyright (c) 2016 Emmanuel Vadot * 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 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 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 #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define TWI_ADDR 0x0 #define TWI_XADDR 0x4 #define TWI_DATA 0x8 #define TWI_CNTR 0xC #define TWI_STAT 0x10 #define TWI_CCR 0x14 #define TWI_SRST 0x18 #define TWI_EFR 0x1C #define TWI_LCR 0x20 static struct ofw_compat_data compat_data[] = { {"allwinner,sun4i-a10-i2c", 1}, {"allwinner,sun6i-a31-i2c", 1}, {"allwinner,sun8i-a83t-i2c", 1}, {NULL, 0}, }; static int a10_twsi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner Integrated I2C Bus Controller"); return (BUS_PROBE_DEFAULT); } static int a10_twsi_attach(device_t dev) { struct twsi_softc *sc; clk_t clk; hwreset_t rst; int error; sc = device_get_softc(dev); /* De-assert reset */ if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "could not de-assert reset\n"); return (error); } } /* Activate clock */ error = clk_get_by_ofw_index(dev, 0, 0, &clk); if (error != 0) { device_printf(dev, "could not find clock\n"); return (error); } error = clk_enable(clk); if (error != 0) { device_printf(dev, "could not enable clock\n"); return (error); } sc->reg_data = TWI_DATA; sc->reg_slave_addr = TWI_ADDR; sc->reg_slave_ext_addr = TWI_XADDR; sc->reg_control = TWI_CNTR; sc->reg_status = TWI_STAT; sc->reg_baud_rate = TWI_CCR; sc->reg_soft_reset = TWI_SRST; /* Setup baud rate params */ sc->baud_rate[IIC_SLOW].param = TWSI_BAUD_RATE_PARAM(11, 2); sc->baud_rate[IIC_FAST].param = TWSI_BAUD_RATE_PARAM(11, 2); sc->baud_rate[IIC_FASTEST].param = TWSI_BAUD_RATE_PARAM(2, 2); return (twsi_attach(dev)); } static phandle_t a10_twsi_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } static device_method_t a10_twsi_methods[] = { /* device interface */ DEVMETHOD(device_probe, a10_twsi_probe), DEVMETHOD(device_attach, a10_twsi_attach), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, a10_twsi_get_node), { 0, 0 } }; DEFINE_CLASS_1(iichb, a10_twsi_driver, a10_twsi_methods, sizeof(struct twsi_softc), twsi_driver); static devclass_t a10_twsi_devclass; EARLY_DRIVER_MODULE(a10_twsi, simplebus, a10_twsi_driver, a10_twsi_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(iicbus, a10_twsi, iicbus_driver, iicbus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); MODULE_DEPEND(a10_twsi, iicbus, 1, 1, 1); +SIMPLEBUS_PNP_INFO(compat_data); Index: stable/12/sys/dev/iicbus/twsi/mv_twsi.c =================================================================== --- stable/12/sys/dev/iicbus/twsi/mv_twsi.c (revision 349313) +++ stable/12/sys/dev/iicbus/twsi/mv_twsi.c (revision 349314) @@ -1,263 +1,264 @@ /*- * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. * All rights reserved. * * Developed by Semihalf. * * 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. * 3. Neither the name of MARVELL nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY 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 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. */ /* * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell * and Allwinner SoCs. Supports master operation only, and works in polling mode. * * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define MV_TWSI_NAME "twsi" #define IICBUS_DEVNAME "iicbus" #define TWSI_ADDR 0x00 #define TWSI_DATA 0x04 #define TWSI_CNTR 0x08 #define TWSI_XADDR 0x10 #define TWSI_STAT 0x0c #define TWSI_BAUD_RATE 0x0c #define TWSI_SRST 0x1c #define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1))) #define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */ #define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */ #define TWSI_DEBUG #undef TWSI_DEBUG #ifdef TWSI_DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif static phandle_t mv_twsi_get_node(device_t, device_t); static int mv_twsi_probe(device_t); static int mv_twsi_attach(device_t); static struct ofw_compat_data compat_data[] = { { "mrvl,twsi", true }, { "marvell,mv64xxx-i2c", true }, { NULL, false } }; static device_method_t mv_twsi_methods[] = { /* device interface */ DEVMETHOD(device_probe, mv_twsi_probe), DEVMETHOD(device_attach, mv_twsi_attach), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, mv_twsi_get_node), DEVMETHOD_END }; DEFINE_CLASS_1(twsi, mv_twsi_driver, mv_twsi_methods, sizeof(struct twsi_softc), twsi_driver); static devclass_t mv_twsi_devclass; DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0); DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); MODULE_DEPEND(twsi, iicbus, 1, 1, 1); +SIMPLEBUS_PNP_INFO(compat_data); static phandle_t mv_twsi_get_node(device_t bus, device_t dev) { /* Used by ofw_iicbus. */ return (ofw_bus_get_node(bus)); } static int mv_twsi_probe(device_t dev) { struct twsi_softc *sc; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); sc->reg_data = TWSI_DATA; sc->reg_slave_addr = TWSI_ADDR; sc->reg_slave_ext_addr = TWSI_XADDR; sc->reg_control = TWSI_CNTR; sc->reg_status = TWSI_STAT; sc->reg_baud_rate = TWSI_BAUD_RATE; sc->reg_soft_reset = TWSI_SRST; device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); return (BUS_PROBE_DEFAULT); } #define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) static void mv_twsi_cal_baud_rate(const uint32_t target, struct twsi_baud_rate *rate) { uint32_t clk, cur, diff, diff0; int m, n, m0, n0; /* Calculate baud rate. */ m0 = n0 = 4; /* Default values on reset */ diff0 = 0xffffffff; clk = get_tclk(); for (n = 0; n < 8; n++) { for (m = 0; m < 16; m++) { cur = TWSI_BAUD_RATE_RAW(clk,m,n); diff = ABSSUB(target, cur); if (diff < diff0) { m0 = m; n0 = n; diff0 = diff; } } } rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0); rate->param = TWSI_BAUD_RATE_PARAM(m0, n0); rate->m = m0; rate->n = n0; } static int mv_twsi_attach(device_t dev) { struct twsi_softc *sc; phandle_t child, iicbusnode; device_t childdev; struct iicbus_ivar *devi; char dname[32]; /* 32 is taken from struct u_device */ uint32_t paddr; int len, error, ret; sc = device_get_softc(dev); mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]); mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]); if (bootverbose) device_printf(dev, "calculated baud rates are:\n" " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n" " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n", sc->baud_rate[IIC_SLOW].raw / 1000, sc->baud_rate[IIC_SLOW].m, sc->baud_rate[IIC_SLOW].n, sc->baud_rate[IIC_FAST].raw / 1000, sc->baud_rate[IIC_FAST].m, sc->baud_rate[IIC_FAST].n); ret = twsi_attach(dev); if (ret != 0) return (ret); iicbusnode = 0; /* Find iicbus as the child devices in the device tree. */ for (child = OF_child(ofw_bus_get_node(dev)); child != 0; child = OF_peer(child)) { len = OF_getproplen(child, "model"); if (len <= 0 || len > sizeof(dname)) continue; error = OF_getprop(child, "model", &dname, len); if (error == -1) continue; len = strlen(dname); if (len == strlen(IICBUS_DEVNAME) && strncasecmp(dname, IICBUS_DEVNAME, len) == 0) { iicbusnode = child; break; } } if (iicbusnode == 0) goto attach_end; /* Attach child devices onto iicbus. */ for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) { /* Get slave address. */ error = OF_getencprop(child, "i2c-address", &paddr, sizeof(paddr)); if (error == -1) error = OF_getencprop(child, "reg", &paddr, sizeof(paddr)); if (error == -1) continue; /* Get device driver name. */ len = OF_getproplen(child, "model"); if (len <= 0 || len > sizeof(dname)) continue; OF_getprop(child, "model", &dname, len); if (bootverbose) device_printf(dev, "adding a device %s at %d.\n", dname, paddr); childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1); devi = IICBUS_IVAR(childdev); devi->addr = paddr; } attach_end: bus_generic_attach(sc->iicbus); return (0); } Index: stable/12/sys/dev/ofw/ofw_bus_subr.h =================================================================== --- stable/12/sys/dev/ofw/ofw_bus_subr.h (revision 349313) +++ stable/12/sys/dev/ofw/ofw_bus_subr.h (revision 349314) @@ -1,150 +1,152 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 Marius Strobl * 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, * without modification, immediately at the beginning of the file. * 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 _DEV_OFW_OFW_BUS_SUBR_H_ #define _DEV_OFW_OFW_BUS_SUBR_H_ #include #ifdef INTRNG #include #endif #include #include "ofw_bus_if.h" #define ORIP_NOINT -1 #define ORIR_NOTFOUND 0xffffffff struct ofw_bus_iinfo { uint8_t *opi_imap; uint8_t *opi_imapmsk; int opi_imapsz; pcell_t opi_addrc; }; struct ofw_compat_data { const char *ocd_str; uintptr_t ocd_data; }; #ifdef INTRNG struct intr_map_data_fdt { struct intr_map_data hdr; phandle_t iparent; u_int ncells; pcell_t cells[]; }; #endif -#define SIMPLEBUS_PNP_DESCR "Z:compat;P:#;" -#define SIMPLEBUS_PNP_INFO(t) \ - MODULE_PNP_INFO(SIMPLEBUS_PNP_DESCR, simplebus, t, t, sizeof(t) / sizeof(t[0])); +#define FDTCOMPAT_PNP_DESCR "Z:compat;P:#;" +#define FDTCOMPAT_PNP_INFO(t, busname) \ + MODULE_PNP_INFO(FDTCOMPAT_PNP_DESCR, busname, t, t, sizeof(t) / sizeof(t[0])); + +#define SIMPLEBUS_PNP_INFO(t) FDTCOMPAT_PNP_INFO(t, simplebus) /* Generic implementation of ofw_bus_if.m methods and helper routines */ int ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *, phandle_t); void ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *); ofw_bus_get_compat_t ofw_bus_gen_get_compat; ofw_bus_get_model_t ofw_bus_gen_get_model; ofw_bus_get_name_t ofw_bus_gen_get_name; ofw_bus_get_node_t ofw_bus_gen_get_node; ofw_bus_get_type_t ofw_bus_gen_get_type; /* Helper method to report interesting OF properties in pnpinfo */ bus_child_pnpinfo_str_t ofw_bus_gen_child_pnpinfo_str; /* Routines for processing firmware interrupt maps */ void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int); int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int, void *, int, void *, int, phandle_t *); int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *, void *, void *, int, phandle_t *); /* Routines for processing msi maps */ int ofw_bus_msimap(phandle_t, uint16_t, phandle_t *, uint32_t *); /* Routines for parsing device-tree data into resource lists. */ int ofw_bus_reg_to_rl(device_t, phandle_t, pcell_t, pcell_t, struct resource_list *); int ofw_bus_assigned_addresses_to_rl(device_t, phandle_t, pcell_t, pcell_t, struct resource_list *); int ofw_bus_intr_to_rl(device_t, phandle_t, struct resource_list *, int *); int ofw_bus_intr_by_rid(device_t, phandle_t, int, phandle_t *, int *, pcell_t **); /* Helper to get device status property */ const char *ofw_bus_get_status(device_t dev); int ofw_bus_status_okay(device_t dev); int ofw_bus_node_status_okay(phandle_t node); /* Helper to get node's interrupt parent */ phandle_t ofw_bus_find_iparent(phandle_t); /* Helper routine for checking compat prop */ int ofw_bus_is_compatible(device_t, const char *); int ofw_bus_is_compatible_strict(device_t, const char *); int ofw_bus_node_is_compatible(phandle_t, const char *); /* * Helper routine to search a list of compat properties. The table is * terminated by an entry with a NULL compat-string pointer; a pointer to that * table entry is returned if none of the compat strings match for the device, * giving you control over the not-found value. Will not return NULL unless the * provided table pointer is NULL. */ const struct ofw_compat_data * ofw_bus_search_compatible(device_t, const struct ofw_compat_data *); /* Helper routine for checking existence of a prop */ int ofw_bus_has_prop(device_t, const char *); /* Helper to search for a child with a given compat prop */ phandle_t ofw_bus_find_compatible(phandle_t, const char *); /* Helper to search for a child with a given name */ phandle_t ofw_bus_find_child(phandle_t, const char *); /* Helper routine to find a device_t child matching a given phandle_t */ device_t ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node); /* Helper routines for parsing lists */ int ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, const char *cells_name, int idx, phandle_t *producer, int *ncells, pcell_t **cells); int ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, const char *cells_name, int *count); int ofw_bus_find_string_index(phandle_t node, const char *list_name, const char *name, int *idx); int ofw_bus_string_list_to_array(phandle_t node, const char *list_name, const char ***array); #endif /* !_DEV_OFW_OFW_BUS_SUBR_H_ */ Index: stable/12/sys/dev/spibus/spi.h =================================================================== --- stable/12/sys/dev/spibus/spi.h (revision 349313) +++ stable/12/sys/dev/spibus/spi.h (revision 349314) @@ -1,46 +1,48 @@ /*- * Copyright (c) 2006 M. Warner Losh * 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$ */ struct spi_command { void *tx_cmd; uint32_t tx_cmd_sz; void *rx_cmd; uint32_t rx_cmd_sz; void *tx_data; uint32_t tx_data_sz; void *rx_data; uint32_t rx_data_sz; }; #define SPI_COMMAND_INITIALIZER { 0 } #define SPI_CHIP_SELECT_HIGH 0x1 /* Chip select high (else low) */ -#define SPIBUS_PNP_DESCR "Z:compat;P:#;" -#define SPIBUS_PNP_INFO(t) \ - MODULE_PNP_INFO(SPIBUS_PNP_DESCR, spibus, t, t, sizeof(t) / sizeof(t[0])); +#ifdef FDT +#define SPIBUS_FDT_PNP_INFO(t) FDTCOMPAT_PNP_INFO(t, spibus) +#else +#define SPIBUS_FDT_PNP_INFO(t) +#endif Index: stable/12 =================================================================== --- stable/12 (revision 349313) +++ stable/12 (revision 349314) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r348169-348170,348172-348173,348183-348184