1ae1f3df4SWarner Losh /*- 2ae1f3df4SWarner Losh * Copyright (c) 2015 M. Warner Losh <imp@freebsd.org> 3ae1f3df4SWarner Losh * All rights reserved. 4ae1f3df4SWarner Losh * 5ae1f3df4SWarner Losh * Redistribution and use in source and binary forms, with or without 6ae1f3df4SWarner Losh * modification, are permitted provided that the following conditions 7ae1f3df4SWarner Losh * are met: 8ae1f3df4SWarner Losh * 1. Redistributions of source code must retain the above copyright 9ae1f3df4SWarner Losh * notice unmodified, this list of conditions, and the following 10ae1f3df4SWarner Losh * disclaimer. 11ae1f3df4SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 12ae1f3df4SWarner Losh * notice, this list of conditions and the following disclaimer in the 13ae1f3df4SWarner Losh * documentation and/or other materials provided with the distribution. 14ae1f3df4SWarner Losh * 15ae1f3df4SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16ae1f3df4SWarner Losh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17ae1f3df4SWarner Losh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18ae1f3df4SWarner Losh * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19ae1f3df4SWarner Losh * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20ae1f3df4SWarner Losh * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21ae1f3df4SWarner Losh * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22ae1f3df4SWarner Losh * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23ae1f3df4SWarner Losh * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24ae1f3df4SWarner Losh * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25ae1f3df4SWarner Losh */ 26ae1f3df4SWarner Losh 27ae1f3df4SWarner Losh #include <sys/cdefs.h> 28ae1f3df4SWarner Losh __FBSDID("$FreeBSD$"); 29ae1f3df4SWarner Losh 30ae1f3df4SWarner Losh #include <sys/param.h> 31ae1f3df4SWarner Losh #include <sys/systm.h> 32ae1f3df4SWarner Losh #include <sys/kernel.h> 33ae1f3df4SWarner Losh 34ae1f3df4SWarner Losh #include <sys/bus.h> 35ae1f3df4SWarner Losh #include <sys/errno.h> 36ae1f3df4SWarner Losh #include <sys/libkern.h> 37*e2e050c8SConrad Meyer #include <sys/lock.h> 38ae1f3df4SWarner Losh #include <sys/malloc.h> 39ae1f3df4SWarner Losh #include <sys/module.h> 40*e2e050c8SConrad Meyer #include <sys/mutex.h> 41ae1f3df4SWarner Losh 42ae1f3df4SWarner Losh #include <dev/ow/ow.h> 43ae1f3df4SWarner Losh #include <dev/ow/owll.h> 44ae1f3df4SWarner Losh #include <dev/ow/own.h> 45ae1f3df4SWarner Losh 46ae1f3df4SWarner Losh /* 47ae1f3df4SWarner Losh * lldev - link level device 48ae1f3df4SWarner Losh * ndev - network / transport device (this module) 49ae1f3df4SWarner Losh * pdev - presentation device (children of this module) 50ae1f3df4SWarner Losh */ 51ae1f3df4SWarner Losh 52ae1f3df4SWarner Losh typedef int ow_enum_fn(device_t, device_t); 53ae1f3df4SWarner Losh typedef int ow_found_fn(device_t, romid_t); 54ae1f3df4SWarner Losh 55ae1f3df4SWarner Losh struct ow_softc 56ae1f3df4SWarner Losh { 57ae1f3df4SWarner Losh device_t dev; /* Newbus driver back pointer */ 58ae1f3df4SWarner Losh struct mtx mtx; /* bus mutex */ 59ae1f3df4SWarner Losh device_t owner; /* bus owner, if != NULL */ 60ae1f3df4SWarner Losh }; 61ae1f3df4SWarner Losh 62ae1f3df4SWarner Losh struct ow_devinfo 63ae1f3df4SWarner Losh { 64ae1f3df4SWarner Losh romid_t romid; 65ae1f3df4SWarner Losh }; 66ae1f3df4SWarner Losh 67ae1f3df4SWarner Losh static int ow_acquire_bus(device_t ndev, device_t pdev, int how); 68ae1f3df4SWarner Losh static void ow_release_bus(device_t ndev, device_t pdev); 69ae1f3df4SWarner Losh 70ae1f3df4SWarner Losh #define OW_LOCK(_sc) mtx_lock(&(_sc)->mtx) 71ae1f3df4SWarner Losh #define OW_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 72ae1f3df4SWarner Losh #define OW_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) 73ae1f3df4SWarner Losh #define OW_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) 74ae1f3df4SWarner Losh #define OW_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) 75ae1f3df4SWarner Losh 76ae1f3df4SWarner Losh static MALLOC_DEFINE(M_OW, "ow", "House keeping data for 1wire bus"); 77ae1f3df4SWarner Losh 78ae1f3df4SWarner Losh static struct ow_timing timing_regular = { 79ae1f3df4SWarner Losh .t_slot = 60, /* 60 to 120 */ 80ae1f3df4SWarner Losh .t_low0 = 60, /* really 60 to 120 */ 81ae1f3df4SWarner Losh .t_low1 = 1, /* really 1 to 15 */ 82ae1f3df4SWarner Losh .t_release = 45, /* <= 45us */ 83ae1f3df4SWarner Losh .t_rec = 1, /* at least 1us */ 84ae1f3df4SWarner Losh .t_rdv = 15, /* 15us */ 85ae1f3df4SWarner Losh .t_rstl = 480, /* 480us or more */ 86ae1f3df4SWarner Losh .t_rsth = 480, /* 480us or more */ 87ae1f3df4SWarner Losh .t_pdl = 60, /* 60us to 240us */ 88ae1f3df4SWarner Losh .t_pdh = 60, /* 15us to 60us */ 89ae1f3df4SWarner Losh .t_lowr = 1, /* 1us */ 90ae1f3df4SWarner Losh }; 91ae1f3df4SWarner Losh 92ae1f3df4SWarner Losh /* NB: Untested */ 93ae1f3df4SWarner Losh static struct ow_timing timing_overdrive = { 94ae1f3df4SWarner Losh .t_slot = 11, /* 6us to 16us */ 95ae1f3df4SWarner Losh .t_low0 = 6, /* really 6 to 16 */ 96ae1f3df4SWarner Losh .t_low1 = 1, /* really 1 to 2 */ 97ae1f3df4SWarner Losh .t_release = 4, /* <= 4us */ 98ae1f3df4SWarner Losh .t_rec = 1, /* at least 1us */ 99ae1f3df4SWarner Losh .t_rdv = 2, /* 2us */ 100ae1f3df4SWarner Losh .t_rstl = 48, /* 48us to 80us */ 101ae1f3df4SWarner Losh .t_rsth = 48, /* 48us or more */ 102ae1f3df4SWarner Losh .t_pdl = 8, /* 8us to 24us */ 103ae1f3df4SWarner Losh .t_pdh = 2, /* 2us to 6us */ 104ae1f3df4SWarner Losh .t_lowr = 1, /* 1us */ 105ae1f3df4SWarner Losh }; 106ae1f3df4SWarner Losh 107ae1f3df4SWarner Losh static void 108ae1f3df4SWarner Losh ow_send_byte(device_t lldev, struct ow_timing *t, uint8_t byte) 109ae1f3df4SWarner Losh { 110ae1f3df4SWarner Losh int i; 111ae1f3df4SWarner Losh 112ae1f3df4SWarner Losh for (i = 0; i < 8; i++) 113ae1f3df4SWarner Losh if (byte & (1 << i)) 114ae1f3df4SWarner Losh OWLL_WRITE_ONE(lldev, t); 115ae1f3df4SWarner Losh else 116ae1f3df4SWarner Losh OWLL_WRITE_ZERO(lldev, t); 117ae1f3df4SWarner Losh } 118ae1f3df4SWarner Losh 119ae1f3df4SWarner Losh static void 120ae1f3df4SWarner Losh ow_read_byte(device_t lldev, struct ow_timing *t, uint8_t *bytep) 121ae1f3df4SWarner Losh { 122ae1f3df4SWarner Losh int i; 123ae1f3df4SWarner Losh uint8_t byte = 0; 124ae1f3df4SWarner Losh int bit; 125ae1f3df4SWarner Losh 126ae1f3df4SWarner Losh for (i = 0; i < 8; i++) { 127ae1f3df4SWarner Losh OWLL_READ_DATA(lldev, t, &bit); 128ae1f3df4SWarner Losh byte |= bit << i; 129ae1f3df4SWarner Losh } 130ae1f3df4SWarner Losh *bytep = byte; 131ae1f3df4SWarner Losh } 132ae1f3df4SWarner Losh 133ae1f3df4SWarner Losh static int 134ae1f3df4SWarner Losh ow_send_command(device_t ndev, device_t pdev, struct ow_cmd *cmd) 135ae1f3df4SWarner Losh { 136ae1f3df4SWarner Losh int present, i, bit, tries; 137ae1f3df4SWarner Losh device_t lldev; 138ae1f3df4SWarner Losh struct ow_timing *t; 139ae1f3df4SWarner Losh 140ae1f3df4SWarner Losh lldev = device_get_parent(ndev); 141ae1f3df4SWarner Losh 142ae1f3df4SWarner Losh /* 143ae1f3df4SWarner Losh * Retry the reset a couple of times before giving up. 144ae1f3df4SWarner Losh */ 145ae1f3df4SWarner Losh tries = 4; 146ae1f3df4SWarner Losh do { 147ae1f3df4SWarner Losh OWLL_RESET_AND_PRESENCE(lldev, &timing_regular, &present); 148ae1f3df4SWarner Losh if (present == 1) 149ae1f3df4SWarner Losh device_printf(ndev, "Reset said no device on bus?.\n"); 150ae1f3df4SWarner Losh } while (present == 1 && tries-- > 0); 151ae1f3df4SWarner Losh if (present == 1) { 152ae1f3df4SWarner Losh device_printf(ndev, "Reset said the device wasn't there.\n"); 153ae1f3df4SWarner Losh return ENOENT; /* No devices acked the RESET */ 154ae1f3df4SWarner Losh } 155ae1f3df4SWarner Losh if (present == -1) { 156ae1f3df4SWarner Losh device_printf(ndev, "Reset discovered bus wired wrong.\n"); 157ae1f3df4SWarner Losh return ENOENT; 158ae1f3df4SWarner Losh } 159ae1f3df4SWarner Losh 160ae1f3df4SWarner Losh for (i = 0; i < cmd->rom_len; i++) 161ae1f3df4SWarner Losh ow_send_byte(lldev, &timing_regular, cmd->rom_cmd[i]); 162ae1f3df4SWarner Losh for (i = 0; i < cmd->rom_read_len; i++) 163ae1f3df4SWarner Losh ow_read_byte(lldev, &timing_regular, cmd->rom_read + i); 164ae1f3df4SWarner Losh if (cmd->xpt_len) { 165ae1f3df4SWarner Losh /* 166ae1f3df4SWarner Losh * Per AN937, the reset pulse and ROM level are always 167ae1f3df4SWarner Losh * done with the regular timings. Certain ROM commands 168ae1f3df4SWarner Losh * put the device into overdrive mode for the remainder 169ae1f3df4SWarner Losh * of the data transfer, which is why we have to pass the 170ae1f3df4SWarner Losh * timings here. Commands that need to be handled like this 171ae1f3df4SWarner Losh * are expected to be flagged by the client. 172ae1f3df4SWarner Losh */ 173ae1f3df4SWarner Losh t = (cmd->flags & OW_FLAG_OVERDRIVE) ? 174ae1f3df4SWarner Losh &timing_overdrive : &timing_regular; 175ae1f3df4SWarner Losh for (i = 0; i < cmd->xpt_len; i++) 176ae1f3df4SWarner Losh ow_send_byte(lldev, t, cmd->xpt_cmd[i]); 177ae1f3df4SWarner Losh if (cmd->flags & OW_FLAG_READ_BIT) { 178ae1f3df4SWarner Losh memset(cmd->xpt_read, 0, (cmd->xpt_read_len + 7) / 8); 179ae1f3df4SWarner Losh for (i = 0; i < cmd->xpt_read_len; i++) { 180ae1f3df4SWarner Losh OWLL_READ_DATA(lldev, t, &bit); 181ae1f3df4SWarner Losh cmd->xpt_read[i / 8] |= bit << (i % 8); 182ae1f3df4SWarner Losh } 183ae1f3df4SWarner Losh } else { 184ae1f3df4SWarner Losh for (i = 0; i < cmd->xpt_read_len; i++) 185ae1f3df4SWarner Losh ow_read_byte(lldev, t, cmd->xpt_read + i); 186ae1f3df4SWarner Losh } 187ae1f3df4SWarner Losh } 188ae1f3df4SWarner Losh return 0; 189ae1f3df4SWarner Losh } 190ae1f3df4SWarner Losh 191ae1f3df4SWarner Losh static int 192ae1f3df4SWarner Losh ow_search_rom(device_t lldev, device_t dev) 193ae1f3df4SWarner Losh { 194ae1f3df4SWarner Losh struct ow_cmd cmd; 195ae1f3df4SWarner Losh 196ae1f3df4SWarner Losh memset(&cmd, 0, sizeof(cmd)); 197ae1f3df4SWarner Losh cmd.rom_cmd[0] = SEARCH_ROM; 198ae1f3df4SWarner Losh cmd.rom_len = 1; 199ae1f3df4SWarner Losh return ow_send_command(lldev, dev, &cmd); 200ae1f3df4SWarner Losh } 201ae1f3df4SWarner Losh 202ae1f3df4SWarner Losh #if 0 203ae1f3df4SWarner Losh static int 204ae1f3df4SWarner Losh ow_alarm_search(device_t lldev, device_t dev) 205ae1f3df4SWarner Losh { 206ae1f3df4SWarner Losh struct ow_cmd cmd; 207ae1f3df4SWarner Losh 208ae1f3df4SWarner Losh memset(&cmd, 0, sizeof(cmd)); 209ae1f3df4SWarner Losh cmd.rom_cmd[0] = ALARM_SEARCH; 210ae1f3df4SWarner Losh cmd.rom_len = 1; 211ae1f3df4SWarner Losh return ow_send_command(lldev, dev, &cmd); 212ae1f3df4SWarner Losh } 213ae1f3df4SWarner Losh #endif 214ae1f3df4SWarner Losh 215ae1f3df4SWarner Losh static int 216ae1f3df4SWarner Losh ow_add_child(device_t dev, romid_t romid) 217ae1f3df4SWarner Losh { 218ae1f3df4SWarner Losh struct ow_devinfo *di; 219ae1f3df4SWarner Losh device_t child; 220ae1f3df4SWarner Losh 221ae1f3df4SWarner Losh di = malloc(sizeof(*di), M_OW, M_WAITOK); 222ae1f3df4SWarner Losh di->romid = romid; 223ae1f3df4SWarner Losh child = device_add_child(dev, NULL, -1); 224ae1f3df4SWarner Losh if (child == NULL) { 225ae1f3df4SWarner Losh free(di, M_OW); 226ae1f3df4SWarner Losh return ENOMEM; 227ae1f3df4SWarner Losh } 228ae1f3df4SWarner Losh device_set_ivars(child, di); 229ae1f3df4SWarner Losh return (0); 230ae1f3df4SWarner Losh } 231ae1f3df4SWarner Losh 232ae1f3df4SWarner Losh static device_t 233ae1f3df4SWarner Losh ow_child_by_romid(device_t dev, romid_t romid) 234ae1f3df4SWarner Losh { 235ae1f3df4SWarner Losh device_t *children, retval, child; 236ae1f3df4SWarner Losh int nkid, i; 237ae1f3df4SWarner Losh struct ow_devinfo *di; 238ae1f3df4SWarner Losh 239ae1f3df4SWarner Losh if (device_get_children(dev, &children, &nkid) != 0) 240ae1f3df4SWarner Losh return (NULL); 241ae1f3df4SWarner Losh retval = NULL; 242ae1f3df4SWarner Losh for (i = 0; i < nkid; i++) { 243ae1f3df4SWarner Losh child = children[i]; 244ae1f3df4SWarner Losh di = device_get_ivars(child); 245ae1f3df4SWarner Losh if (di->romid == romid) { 246ae1f3df4SWarner Losh retval = child; 247ae1f3df4SWarner Losh break; 248ae1f3df4SWarner Losh } 249ae1f3df4SWarner Losh } 250ae1f3df4SWarner Losh free(children, M_TEMP); 251ae1f3df4SWarner Losh 252ae1f3df4SWarner Losh return (retval); 253ae1f3df4SWarner Losh } 254ae1f3df4SWarner Losh 255ae1f3df4SWarner Losh /* 256ae1f3df4SWarner Losh * CRC generator table -- taken from AN937 DOW CRC LOOKUP FUNCTION Table 2 257ae1f3df4SWarner Losh */ 258ae1f3df4SWarner Losh const uint8_t ow_crc_table[] = { 259ae1f3df4SWarner Losh 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 260ae1f3df4SWarner Losh 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 261ae1f3df4SWarner Losh 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 262ae1f3df4SWarner Losh 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 263ae1f3df4SWarner Losh 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 264ae1f3df4SWarner Losh 219, 133,103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 265ae1f3df4SWarner Losh 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 266ae1f3df4SWarner Losh 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 267ae1f3df4SWarner Losh 140,210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113,147, 205, 268ae1f3df4SWarner Losh 17, 79, 173, 243, 112, 46, 204, 146, 211,141, 111, 49, 178, 236, 14, 80, 269ae1f3df4SWarner Losh 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82,176, 238, 270ae1f3df4SWarner Losh 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 271ae1f3df4SWarner Losh 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 272ae1f3df4SWarner Losh 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 273ae1f3df4SWarner Losh 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 274ae1f3df4SWarner Losh 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53 275ae1f3df4SWarner Losh }; 276ae1f3df4SWarner Losh 277ae1f3df4SWarner Losh /* 278ae1f3df4SWarner Losh * Converted from DO_CRC page 131 ANN937 279ae1f3df4SWarner Losh */ 280ae1f3df4SWarner Losh static uint8_t 281ae1f3df4SWarner Losh ow_crc(device_t ndev, device_t pdev, uint8_t *buffer, size_t len) 282ae1f3df4SWarner Losh { 283ae1f3df4SWarner Losh uint8_t crc = 0; 284ae1f3df4SWarner Losh int i; 285ae1f3df4SWarner Losh 286ae1f3df4SWarner Losh for (i = 0; i < len; i++) 287ae1f3df4SWarner Losh crc = ow_crc_table[crc ^ buffer[i]]; 288ae1f3df4SWarner Losh return crc; 289ae1f3df4SWarner Losh } 290ae1f3df4SWarner Losh 291ae1f3df4SWarner Losh static int 292ae1f3df4SWarner Losh ow_check_crc(romid_t romid) 293ae1f3df4SWarner Losh { 294ae1f3df4SWarner Losh return ow_crc(NULL, NULL, (uint8_t *)&romid, sizeof(romid)) == 0; 295ae1f3df4SWarner Losh } 296ae1f3df4SWarner Losh 297ae1f3df4SWarner Losh static int 298ae1f3df4SWarner Losh ow_device_found(device_t dev, romid_t romid) 299ae1f3df4SWarner Losh { 300ae1f3df4SWarner Losh 301ae1f3df4SWarner Losh /* XXX Move this up into enumerate? */ 302ae1f3df4SWarner Losh /* 303ae1f3df4SWarner Losh * All valid ROM IDs have a valid CRC. Check that first. 304ae1f3df4SWarner Losh */ 305ae1f3df4SWarner Losh if (!ow_check_crc(romid)) { 306ae1f3df4SWarner Losh device_printf(dev, "Device romid %8D failed CRC.\n", 307ae1f3df4SWarner Losh &romid, ":"); 308ae1f3df4SWarner Losh return EINVAL; 309ae1f3df4SWarner Losh } 310ae1f3df4SWarner Losh 311ae1f3df4SWarner Losh /* 312ae1f3df4SWarner Losh * If we've seen this child before, don't add a new one for it. 313ae1f3df4SWarner Losh */ 314ae1f3df4SWarner Losh if (ow_child_by_romid(dev, romid) != NULL) 315ae1f3df4SWarner Losh return 0; 316ae1f3df4SWarner Losh 317ae1f3df4SWarner Losh return ow_add_child(dev, romid); 318ae1f3df4SWarner Losh } 319ae1f3df4SWarner Losh 320ae1f3df4SWarner Losh static int 321ae1f3df4SWarner Losh ow_enumerate(device_t dev, ow_enum_fn *enumfp, ow_found_fn *foundfp) 322ae1f3df4SWarner Losh { 323ae1f3df4SWarner Losh device_t lldev = device_get_parent(dev); 324ae1f3df4SWarner Losh int first, second, i, dir, prior, last, err, retries; 325ae1f3df4SWarner Losh uint64_t probed, last_mask; 326ae1f3df4SWarner Losh int sanity = 10; 327ae1f3df4SWarner Losh 328ae1f3df4SWarner Losh prior = -1; 329ae1f3df4SWarner Losh last_mask = 0; 330ae1f3df4SWarner Losh retries = 0; 331ae1f3df4SWarner Losh last = -2; 332ae1f3df4SWarner Losh err = ow_acquire_bus(dev, dev, OWN_DONTWAIT); 333ae1f3df4SWarner Losh if (err != 0) 334ae1f3df4SWarner Losh return err; 335ae1f3df4SWarner Losh while (last != -1) { 336ae1f3df4SWarner Losh if (sanity-- < 0) { 337ae1f3df4SWarner Losh printf("Reached the sanity limit\n"); 338ae1f3df4SWarner Losh return EIO; 339ae1f3df4SWarner Losh } 340ae1f3df4SWarner Losh again: 341ae1f3df4SWarner Losh probed = 0; 342ae1f3df4SWarner Losh last = -1; 343ae1f3df4SWarner Losh 344ae1f3df4SWarner Losh /* 345ae1f3df4SWarner Losh * See AN397 section 5.II.C.3 for the algorithm (though a bit 346ae1f3df4SWarner Losh * poorly stated). The search command forces each device to 347ae1f3df4SWarner Losh * send ROM ID bits one at a time (first the bit, then the 348cef367e6SEitan Adler * complement) the master (us) sends back a bit. If the 349ae1f3df4SWarner Losh * device's bit doesn't match what we send back, that device 350ae1f3df4SWarner Losh * stops sending bits back. So each time through we remember 351ae1f3df4SWarner Losh * where we made the last decision (always 0). If there's a 352ae1f3df4SWarner Losh * conflict there this time (and there will be in the absence 353ae1f3df4SWarner Losh * of a hardware failure) we go with 1. This way, we prune the 354ae1f3df4SWarner Losh * devices on the bus and wind up with a unique ROM. We know 355ae1f3df4SWarner Losh * we're done when we detect no new conflicts. The same 356ae1f3df4SWarner Losh * algorithm is used for devices in alarm state as well. 357ae1f3df4SWarner Losh * 358ae1f3df4SWarner Losh * In addition, experience has shown that sometimes devices 359ae1f3df4SWarner Losh * stop responding in the middle of enumeration, so try this 360ae1f3df4SWarner Losh * step again a few times when that happens. It is unclear if 361ae1f3df4SWarner Losh * this is due to a nosiy electrical environment or some odd 362ae1f3df4SWarner Losh * timing issue. 363ae1f3df4SWarner Losh */ 364ae1f3df4SWarner Losh 365ae1f3df4SWarner Losh /* 366ae1f3df4SWarner Losh * The enumeration command should be successfully sent, if not, 367ae1f3df4SWarner Losh * we have big issues on the bus so punt. Lower layers report 368ae1f3df4SWarner Losh * any unusual errors, so we don't need to here. 369ae1f3df4SWarner Losh */ 370ae1f3df4SWarner Losh err = enumfp(dev, dev); 371ae1f3df4SWarner Losh if (err != 0) 372ae1f3df4SWarner Losh return (err); 373ae1f3df4SWarner Losh 374ae1f3df4SWarner Losh for (i = 0; i < 64; i++) { 375ae1f3df4SWarner Losh OWLL_READ_DATA(lldev, &timing_regular, &first); 376ae1f3df4SWarner Losh OWLL_READ_DATA(lldev, &timing_regular, &second); 377ae1f3df4SWarner Losh switch (first | second << 1) { 378ae1f3df4SWarner Losh case 0: /* Conflict */ 379ae1f3df4SWarner Losh if (i < prior) 380ae1f3df4SWarner Losh dir = (last_mask >> i) & 1; 381ae1f3df4SWarner Losh else 382ae1f3df4SWarner Losh dir = i == prior; 383ae1f3df4SWarner Losh 384ae1f3df4SWarner Losh if (dir == 0) 385ae1f3df4SWarner Losh last = i; 386ae1f3df4SWarner Losh break; 387ae1f3df4SWarner Losh case 1: /* 1 then 0 -> 1 for all */ 388ae1f3df4SWarner Losh dir = 1; 389ae1f3df4SWarner Losh break; 390ae1f3df4SWarner Losh case 2: /* 0 then 1 -> 0 for all */ 391ae1f3df4SWarner Losh dir = 0; 392ae1f3df4SWarner Losh break; 393ae1f3df4SWarner Losh case 3: 394ae1f3df4SWarner Losh /* 395ae1f3df4SWarner Losh * No device responded. This is unexpected, but 396ae1f3df4SWarner Losh * experience has shown that on some platforms 397ae1f3df4SWarner Losh * we miss a timing window, or otherwise have 398ae1f3df4SWarner Losh * an issue. Start this step over. Since we've 399ae1f3df4SWarner Losh * not updated prior yet, we can just jump to 400ae1f3df4SWarner Losh * the top of the loop for a re-do of this step. 401ae1f3df4SWarner Losh */ 402ae1f3df4SWarner Losh printf("oops, starting over\n"); 403ae1f3df4SWarner Losh if (++retries > 5) 404ae1f3df4SWarner Losh return (EIO); 405ae1f3df4SWarner Losh goto again; 40663aa6c0cSPedro F. Giffuni default: /* NOTREACHED */ 40763aa6c0cSPedro F. Giffuni __unreachable(); 408ae1f3df4SWarner Losh } 409ae1f3df4SWarner Losh if (dir) { 410ae1f3df4SWarner Losh OWLL_WRITE_ONE(lldev, &timing_regular); 411ae1f3df4SWarner Losh probed |= 1ull << i; 412ae1f3df4SWarner Losh } else { 413ae1f3df4SWarner Losh OWLL_WRITE_ZERO(lldev, &timing_regular); 414ae1f3df4SWarner Losh } 415ae1f3df4SWarner Losh } 416ae1f3df4SWarner Losh retries = 0; 417ae1f3df4SWarner Losh foundfp(dev, probed); 418ae1f3df4SWarner Losh last_mask = probed; 419ae1f3df4SWarner Losh prior = last; 42074b8d63dSPedro F. Giffuni } 421ae1f3df4SWarner Losh ow_release_bus(dev, dev); 422ae1f3df4SWarner Losh 423ae1f3df4SWarner Losh return (0); 424ae1f3df4SWarner Losh } 425ae1f3df4SWarner Losh 426ae1f3df4SWarner Losh static int 427ae1f3df4SWarner Losh ow_probe(device_t dev) 428ae1f3df4SWarner Losh { 429ae1f3df4SWarner Losh 430ae1f3df4SWarner Losh device_set_desc(dev, "1 Wire Bus"); 431ae1f3df4SWarner Losh return (BUS_PROBE_GENERIC); 432ae1f3df4SWarner Losh } 433ae1f3df4SWarner Losh 434ae1f3df4SWarner Losh static int 435ae1f3df4SWarner Losh ow_attach(device_t ndev) 436ae1f3df4SWarner Losh { 437ae1f3df4SWarner Losh struct ow_softc *sc; 438ae1f3df4SWarner Losh 439ae1f3df4SWarner Losh /* 440ae1f3df4SWarner Losh * Find all the devices on the bus. We don't probe / attach them in the 441ae1f3df4SWarner Losh * enumeration phase. We do this because we want to allow the probe / 442ae1f3df4SWarner Losh * attach routines of the child drivers to have as full an access to the 443ae1f3df4SWarner Losh * bus as possible. While we reset things before the next step of the 444ae1f3df4SWarner Losh * search (so it would likely be OK to allow access by the clients to 445ae1f3df4SWarner Losh * the bus), it is more conservative to find them all, then to do the 446ae1f3df4SWarner Losh * attach of the devices. This also allows the child devices to have 447ae1f3df4SWarner Losh * more knowledge of the bus. We also ignore errors from the enumeration 448ae1f3df4SWarner Losh * because they might happen after we've found a few devices. 449ae1f3df4SWarner Losh */ 450ae1f3df4SWarner Losh sc = device_get_softc(ndev); 451ae1f3df4SWarner Losh sc->dev = ndev; 452ae1f3df4SWarner Losh mtx_init(&sc->mtx, device_get_nameunit(sc->dev), "ow", MTX_DEF); 453ae1f3df4SWarner Losh ow_enumerate(ndev, ow_search_rom, ow_device_found); 454ae1f3df4SWarner Losh return bus_generic_attach(ndev); 455ae1f3df4SWarner Losh } 456ae1f3df4SWarner Losh 457ae1f3df4SWarner Losh static int 458ae1f3df4SWarner Losh ow_detach(device_t ndev) 459ae1f3df4SWarner Losh { 460ae1f3df4SWarner Losh device_t *children, child; 461ae1f3df4SWarner Losh int nkid, i; 462ae1f3df4SWarner Losh struct ow_devinfo *di; 463ae1f3df4SWarner Losh struct ow_softc *sc; 464ae1f3df4SWarner Losh 465ae1f3df4SWarner Losh sc = device_get_softc(ndev); 466ae1f3df4SWarner Losh /* 467ae1f3df4SWarner Losh * detach all the children first. This is blocking until any threads 468ae1f3df4SWarner Losh * have stopped, etc. 469ae1f3df4SWarner Losh */ 470ae1f3df4SWarner Losh bus_generic_detach(ndev); 471ae1f3df4SWarner Losh 472ae1f3df4SWarner Losh /* 473ae1f3df4SWarner Losh * We delete all the children, and free up the ivars 474ae1f3df4SWarner Losh */ 475ae1f3df4SWarner Losh if (device_get_children(ndev, &children, &nkid) != 0) 476ae1f3df4SWarner Losh return ENOMEM; 477ae1f3df4SWarner Losh for (i = 0; i < nkid; i++) { 478ae1f3df4SWarner Losh child = children[i]; 479ae1f3df4SWarner Losh di = device_get_ivars(child); 480ae1f3df4SWarner Losh free(di, M_OW); 481ae1f3df4SWarner Losh device_delete_child(ndev, child); 482ae1f3df4SWarner Losh } 483ae1f3df4SWarner Losh free(children, M_TEMP); 484ae1f3df4SWarner Losh 485ae1f3df4SWarner Losh OW_LOCK_DESTROY(sc); 486ae1f3df4SWarner Losh return 0; 487ae1f3df4SWarner Losh } 488ae1f3df4SWarner Losh 489ae1f3df4SWarner Losh /* 490ae1f3df4SWarner Losh * Not sure this is really needed. I'm having trouble figuring out what 491ae1f3df4SWarner Losh * location means in the context of the one wire bus. 492ae1f3df4SWarner Losh */ 493ae1f3df4SWarner Losh static int 494ae1f3df4SWarner Losh ow_child_location_str(device_t dev, device_t child, char *buf, 495ae1f3df4SWarner Losh size_t buflen) 496ae1f3df4SWarner Losh { 497ae1f3df4SWarner Losh 498d63180f2SWarner Losh *buf = '\0'; 499ae1f3df4SWarner Losh return (0); 500ae1f3df4SWarner Losh } 501ae1f3df4SWarner Losh 502ae1f3df4SWarner Losh static int 503ae1f3df4SWarner Losh ow_child_pnpinfo_str(device_t dev, device_t child, char *buf, 504ae1f3df4SWarner Losh size_t buflen) 505ae1f3df4SWarner Losh { 506ae1f3df4SWarner Losh struct ow_devinfo *di; 507ae1f3df4SWarner Losh 508ae1f3df4SWarner Losh di = device_get_ivars(child); 509ae1f3df4SWarner Losh snprintf(buf, buflen, "romid=%8D", &di->romid, ":"); 510ae1f3df4SWarner Losh return (0); 511ae1f3df4SWarner Losh } 512ae1f3df4SWarner Losh 513ae1f3df4SWarner Losh static int 514ae1f3df4SWarner Losh ow_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 515ae1f3df4SWarner Losh { 516ae1f3df4SWarner Losh struct ow_devinfo *di; 517ae1f3df4SWarner Losh romid_t **ptr; 518ae1f3df4SWarner Losh 519ae1f3df4SWarner Losh di = device_get_ivars(child); 520ae1f3df4SWarner Losh switch (which) { 521ae1f3df4SWarner Losh case OW_IVAR_FAMILY: 522ae1f3df4SWarner Losh *result = di->romid & 0xff; 523ae1f3df4SWarner Losh break; 524ae1f3df4SWarner Losh case OW_IVAR_ROMID: 525ae1f3df4SWarner Losh ptr = (romid_t **)result; 526ae1f3df4SWarner Losh *ptr = &di->romid; 527ae1f3df4SWarner Losh break; 528ae1f3df4SWarner Losh default: 529ae1f3df4SWarner Losh return EINVAL; 530ae1f3df4SWarner Losh } 531ae1f3df4SWarner Losh 532ae1f3df4SWarner Losh return 0; 533ae1f3df4SWarner Losh } 534ae1f3df4SWarner Losh 535ae1f3df4SWarner Losh static int 536ae1f3df4SWarner Losh ow_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 537ae1f3df4SWarner Losh { 538ae1f3df4SWarner Losh 539ae1f3df4SWarner Losh return EINVAL; 540ae1f3df4SWarner Losh } 541ae1f3df4SWarner Losh 542ae1f3df4SWarner Losh static int 543ae1f3df4SWarner Losh ow_print_child(device_t ndev, device_t pdev) 544ae1f3df4SWarner Losh { 545ae1f3df4SWarner Losh int retval = 0; 546ae1f3df4SWarner Losh struct ow_devinfo *di; 547ae1f3df4SWarner Losh 548ae1f3df4SWarner Losh di = device_get_ivars(pdev); 549ae1f3df4SWarner Losh 550ae1f3df4SWarner Losh retval += bus_print_child_header(ndev, pdev); 551ae1f3df4SWarner Losh retval += printf(" romid %8D", &di->romid, ":"); 552ae1f3df4SWarner Losh retval += bus_print_child_footer(ndev, pdev); 553ae1f3df4SWarner Losh 554ae1f3df4SWarner Losh return retval; 555ae1f3df4SWarner Losh } 556ae1f3df4SWarner Losh 557ae1f3df4SWarner Losh static void 558ae1f3df4SWarner Losh ow_probe_nomatch(device_t ndev, device_t pdev) 559ae1f3df4SWarner Losh { 560ae1f3df4SWarner Losh struct ow_devinfo *di; 561ae1f3df4SWarner Losh 562ae1f3df4SWarner Losh di = device_get_ivars(pdev); 563ae1f3df4SWarner Losh device_printf(ndev, "romid %8D: no driver\n", &di->romid, ":"); 564ae1f3df4SWarner Losh } 565ae1f3df4SWarner Losh 566ae1f3df4SWarner Losh static int 567ae1f3df4SWarner Losh ow_acquire_bus(device_t ndev, device_t pdev, int how) 568ae1f3df4SWarner Losh { 569ae1f3df4SWarner Losh struct ow_softc *sc; 570ae1f3df4SWarner Losh 571ae1f3df4SWarner Losh sc = device_get_softc(ndev); 572ae1f3df4SWarner Losh OW_ASSERT_UNLOCKED(sc); 573ae1f3df4SWarner Losh OW_LOCK(sc); 574ae1f3df4SWarner Losh if (sc->owner != NULL) { 575ae1f3df4SWarner Losh if (sc->owner == pdev) 576ae1f3df4SWarner Losh panic("%s: %s recursively acquiring the bus.\n", 577ae1f3df4SWarner Losh device_get_nameunit(ndev), 578ae1f3df4SWarner Losh device_get_nameunit(pdev)); 579ae1f3df4SWarner Losh if (how == OWN_DONTWAIT) { 580ae1f3df4SWarner Losh OW_UNLOCK(sc); 581ae1f3df4SWarner Losh return EWOULDBLOCK; 582ae1f3df4SWarner Losh } 583ae1f3df4SWarner Losh while (sc->owner != NULL) 584ae1f3df4SWarner Losh mtx_sleep(sc, &sc->mtx, 0, "owbuswait", 0); 585ae1f3df4SWarner Losh } 586ae1f3df4SWarner Losh sc->owner = pdev; 587ae1f3df4SWarner Losh OW_UNLOCK(sc); 588ae1f3df4SWarner Losh 589ae1f3df4SWarner Losh return 0; 590ae1f3df4SWarner Losh } 591ae1f3df4SWarner Losh 592ae1f3df4SWarner Losh static void 593ae1f3df4SWarner Losh ow_release_bus(device_t ndev, device_t pdev) 594ae1f3df4SWarner Losh { 595ae1f3df4SWarner Losh struct ow_softc *sc; 596ae1f3df4SWarner Losh 597ae1f3df4SWarner Losh sc = device_get_softc(ndev); 598ae1f3df4SWarner Losh OW_ASSERT_UNLOCKED(sc); 599ae1f3df4SWarner Losh OW_LOCK(sc); 600ae1f3df4SWarner Losh if (sc->owner == NULL) 601ae1f3df4SWarner Losh panic("%s: %s releasing unowned bus.", device_get_nameunit(ndev), 602ae1f3df4SWarner Losh device_get_nameunit(pdev)); 603ae1f3df4SWarner Losh if (sc->owner != pdev) 604ae1f3df4SWarner Losh panic("%s: %s don't own the bus. %s does. game over.", 605ae1f3df4SWarner Losh device_get_nameunit(ndev), device_get_nameunit(pdev), 606ae1f3df4SWarner Losh device_get_nameunit(sc->owner)); 607ae1f3df4SWarner Losh sc->owner = NULL; 608ae1f3df4SWarner Losh wakeup(sc); 609ae1f3df4SWarner Losh OW_UNLOCK(sc); 610ae1f3df4SWarner Losh } 611ae1f3df4SWarner Losh 612ae1f3df4SWarner Losh devclass_t ow_devclass; 613ae1f3df4SWarner Losh 614ae1f3df4SWarner Losh static device_method_t ow_methods[] = { 615ae1f3df4SWarner Losh /* Device interface */ 616ae1f3df4SWarner Losh DEVMETHOD(device_probe, ow_probe), 617ae1f3df4SWarner Losh DEVMETHOD(device_attach, ow_attach), 618ae1f3df4SWarner Losh DEVMETHOD(device_detach, ow_detach), 619ae1f3df4SWarner Losh 620ae1f3df4SWarner Losh /* Bus interface */ 621ae1f3df4SWarner Losh DEVMETHOD(bus_child_pnpinfo_str, ow_child_pnpinfo_str), 622ae1f3df4SWarner Losh DEVMETHOD(bus_child_location_str, ow_child_location_str), 623ae1f3df4SWarner Losh DEVMETHOD(bus_read_ivar, ow_read_ivar), 624ae1f3df4SWarner Losh DEVMETHOD(bus_write_ivar, ow_write_ivar), 625ae1f3df4SWarner Losh DEVMETHOD(bus_print_child, ow_print_child), 626ae1f3df4SWarner Losh DEVMETHOD(bus_probe_nomatch, ow_probe_nomatch), 627ae1f3df4SWarner Losh 628ae1f3df4SWarner Losh /* One Wire Network/Transport layer interface */ 629ae1f3df4SWarner Losh DEVMETHOD(own_send_command, ow_send_command), 630ae1f3df4SWarner Losh DEVMETHOD(own_acquire_bus, ow_acquire_bus), 631ae1f3df4SWarner Losh DEVMETHOD(own_release_bus, ow_release_bus), 632ae1f3df4SWarner Losh DEVMETHOD(own_crc, ow_crc), 633ae1f3df4SWarner Losh { 0, 0 } 634ae1f3df4SWarner Losh }; 635ae1f3df4SWarner Losh 636ae1f3df4SWarner Losh static driver_t ow_driver = { 637ae1f3df4SWarner Losh "ow", 638ae1f3df4SWarner Losh ow_methods, 639ae1f3df4SWarner Losh sizeof(struct ow_softc), 640ae1f3df4SWarner Losh }; 641ae1f3df4SWarner Losh 642ae1f3df4SWarner Losh DRIVER_MODULE(ow, owc, ow_driver, ow_devclass, 0, 0); 643ae1f3df4SWarner Losh MODULE_VERSION(ow, 1); 644