Index: arch/macppc/dev/cuda.c =================================================================== RCS file: /cvsroot/src/sys/arch/macppc/dev/cuda.c,v retrieving revision 1.29 retrieving revision 1.29.2.5 diff -u -p -r1.29 -r1.29.2.5 --- arch/macppc/dev/cuda.c 7 Aug 2021 16:18:57 -0000 1.29 +++ arch/macppc/dev/cuda.c 11 Sep 2021 17:22:35 -0000 1.29.2.5 @@ -1,4 +1,4 @@ -/* $NetBSD: cuda.c,v 1.29 2021/08/07 16:18:57 thorpej Exp $ */ +/* $NetBSD: cuda.c,v 1.29.2.5 2021/09/11 17:22:35 thorpej Exp $ */ /*- * Copyright (c) 2006 Michael Lorenz @@ -27,7 +27,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: cuda.c,v 1.29 2021/08/07 16:18:57 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: cuda.c,v 1.29.2.5 2021/09/11 17:22:35 thorpej Exp $"); #include #include @@ -72,6 +72,8 @@ typedef struct _cuda_handler { void *cookie; } CudaHandler; +#define CUDA_MAX_I2C_DEVICES 2 + struct cuda_softc { device_t sc_dev; void *sc_ih; @@ -81,6 +83,16 @@ struct cuda_softc { struct i2c_controller sc_i2c; bus_space_tag_t sc_memt; bus_space_handle_t sc_memh; + + /* + * We provide our own i2c device enumeration method, so we + * need to provide our own devhandle_impl. + */ + struct devhandle_impl sc_devhandle_impl; + + struct i2c_deventry sc_i2c_devices[CUDA_MAX_I2C_DEVICES]; + unsigned int sc_ni2c_devices; + int sc_node; int sc_state; int sc_waiting; @@ -147,6 +159,41 @@ static int cuda_adb_set_handler(void *, static int cuda_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, void *, size_t, int); +static void +cuda_add_i2c_device(struct cuda_softc *sc, const char *name, + const char *compatible, i2c_addr_t addr) +{ + KASSERT(sc->sc_ni2c_devices < CUDA_MAX_I2C_DEVICES); + sc->sc_i2c_devices[sc->sc_ni2c_devices].name = name; + sc->sc_i2c_devices[sc->sc_ni2c_devices].compat = compatible; + sc->sc_i2c_devices[sc->sc_ni2c_devices].addr = addr; + sc->sc_ni2c_devices++; +} + +static int +cuda_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) +{ + struct i2c_enumerate_devices_args *args = v; + + /* dev is the "iicbus" instance. Cuda softc is in args. */ + struct cuda_softc *sc = args->ia->ia_tag->ic_cookie; + + return i2c_enumerate_deventries(dev, call_handle, args, + sc->sc_i2c_devices, sc->sc_ni2c_devices); +} + +static device_call_t +cuda_devhandle_lookup_device_call(devhandle_t handle, const char *name, + devhandle_t *call_handlep) +{ + if (strcmp(name, "i2c-enumerate-devices") == 0) { + return cuda_i2c_enumerate_devices; + } + + /* Defer everything else to the "super". */ + return NULL; +} + static int cuda_match(device_t parent, struct cfdata *cf, void *aux) { @@ -172,9 +219,6 @@ cuda_attach(device_t parent, device_t se struct cuda_softc *sc = device_private(self); struct i2cbus_attach_args iba; static struct cuda_attach_args caa; - prop_dictionary_t dict = device_properties(self); - prop_dictionary_t dev; - prop_array_t cfg; int irq = ca->ca_intr[0]; int node, i, child; char name[32]; @@ -251,37 +295,34 @@ cuda_attach(device_t parent, device_t se #if notyet config_found(self, &caa, cuda_print, CFARGS_NONE); #endif - cfg = prop_array_create(); - prop_dictionary_set(dict, "i2c-child-devices", cfg); - prop_object_release(cfg); - /* we don't have OF nodes for i2c devices so we have to make our own */ - node = OF_finddevice("/valkyrie"); if (node != -1) { - dev = prop_dictionary_create(); - prop_dictionary_set_string(dev, "name", "videopll"); - prop_dictionary_set_uint32(dev, "addr", 0x50); - prop_array_add(cfg, dev); - prop_object_release(dev); + cuda_add_i2c_device(sc, "videopll", + "aapl,valkyrie-videopll", 0x50); } - node = OF_finddevice("/perch"); if (node != -1) { - dev = prop_dictionary_create(); - prop_dictionary_set_string(dev, "name", "sgsmix"); - prop_dictionary_set_uint32(dev, "addr", 0x8a); - prop_array_add(cfg, dev); - prop_object_release(dev); + cuda_add_i2c_device(sc, "sgsmix", "st,tda7433", 0x8a); } - memset(&iba, 0, sizeof(iba)); - iba.iba_tag = &sc->sc_i2c; + /* + * Subclass our device handle so we can override + * "i2c-enumerate-devices" and give that to the + * i2c bus instance. + */ + devhandle_t devhandle = devhandle_subclass(device_handle(self), + &sc->sc_devhandle_impl, cuda_devhandle_lookup_device_call); + iic_tag_init(&sc->sc_i2c); sc->sc_i2c.ic_cookie = sc; sc->sc_i2c.ic_exec = cuda_i2c_exec; + + memset(&iba, 0, sizeof(iba)); + iba.iba_tag = &sc->sc_i2c; config_found(self, &iba, iicbus_print, - CFARGS(.iattr = "i2cbus")); + CFARGS(.iattr = "i2cbus", + .devhandle = devhandle)); if (cuda0 == NULL) cuda0 = &caa; Index: arch/macppc/dev/ki2c.c =================================================================== RCS file: /cvsroot/src/sys/arch/macppc/dev/ki2c.c,v retrieving revision 1.32 retrieving revision 1.32.2.4 diff -u -p -r1.32 -r1.32.2.4 --- arch/macppc/dev/ki2c.c 7 Aug 2021 16:18:57 -0000 1.32 +++ arch/macppc/dev/ki2c.c 11 Sep 2021 17:22:35 -0000 1.32.2.4 @@ -1,4 +1,4 @@ -/* $NetBSD: ki2c.c,v 1.32 2021/08/07 16:18:57 thorpej Exp $ */ +/* $NetBSD: ki2c.c,v 1.32.2.4 2021/09/11 17:22:35 thorpej Exp $ */ /* Id: ki2c.c,v 1.7 2002/10/05 09:56:05 tsubai Exp */ /*- @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -38,35 +39,220 @@ #include "opt_ki2c.h" #include +#include "locators.h" + #ifdef KI2C_DEBUG #define DPRINTF printf #else #define DPRINTF while (0) printf #endif -int ki2c_match(device_t, cfdata_t, void *); -void ki2c_attach(device_t, device_t, void *); -inline uint8_t ki2c_readreg(struct ki2c_softc *, int); -inline void ki2c_writereg(struct ki2c_softc *, int, uint8_t); -u_int ki2c_getmode(struct ki2c_softc *); -void ki2c_setmode(struct ki2c_softc *, u_int); -u_int ki2c_getspeed(struct ki2c_softc *); -void ki2c_setspeed(struct ki2c_softc *, u_int); -int ki2c_intr(struct ki2c_softc *); -int ki2c_poll(struct ki2c_softc *, int); -int ki2c_start(struct ki2c_softc *, int, int, void *, int); -int ki2c_read(struct ki2c_softc *, int, int, void *, int); -int ki2c_write(struct ki2c_softc *, int, int, void *, int); +static int ki2c_match(device_t, cfdata_t, void *); +static void ki2c_attach(device_t, device_t, void *); +static int ki2c_intr(struct ki2c_softc *); /* I2C glue */ -static int ki2c_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, - void *, size_t, int); - +static int ki2c_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); +static int ki2c_i2c_acquire_bus(void *, int); +static void ki2c_i2c_release_bus(void *, int); CFATTACH_DECL_NEW(ki2c, sizeof(struct ki2c_softc), ki2c_match, ki2c_attach, NULL, NULL); -int +static prop_dictionary_t +ki2c_i2c_device_props(struct ki2c_softc *sc, int node) +{ + prop_dictionary_t props = prop_dictionary_create(); + uint32_t reg; + char descr[32], num[8]; + + /* We're fetching descriptions for sensors. */ + /* XXX This is a terrible hack and should not be done this way XXX */ + + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + if (of_getprop_uint32(node, "reg", ®) == -1) { + continue; + } + if (OF_getprop(node, "location", descr, sizeof(descr)) <= 0) { + continue; + } + snprintf(num, sizeof(num), "s%02x", reg); + + aprint_debug_dev(sc->sc_dev, + "%s: sensor %s -> %s\n", __func__, num, descr); + + prop_dictionary_set_string(props, num, descr); + } + + return props; +} + +static bool +ki2c_i2c_enumerate_device(struct ki2c_softc *sc, device_t dev, int node, + const char *name, uint32_t addr, + struct i2c_enumerate_devices_args * const args) +{ + int compat_size; + prop_dictionary_t props; + char compat_buf[32]; + char *compat; + bool cbrv; + + compat_size = OF_getproplen(node, "compatible"); + if (compat_size <= 0) { + /* some i2c device nodes don't have 'compatible' */ + aprint_debug_dev(sc->sc_dev, + "no compatible property for phandle %d; using '%s'\n", + node, name); + compat = compat_buf; + strlcpy(compat, name, sizeof(compat)); + compat_size = strlen(compat) + 1; + } else { + compat = kmem_tmpbuf_alloc(compat_size, compat_buf, + sizeof(compat_buf), KM_SLEEP); + if (OF_getprop(node, "compatible", compat, + sizeof(compat)) <= 0) { + aprint_error_dev(sc->sc_dev, + "unable to get compatible property for " + "phandle %d ('%s')\n", node, name); + goto bad; + } + } + + props = ki2c_i2c_device_props(sc, node); + + args->ia->ia_addr = (i2c_addr_t)addr; + args->ia->ia_name = name; + args->ia->ia_clist = compat; + args->ia->ia_clist_size = compat_size; + args->ia->ia_prop = props; + args->ia->ia_devhandle = devhandle_from_of(node); + + cbrv = args->callback(dev, args); + + prop_object_release(props); + out: + kmem_tmpbuf_free(compat, compat_size, compat_buf); + return cbrv; /* callback decides if we keep enumerating */ + + bad: + cbrv = true; /* keep enumerating */ + goto out; +} + +static int +ki2c_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) +{ + struct i2c_enumerate_devices_args *args = v; + int bus_phandle, node; + uint32_t addr; + char name[32]; + + /* dev is the "iic" bus instance. ki2c channel is in args. */ + struct ki2c_channel *ch = args->ia->ia_tag->ic_cookie; + struct ki2c_softc *sc = ch->ch_ki2c; + + /* + * If we're not using the separate nodes scheme, we need + * to filter out devices from the other channel. We detect + * this by comparing the bus phandle to the controller phandle, + * and if they match, we are NOT using the separate nodes + * scheme. + */ + bus_phandle = devhandle_to_of(device_handle(dev)); + bool filter_by_channel = + bus_phandle == devhandle_to_of(device_handle(sc->sc_dev)); + + for (node = OF_child(bus_phandle); node != 0; node = OF_peer(node)) { + if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { + aprint_error_dev(sc->sc_dev, + "unable to get name property for phandle %d\n", + node); + continue; + } + if (of_getprop_uint32(node, "reg", &addr) == -1 && + of_getprop_uint32(node, "i2c-address", &addr) == -1) { + aprint_error_dev(sc->sc_dev, + "unable to get i2c address for phandle %d ('%s')\n", + node, name); + continue; + } + if (filter_by_channel && ((addr >> 8) & 1) != ch->ch_channel) { + continue; + } + addr = (addr & 0xff) >> 1; + if (!ki2c_i2c_enumerate_device(sc, dev, node, name, addr, + args)) { + break; + } + } + + return 0; +} + +static device_call_t +ki2c_devhandle_lookup_device_call(devhandle_t handle, const char *name, + devhandle_t *call_handlep) +{ + if (strcmp(name, "i2c-enumerate-devices") == 0) { + return ki2c_i2c_enumerate_devices; + } + + /* Defer everything else to the "super". */ + return NULL; +} + +static inline uint8_t +ki2c_readreg(struct ki2c_softc *sc, int reg) +{ + + return bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_regstep * reg); +} + +static inline void +ki2c_writereg(struct ki2c_softc *sc, int reg, uint8_t val) +{ + + bus_space_write_1(sc->sc_tag, sc->sc_bh, reg * sc->sc_regstep, val); + delay(10); +} + +#if 0 +static u_int +ki2c_getmode(struct ki2c_softc *sc) +{ + return ki2c_readreg(sc, MODE) & I2C_MODE; +} +#endif + +static void +ki2c_setmode(struct ki2c_softc *sc, u_int mode) +{ + ki2c_writereg(sc, MODE, mode); +} + +#if 0 +static u_int +ki2c_getspeed(struct ki2c_softc *sc) +{ + return ki2c_readreg(sc, MODE) & I2C_SPEED; +} +#endif + +static void +ki2c_setspeed(struct ki2c_softc *sc, u_int speed) +{ + u_int x; + + KASSERT((speed & ~I2C_SPEED) == 0); + x = ki2c_readreg(sc, MODE); + x &= ~I2C_SPEED; + x |= speed; + ki2c_writereg(sc, MODE, x); +} + +static int ki2c_match(device_t parent, cfdata_t match, void *aux) { struct confargs *ca = aux; @@ -77,21 +263,17 @@ ki2c_match(device_t parent, cfdata_t mat return 0; } -void +static void ki2c_attach(device_t parent, device_t self, void *aux) { struct ki2c_softc *sc = device_private(self); struct confargs *ca = aux; + struct ki2c_channel *ch; int node = ca->ca_node; - uint32_t addr, channel, reg; - int rate, child, /*namelen,*/ i2cbus[2] = {0, 0}; + uint32_t channel, addr; + int i, rate, child; struct i2cbus_attach_args iba; - prop_dictionary_t dict = device_properties(self); - prop_array_t cfg; - int devs, devc; - char compat[256], num[8], descr[32]; - prop_dictionary_t dev; - prop_data_t data; + devhandle_t devhandle; char name[32]; sc->sc_dev = self; @@ -126,135 +308,120 @@ ki2c_attach(device_t parent, device_t se ki2c_setspeed(sc, I2C_100kHz); /* XXX rate */ ki2c_writereg(sc, IER,I2C_INT_DATA|I2C_INT_ADDR|I2C_INT_STOP); - - cfg = prop_array_create(); - prop_dictionary_set(dict, "i2c-child-devices", cfg); - prop_object_release(cfg); - - /* - * newer OF puts I2C devices under 'i2c-bus' instead of attaching them - * directly to the ki2c node so we just check if we have a child named - * 'i2c-bus' and if so we attach its children, not ours + + /* + * Two physical I2C busses share a single controller. It's not + * quite a mux, which is why we don't attach it that way. + * + * The locking order is: + * + * iic bus mutex -> ctrl_lock * - * XXX - * should probably check for multiple i2c-bus children + * ctrl_lock is taken in ki2c_i2c_acquire_bus. */ + mutex_init(&sc->sc_ctrl_lock, MUTEX_DEFAULT, IPL_NONE); + + /* Set up the channel structures. */ + for (i = 0; i < KI2C_MAX_I2C_CHANNELS; i++) { + ch = &sc->sc_channels[i]; + + iic_tag_init(&ch->ch_i2c); + ch->ch_i2c.ic_channel = ch->ch_channel = i; + ch->ch_i2c.ic_cookie = ch; + ch->ch_i2c.ic_acquire_bus = ki2c_i2c_acquire_bus; + ch->ch_i2c.ic_release_bus = ki2c_i2c_release_bus; + ch->ch_i2c.ic_exec = ki2c_i2c_exec; + + ch->ch_ki2c = sc; + } - int found_busnode = 0; - channel = 0; - child = OF_child(node); - while (child != 0) { + /* + * Different systems have different I2C device tree topologies. + * + * Some systems use a scheme like this: + * + * /u3@0,f8000000/i2c@f8001000/temp-monitor@98 + * /u3@0,f8000000/i2c@f8001000/fan@15e + * + * Here, we see the channel encoded in bit #8 of the address. + * + * Other systems use a scheme like this: + * + * /ht@0,f2000000/pci@4000,0,0/mac-io@7/i2c@18000/i2c-bus@0 + * /ht@0,f2000000/pci@4000,0,0/mac-io@7/i2c@18000/i2c-bus@0/codec@8c + * + * /u4@0,f8000000/i2c@f8001000/i2c-bus@1 + * /u4@0,f8000000/i2c@f8001000/i2c-bus@1/temp-monitor@94 + * + * Here, a separate device tree node represents the channel. + * Note that in BOTH cases, the I2C address of the devices are + * shifted left by 1 (as it would be on the wire to leave room + * for the read/write bit). + * + * So, what we're going to do here is look for i2c-bus nodes. If + * we find them, we remember those phandles, and will use them for + * device enumeration. If we don't, then we will use the controller + * phandle for device enumeration and filter based on the channel + * bit in the "reg" property. + */ + int i2c_bus_phandles[KI2C_MAX_I2C_CHANNELS] = { 0 }; + bool separate_nodes_scheme = false; + for (child = OF_child(node); child != 0; child = OF_peer(child)) { OF_getprop(child, "name", name, sizeof(name)); if (strcmp(name, "i2c-bus") == 0) { - OF_getprop(child, "reg", &channel, sizeof(channel)); - i2cbus[channel] = child; - DPRINTF("found channel %x\n", channel); - found_busnode = 1; - } - child = OF_peer(child); - } - if (found_busnode == 0) - i2cbus[0] = node; - - for (channel = 0; channel < 2; channel++) { - devs = OF_child(i2cbus[channel]); - while (devs != 0) { - if (OF_getprop(devs, "name", name, 32) <= 0) - goto skip; - if (OF_getprop(devs, "compatible", compat, 256) <= 0) { - /* some i2c device nodes don't have 'compatible' */ - memset(compat, 0, 256); - strncpy(compat, name, 256); - } - if (OF_getprop(devs, "reg", &addr, 4) <= 0) - if (OF_getprop(devs, "i2c-address", &addr, 4) <= 0) - goto skip; - addr |= channel << 8; - addr = addr >> 1; - DPRINTF("-> %s@%x\n", name, addr); - dev = prop_dictionary_create(); - prop_dictionary_set_string(dev, "name", name); - data = prop_data_create_copy(compat, strlen(compat)+1); - prop_dictionary_set(dev, "compatible", data); - prop_object_release(data); - prop_dictionary_set_uint32(dev, "addr", addr); - prop_dictionary_set_uint64(dev, "cookie", devs); - /* look for location info for sensors */ - devc = OF_child(devs); - while (devc != 0) { - if (OF_getprop(devc, "reg", ®, 4) < 4) goto nope; - if (OF_getprop(devc, "location", descr, 32) <= 0) - goto nope; - DPRINTF("found '%s' at %02x\n", descr, reg); - snprintf(num, 7, "s%02x", reg); - prop_dictionary_set_string(dev, num, descr); - nope: - devc = OF_peer(devc); + separate_nodes_scheme = true; + if (of_getprop_uint32(child, "reg", &channel) == -1) { + continue; } - prop_array_add(cfg, dev); - prop_object_release(dev); - skip: - devs = OF_peer(devs); + if (channel >= KI2C_MAX_I2C_CHANNELS) { + continue; + } + i2c_bus_phandles[channel] = child; } } - /* fill in the i2c tag */ - iic_tag_init(&sc->sc_i2c); - sc->sc_i2c.ic_cookie = sc; - sc->sc_i2c.ic_exec = ki2c_i2c_exec; - - memset(&iba, 0, sizeof(iba)); - iba.iba_tag = &sc->sc_i2c; - config_found(sc->sc_dev, &iba, iicbus_print, CFARGS_NONE); - -} - -uint8_t -ki2c_readreg(struct ki2c_softc *sc, int reg) -{ - - return bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_regstep * reg); -} - -void -ki2c_writereg(struct ki2c_softc *sc, int reg, uint8_t val) -{ - - bus_space_write_1(sc->sc_tag, sc->sc_bh, reg * sc->sc_regstep, val); - delay(10); -} - -u_int -ki2c_getmode(struct ki2c_softc *sc) -{ - return ki2c_readreg(sc, MODE) & I2C_MODE; -} - -void -ki2c_setmode(struct ki2c_softc *sc, u_int mode) -{ - ki2c_writereg(sc, MODE, mode); -} + /* + * Set up our handle implementation so that we can override + * "i2c-enumerate-devices". + */ + devhandle = device_handle(self); + devhandle_impl_inherit(&sc->sc_devhandle_impl, devhandle.impl); + sc->sc_devhandle_impl.lookup_device_call = + ki2c_devhandle_lookup_device_call; + + for (i = 0; i < KI2C_MAX_I2C_CHANNELS; i++) { + int locs[I2CBUSCF_NLOCS]; + + ch = &sc->sc_channels[i]; + + if (separate_nodes_scheme) { + if (i2c_bus_phandles[i] == 0) { + /* + * This wasn't represented (either at all + * or not correctly) in the device tree, + * so skip attaching the "iic" instance. + */ + continue; + } + devhandle = devhandle_from_of(i2c_bus_phandles[i]); + } else { + devhandle = device_handle(self); + } + devhandle.impl = &sc->sc_devhandle_impl; -u_int -ki2c_getspeed(struct ki2c_softc *sc) -{ - return ki2c_readreg(sc, MODE) & I2C_SPEED; -} + locs[I2CBUSCF_BUS] = ch->ch_i2c.ic_channel; -void -ki2c_setspeed(struct ki2c_softc *sc, u_int speed) -{ - u_int x; + memset(&iba, 0, sizeof(iba)); + iba.iba_tag = &ch->ch_i2c; + config_found(sc->sc_dev, &iba, iicbus_print_multi, + CFARGS(.submatch = config_stdsubmatch, + .locators = locs, + .devhandle = devhandle)); + } - KASSERT((speed & ~I2C_SPEED) == 0); - x = ki2c_readreg(sc, MODE); - x &= ~I2C_SPEED; - x |= speed; - ki2c_writereg(sc, MODE, x); } -int +static int ki2c_intr(struct ki2c_softc *sc) { u_int isr, x; @@ -320,7 +487,7 @@ out: return 1; } -int +static int ki2c_poll(struct ki2c_softc *sc, int timo) { while (sc->sc_flags & I2C_BUSY) { @@ -329,18 +496,18 @@ ki2c_poll(struct ki2c_softc *sc, int tim timo -= 100; if (timo < 0) { DPRINTF("i2c_poll: timeout\n"); - return -1; + return ETIMEDOUT; } delay(100); } return 0; } -int +static int ki2c_start(struct ki2c_softc *sc, int addr, int subaddr, void *data, int len) { int rw = (sc->sc_flags & I2C_READING) ? 1 : 0; - int timo, x; + int error, timo, x; KASSERT((addr & 1) == 0); @@ -360,16 +527,17 @@ ki2c_start(struct ki2c_softc *sc, int ad x = ki2c_readreg(sc, CONTROL) | I2C_CT_ADDR; ki2c_writereg(sc, CONTROL, x); - if (ki2c_poll(sc, timo)) - return -1; + if ((error = ki2c_poll(sc, timo)) != 0) + return error; + if (sc->sc_flags & I2C_ERROR) { DPRINTF("I2C_ERROR\n"); - return -1; + return EIO; } return 0; } -int +static int ki2c_read(struct ki2c_softc *sc, int addr, int subaddr, void *data, int len) { sc->sc_flags = I2C_READING; @@ -377,7 +545,7 @@ ki2c_read(struct ki2c_softc *sc, int add return ki2c_start(sc, addr, subaddr, data, len); } -int +static int ki2c_write(struct ki2c_softc *sc, int addr, int subaddr, void *data, int len) { sc->sc_flags = 0; @@ -385,12 +553,38 @@ ki2c_write(struct ki2c_softc *sc, int ad return ki2c_start(sc, addr, subaddr, data, len); } +static int +ki2c_i2c_acquire_bus(void * const v, int const flags) +{ + struct ki2c_channel *ch = v; + struct ki2c_softc *sc = ch->ch_ki2c; + + if (flags & I2C_F_POLL) { + if (! mutex_tryenter(&sc->sc_ctrl_lock)) { + return EBUSY; + } + } else { + mutex_enter(&sc->sc_ctrl_lock); + } + return 0; +} + +static void +ki2c_i2c_release_bus(void * const v, int const flags) +{ + struct ki2c_channel *ch = v; + struct ki2c_softc *sc = ch->ch_ki2c; + + mutex_exit(&sc->sc_ctrl_lock); +} + int ki2c_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd, size_t cmdlen, void *vbuf, size_t buflen, int flags) { - struct ki2c_softc *sc = cookie; - int i; + struct ki2c_channel *ch = cookie; + struct ki2c_softc *sc = ch->ch_ki2c; + int i, error; size_t w_len; uint8_t *wp; uint8_t wrbuf[I2C_EXEC_MAX_CMDLEN + I2C_EXEC_MAX_CMDLEN]; @@ -402,11 +596,13 @@ ki2c_i2c_exec(void *cookie, i2c_op_t op, * return an error. */ if (cmdlen == 0 && buflen == 0) - return -1; + return ENOTSUP; - channel = (addr & 0xf80) ? 0x10 : 0x00; - addr &= 0x7f; - + /* Don't support 10-bit addressing. */ + if (addr > 0x7f) + return ENOTSUP; + + channel = ch->ch_channel == 1 ? 0x10 : 0x00; /* we handle the subaddress stuff ourselves */ ki2c_setmode(sc, channel | I2C_STDMODE); @@ -440,13 +636,18 @@ ki2c_i2c_exec(void *cookie, i2c_op_t op, } } - if (w_len > 0) - if (ki2c_write(sc, addr << 1, 0, wp, w_len) !=0 ) - return -1; + if (w_len > 0) { + error = ki2c_write(sc, addr << 1, 0, wp, w_len); + if (error) { + return error; + } + } if (I2C_OP_READ_P(op)) { - if (ki2c_read(sc, addr << 1, 0, vbuf, buflen) !=0 ) - return -1; + error = ki2c_read(sc, addr << 1, 0, vbuf, buflen); + if (error) { + return error; + } } return 0; } Index: arch/macppc/dev/ki2cvar.h =================================================================== RCS file: /cvsroot/src/sys/arch/macppc/dev/ki2cvar.h,v retrieving revision 1.5 retrieving revision 1.5.30.1 diff -u -p -r1.5 -r1.5.30.1 --- arch/macppc/dev/ki2cvar.h 15 Sep 2017 21:34:42 -0000 1.5 +++ arch/macppc/dev/ki2cvar.h 9 Aug 2021 00:30:08 -0000 1.5.30.1 @@ -1,4 +1,4 @@ -/* $NetBSD: ki2cvar.h,v 1.5 2017/09/15 21:34:42 macallan Exp $ */ +/* $NetBSD: ki2cvar.h,v 1.5.30.1 2021/08/09 00:30:08 thorpej Exp $ */ /* Id: ki2c.c,v 1.7 2002/10/05 09:56:05 tsubai Exp */ /*- @@ -83,25 +83,31 @@ #define I2C_READING 0x02 #define I2C_ERROR 0x04 +#define KI2C_MAX_I2C_CHANNELS 2 + struct ki2c_softc { device_t sc_dev; bus_space_tag_t sc_tag; bus_space_handle_t sc_bh; int sc_regstep; - - struct i2c_controller sc_i2c; - kmutex_t sc_buslock; + + /* + * We provide our own i2c device enumeration method, so we + * need to provide our own devhandle_impl. + */ + struct devhandle_impl sc_devhandle_impl; + + struct ki2c_channel { + struct i2c_controller ch_i2c; + struct ki2c_softc *ch_ki2c; + int ch_channel; + } sc_channels[KI2C_MAX_I2C_CHANNELS]; + + kmutex_t sc_ctrl_lock; int sc_flags; u_char *sc_data; int sc_resid; }; -struct ki2c_confargs { - char *ka_name; /* device name */ - int ka_node; - i2c_tag_t ka_tag; /* our controller */ - i2c_addr_t ka_addr; /* address of device */ -}; - #endif Index: arch/sandpoint/sandpoint/autoconf.c =================================================================== RCS file: /cvsroot/src/sys/arch/sandpoint/sandpoint/autoconf.c,v retrieving revision 1.29 retrieving revision 1.29.16.4 diff -u -p -r1.29 -r1.29.16.4 --- arch/sandpoint/sandpoint/autoconf.c 9 Jul 2020 05:12:09 -0000 1.29 +++ arch/sandpoint/sandpoint/autoconf.c 11 Sep 2021 01:03:18 -0000 1.29.16.4 @@ -1,4 +1,4 @@ -/* $NetBSD: autoconf.c,v 1.29 2020/07/09 05:12:09 nisimura Exp $ */ +/* $NetBSD: autoconf.c,v 1.29.16.4 2021/09/11 01:03:18 thorpej Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. @@ -35,7 +35,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.29 2020/07/09 05:12:09 nisimura Exp $"); +__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.29.16.4 2021/09/11 01:03:18 thorpej Exp $"); #include #include @@ -44,6 +44,7 @@ __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v #include #include +#include #include #include @@ -57,31 +58,139 @@ static struct btinfo_net *bi_net; static struct btinfo_prodfamily *bi_pfam; static struct btinfo_model *bi_model; -struct i2c_dev { - const char *name; - unsigned addr; - /* only attach when one of these bits in the model flags is set */ - uint32_t model_mask; -}; - -#define MAXI2CDEVS 4 -struct model_i2c { - const char *family; - struct i2c_dev i2c_devs[MAXI2CDEVS]; -}; - -static struct model_i2c model_i2c_list[] = { - { "dlink", { { "strtc", 0x68, 0 } } }, - { "iomega", { { "dsrtc", 0x68, 0 } } }, - { "kurobox", { { "rs5c372rtc", 0x32, 0 } } }, - { "kurot4", { { "rs5c372rtc", 0x32, 0 } } }, - { "nhnas", { { "pcf8563rtc", 0x51, 0 } } }, - { "qnap", { { "s390rtc", 0x30, 0 } } }, - { "synology", { { "rs5c372rtc", 0x32, 0 }, - { "lmtemp", 0x48, BI_MODEL_THERMAL } } }, +struct sandpoint_i2c_data { + const struct i2c_deventry *entries; + unsigned int nentries; + uint32_t model_mask; }; -static void add_i2c_child_devices(device_t, const char *); +static const struct i2c_deventry dlink_i2cdevs[] = { + { .name = "strtc", .compat = "st,m41t80", .addr = 0x68, }, +}; +static const struct sandpoint_i2c_data dlink_i2cdata[] = { + { .entries = dlink_i2cdevs, + .nentries = __arraycount(dlink_i2cdevs), }, + { .entries = NULL }, +}; + +static const struct i2c_deventry iomega_i2cdevs[] = { + { .name = "dsrtc", .compat = "dallas,ds1307", .addr = 0x68, }, +}; +static const struct sandpoint_i2c_data iomega_i2cdata[] = { + { .entries = iomega_i2cdevs, + .nentries = __arraycount(iomega_i2cdevs), }, + { .entries = NULL }, +}; + +static const struct i2c_deventry kurobox_i2cdevs[] = { + { .name = "rs5c372rtc", .compat = "ricoh,rs5c372a", .addr = 0x32, }, +}; +static const struct sandpoint_i2c_data kurobox_i2cdata[] = { + { .entries = kurobox_i2cdevs, + .nentries = __arraycount(kurobox_i2cdevs), }, + { .entries = NULL }, +}; + +static const struct i2c_deventry nhnas_i2cdevs[] = { + { .name = "pcf8563rtc", .compat = "nxp,pcf8563", .addr = 0x51, }, +}; +static const struct sandpoint_i2c_data nhnas_i2cdata[] = { + { .entries = nhnas_i2cdevs, + .nentries = __arraycount(nhnas_i2cdevs), }, + { .entries = NULL }, +}; + +static const struct i2c_deventry qnap_i2cdevs[] = { + { .name = "s390rtc", .compat = "sii,s35390a", .addr = 0x30, }, +}; +static const struct sandpoint_i2c_data qnap_i2cdata[] = { + { .entries = qnap_i2cdevs, + .nentries = __arraycount(qnap_i2cdevs), }, + { .entries = NULL }, +}; + +static const struct i2c_deventry synology_thermal_i2cdevs[] = { + { .name = "rs5c372rtc", .compat = "ricoh,rs5c372a", .addr = 0x32, }, + { .name = "lmtemp", .compat = "national,lm75", .addr = 0x48, }, +}; +static const struct i2c_deventry synology_i2cdevs[] = { + { .name = "rs5c372rtc", .compat = "ricoh,rs5c372a", .addr = 0x32, }, +}; +static const struct sandpoint_i2c_data synology_i2cdata[] = { + { .entries = synology_thermal_i2cdevs, + .nentries = __arraycount(synology_thermal_i2cdevs), + .model_mask = BI_MODEL_THERMAL, }, + { .entries = synology_i2cdevs, + .nentries = __arraycount(synology_i2cdevs), }, + { .entries = NULL }, +}; + +static const struct device_compatible_entry sandpoint_i2c_compat[] = { + { .compat = "dlink", .data = dlink_i2cdata }, + { .compat = "iomega", .data = iomega_i2cdata }, + { .compat = "kurobox", .data = kurobox_i2cdata }, + /* kurot4 has same i2c devices as kurobox */ + { .compat = "kurot4", .data = kurobox_i2cdata }, + { .compat = "nhnas", .data = nhnas_i2cdata }, + { .compat = "qnap", .data = qnap_i2cdata }, + { .compat = "synology", .data = synology_i2cdata }, + DEVICE_COMPAT_EOL +}; + +/* + * We provide a device handle implementation for i2c device enumeration. + */ +static int +sandpoint_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) +{ + const struct device_compatible_entry *dce; + const struct sandpoint_i2c_data *data; + + KASSERT(bi_pfam != NULL); + + const char *fam_name = bi_pfam->name; + dce = device_compatible_lookup(&fam_name, 1, sandpoint_i2c_compat); + if (dce == NULL) { + /* no i2c devices for this model. */ + return 0; + } + data = dce->data; + KASSERT(data != NULL); + + /* Filter by model_mask if necessary. */ + for (; data->entries != NULL; data++) { + if (data->model_mask == 0) { + /* We'll use this one! */ + break; + } + if ((data->model_mask & bi_model->flags) == data->model_mask) { + /* We'll use this one! */ + break; + } + } + if (data->entries == NULL) { + /* no i2c devies for this model. */ + return 0; + } + + return i2c_enumerate_deventries(dev, call_handle, v, + data->entries, data->nentries); +} + +static device_call_t +sandpoint_i2c_devhandle_lookup_device_call(devhandle_t handle, const char *name, + devhandle_t *call_handlep) +{ + if (strcmp(name, "i2c-enumerate-devices") == 0) { + return sandpoint_i2c_enumerate_devices; + } + return NULL; +} + +static const struct devhandle_impl sandpoint_i2c_devhandle_impl = { + .type = DEVHANDLE_TYPE_PRIVATE, + .lookup_device_call = sandpoint_i2c_devhandle_lookup_device_call, +}; /* * Determine i/o configuration for a machine. @@ -170,45 +279,10 @@ device_register(device_t dev, void *aux) booted_partition = bi_rdev->cookie & 0xff; } else if (device_is_a(dev, "ociic") && bi_pfam != NULL) { - add_i2c_child_devices(dev, bi_pfam->name); - } -} - -static void -add_i2c_child_devices(device_t self, const char *family) -{ - struct i2c_dev *model_i2c_devs; - prop_dictionary_t pd; - prop_array_t pa; - int i; - - for (i = 0; - i < (int)(sizeof(model_i2c_list) / sizeof(model_i2c_list[0])); - i++) { - if (strcmp(family, model_i2c_list[i].family) == 0) { - model_i2c_devs = model_i2c_list[i].i2c_devs; - goto found; - } + /* For i2c device enumeration. */ + devhandle_t devhandle = { + .impl = &sandpoint_i2c_devhandle_impl, + }; + device_set_handle(dev, devhandle); } - return; - - found: - /* make an i2c-child-devices property list with for direct config. */ - pa = prop_array_create(); - - for (i = 0; i < MAXI2CDEVS && model_i2c_devs[i].name != NULL; i++) { - if (model_i2c_devs[i].model_mask != 0 && - !(bi_model->flags & model_i2c_devs[i].model_mask)) - continue; - pd = prop_dictionary_create(); - prop_dictionary_set_string_nocopy(pd, "name", - model_i2c_devs[i].name); - prop_dictionary_set_uint32(pd, "addr", - model_i2c_devs[i].addr); - prop_array_add(pa, pd); - prop_object_release(pd); - } - - prop_dictionary_set(device_properties(self), "i2c-child-devices", pa); - prop_object_release(pa); } Index: arch/sparc64/sparc64/autoconf.c =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/autoconf.c,v retrieving revision 1.234 retrieving revision 1.234.2.1 diff -u -p -r1.234 -r1.234.2.1 --- arch/sparc64/sparc64/autoconf.c 7 Aug 2021 16:19:06 -0000 1.234 +++ arch/sparc64/sparc64/autoconf.c 9 Aug 2021 00:30:08 -0000 1.234.2.1 @@ -1,4 +1,4 @@ -/* $NetBSD: autoconf.c,v 1.234 2021/08/07 16:19:06 thorpej Exp $ */ +/* $NetBSD: autoconf.c,v 1.234.2.1 2021/08/09 00:30:08 thorpej Exp $ */ /* * Copyright (c) 1996 @@ -48,7 +48,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.234 2021/08/07 16:19:06 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.234.2.1 2021/08/09 00:30:08 thorpej Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" @@ -1099,6 +1099,9 @@ device_register(device_t dev, void *aux) devhandle_t devhandle; int ofnode = 0; + /* Fixup devices that have missing device tree information. */ + sparc64_device_tree_fixup(dev, aux); + /* * If the device has a valid OpenFirmware node association, * grab it now. @@ -1119,24 +1122,6 @@ device_register(device_t dev, void *aux) * Ignore mainbus0 itself, it certainly is not a boot * device. */ - } else if (device_is_a(busdev, "iic")) { - struct i2c_attach_args *ia = aux; - - if (ia->ia_name == NULL) /* indirect config */ - return; - - ofnode = (int)ia->ia_cookie; - if (device_is_a(dev, "pcagpio")) { - if (!strcmp(machine_model, "SUNW,Sun-Fire-V240") || - !strcmp(machine_model, "SUNW,Sun-Fire-V210")) { - add_gpio_props_v210(dev, aux); - } - } - if (device_is_a(dev, "pcf8574io")) { - if (!strcmp(machine_model, "SUNW,Ultra-250")) { - add_gpio_props_e250(dev, aux); - } - } return; } else if (device_is_a(dev, "sd") || device_is_a(dev, "cd")) { struct scsipibus_attach_args *sa = aux; @@ -1191,11 +1176,6 @@ device_register(device_t dev, void *aux) dev_bi_unit_drive_match(dev, ofnode, periph->periph_target + off, 0, periph->periph_lun); } - - if (device_is_a(busdev, "scsibus")) { - /* see if we're in a known SCA drivebay */ - add_drivebay_props(dev, ofnode, aux); - } return; } else if (device_is_a(dev, "wd")) { struct ata_device *adev = aux; @@ -1219,9 +1199,6 @@ device_register(device_t dev, void *aux) ofnode = devhandle_to_of(devhandle); } - if (busdev == NULL) - return; - if (ofnode != 0) { uint8_t eaddr[ETHER_ADDR_LEN]; char tmpstr[32]; @@ -1318,64 +1295,6 @@ noether: } } - /* - * Check for I2C busses and add data for their direct configuration. - */ - if (device_is_a(dev, "iic")) { - devhandle_t bushandle = device_handle(busdev); - int busnode = - devhandle_type(bushandle) == DEVHANDLE_TYPE_OF ? - devhandle_to_of(bushandle) : 0; - - if (busnode) { - prop_dictionary_t props = device_properties(busdev); - prop_object_t cfg = prop_dictionary_get(props, - "i2c-child-devices"); - if (!cfg) { - int node; - const char *name; - - /* - * pmu's i2c devices are under the "i2c" node, - * so find it out. - */ - name = prom_getpropstring(busnode, "name"); - if (strcmp(name, "pmu") == 0) { - for (node = OF_child(busnode); - node != 0; node = OF_peer(node)) { - name = prom_getpropstring(node, - "name"); - if (strcmp(name, "i2c") == 0) { - busnode = node; - break; - } - } - } - - of_enter_i2c_devs(props, busnode, - sizeof(cell_t), 1); - } - } - - if (!strcmp(machine_model, "TAD,SPARCLE")) - add_spdmem_props_sparcle(busdev); - - if (device_is_a(busdev, "pcfiic") && - (!strcmp(machine_model, "SUNW,Sun-Fire-V240") || - !strcmp(machine_model, "SUNW,Sun-Fire-V210"))) - add_env_sensors_v210(busdev); - - /* E450 SUNW,envctrl */ - if (device_is_a(busdev, "pcfiic") && - (!strcmp(machine_model, "SUNW,Ultra-4"))) - add_i2c_props_e450(busdev, busnode); - - /* E250 SUNW,envctrltwo */ - if (device_is_a(busdev, "pcfiic") && - (!strcmp(machine_model, "SUNW,Ultra-250"))) - add_i2c_props_e250(busdev, busnode); - } - /* set properties for PCI framebuffers */ if (device_is_a(busdev, "pci")) { /* see if this is going to be console */ @@ -1438,10 +1357,7 @@ noether: } } #endif - set_static_edid(dict); } - - set_hw_props(dev); } /* Index: arch/sparc64/sparc64/ofw_patch.c =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/ofw_patch.c,v retrieving revision 1.7 retrieving revision 1.7.14.5 diff -u -p -r1.7 -r1.7.14.5 --- arch/sparc64/sparc64/ofw_patch.c 23 Dec 2020 07:01:14 -0000 1.7 +++ arch/sparc64/sparc64/ofw_patch.c 11 Sep 2021 17:22:36 -0000 1.7.14.5 @@ -1,11 +1,11 @@ -/* $NetBSD: ofw_patch.c,v 1.7 2020/12/23 07:01:14 jdc Exp $ */ +/* $NetBSD: ofw_patch.c,v 1.7.14.5 2021/09/11 17:22:36 thorpej Exp $ */ /*- - * Copyright (c) 2020 The NetBSD Foundation, Inc. + * Copyright (c) 2020, 2021 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Julian Coleman. + * by Julian Coleman and Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,9 +29,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__KERNEL_RCSID(0, "$NetBSD: ofw_patch.c,v 1.7 2020/12/23 07:01:14 jdc Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ofw_patch.c,v 1.7.14.5 2021/09/11 17:22:36 thorpej Exp $"); #include +#include #include #include @@ -41,6 +42,10 @@ __KERNEL_RCSID(0, "$NetBSD: ofw_patch.c, #include #include +/***************************************************************************** + * GPIO fixup support + *****************************************************************************/ + static void add_gpio_pin(prop_array_t pins, const char *name, int num, int act, int def) { @@ -54,328 +59,724 @@ add_gpio_pin(prop_array_t pins, const ch prop_object_release(pin); } -static prop_array_t -create_i2c_dict(device_t busdev) +struct gpio_pin_fixup { + const char *name; + int num; + int act; + int def; +}; + +static void +add_gpio_pins(device_t dev, const struct gpio_pin_fixup *addpins, int npins) +{ + prop_dictionary_t dict = device_properties(dev); + prop_array_t pins = prop_array_create(); + int i; + + for (i = 0; i < npins; i++) { + add_gpio_pin(pins, addpins[i].name, addpins[i].num, + addpins[i].act, addpins[i].def); + } + + prop_dictionary_set(dict, "pins", pins); + prop_object_release(pins); +} + +/***************************************************************************** + * I2C device fixup support + *****************************************************************************/ + +/* + * On some systems, there are lots of i2c devices missing from the + * device tree. + * + * The way we deal with this is by subclassing the controller's + * devhandle_impl and overriding the "i2c-enumerate-devices" device + * call. Our implementation will enumerate using the super-class + * (OpenFirmware enumeration), and then enumerate the missing entries + * from our own static tables. + * + * This devhandle_impl will be wrapped inside of a container structure + * that will point to the extra devices that need to be added as children + * of that node. The "i2c-enumerate-devices" call will first enumerate + * devices that are present in the device tree, and then enumerate the + * additions. + */ + +struct i2c_fixup_container { + struct devhandle_impl i2c_devhandle_impl; + devhandle_t i2c_super_handle; + const struct i2c_deventry *i2c_additions; + int i2c_nadditions; +}; + +static int +i2c_fixup_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) +{ + struct i2c_enumerate_devices_args *args = v; + const struct devhandle_impl *impl = call_handle.impl; + const struct i2c_fixup_container *fixup = + container_of((struct devhandle_impl *)__UNCONST(impl), + struct i2c_fixup_container, i2c_devhandle_impl); + devhandle_t super_handle = fixup->i2c_super_handle; + device_call_t super_call; + int super_error, error; + + /* First, enumerate using whatever is in the device tree. */ + super_call = devhandle_lookup_device_call(super_handle, + "i2c-enumerate-devices", &super_handle); + super_error = super_call != NULL ? super_call(dev, super_handle, args) + : 0; + + /* Now enumerate our additions. */ + KASSERT(fixup->i2c_additions != NULL); + error = i2c_enumerate_deventries(dev, call_handle, args, + fixup->i2c_additions, fixup->i2c_nadditions); + + return super_error != 0 ? super_error : error; +} + +static device_call_t +i2c_fixup_lookup_device_call(devhandle_t handle, const char *name, + devhandle_t *call_handlep) { - prop_dictionary_t props = device_properties(busdev); - prop_array_t cfg = NULL; + if (strcmp(name, "i2c-enumerate-devices") == 0) { + return i2c_fixup_enumerate_devices; + } - cfg = prop_dictionary_get(props, "i2c-child-devices"); - if (!cfg) { - DPRINTF(ACDB_PROBE, ("\nCreating new i2c-child-devices\n")); - cfg = prop_array_create(); - prop_dictionary_set(props, "i2c-child-devices", cfg); - prop_dictionary_set_bool(props, "i2c-indirect-config", false); - } - return cfg; + /* Defer everything else to the "super". */ + return NULL; } static void -add_i2c_device(prop_array_t cfg, const char *name, const char *compat, -uint32_t addr, uint64_t node) -{ - prop_dictionary_t dev; - - DPRINTF(ACDB_PROBE, ("\nAdding i2c device: %s (%s) @ 0x%x (%lx)\n", - name, compat == NULL ? "NULL" : compat, addr, node & 0xffffffff)); - dev = prop_dictionary_create(); - prop_dictionary_set_string(dev, "name", name); - if (compat != NULL) - prop_dictionary_set_data(dev, "compatible", compat, - strlen(compat) + 1); - prop_dictionary_set_uint32(dev, "addr", addr); - prop_dictionary_set_uint64(dev, "cookie", node); - prop_array_add(cfg, dev); - prop_object_release(dev); +add_i2c_devices(device_t dev, const struct i2c_deventry *i2c_adds, + unsigned int nadds) +{ + struct i2c_fixup_container *fixup; + + fixup = kmem_alloc(sizeof(*fixup), KM_SLEEP); + + fixup->i2c_additions = i2c_adds; + fixup->i2c_nadditions = nadds; + + /* Stash away the super-class handle. */ + devhandle_t devhandle = device_handle(dev); + fixup->i2c_super_handle = devhandle; + + /* Sub-class it so we can override "i2c-enumerate-devices". */ + devhandle = devhandle_subclass(devhandle, &fixup->i2c_devhandle_impl, + i2c_fixup_lookup_device_call); + + /* + * ...and slide that on into the device. This handle will be + * passed on to the iic bus instance, and our enumeration method + * will get called to enumerate the child devices. + */ + device_set_handle(dev, devhandle); } -void -add_gpio_props_v210(device_t dev, void *aux) +/***************************************************************************** + * System fixup machinery + *****************************************************************************/ + +struct system_fixup { + const struct device_compatible_entry *dtnode_fixups; + void (*special_fixups)(device_t, void *); +}; + +static bool +device_handle_matches_phandle(device_t dev, int phandle) { - struct i2c_attach_args *ia = aux; - prop_dictionary_t dict = device_properties(dev); - prop_array_t pins; + devhandle_t devhandle = device_handle(dev); - switch (ia->ia_addr) { - case 0x38: /* front panel LEDs */ - pins = prop_array_create(); - add_gpio_pin(pins, "LED indicator", 7, 0, -1); - add_gpio_pin(pins, "LED fault", 5, 0, 0); - add_gpio_pin(pins, "LED power", 4, 0, 1); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; - case 0x23: /* drive bay O/1 LEDs */ - pins = prop_array_create(); - add_gpio_pin(pins, "LED bay0_fault", 10, 0, 0); - add_gpio_pin(pins, "LED bay1_fault", 11, 0, 0); - add_gpio_pin(pins, "LED bay0_remove", 12, 0, 0); - add_gpio_pin(pins, "LED bay1_remove", 13, 0, 0); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; - case 0x25: /* drive bay 2/3 LEDs (v240 only)*/ - pins = prop_array_create(); - add_gpio_pin(pins, "LED bay2_fault", 10, 0, 0); - add_gpio_pin(pins, "LED bay3_fault", 11, 0, 0); - add_gpio_pin(pins, "LED bay2_remove", 12, 0, 0); - add_gpio_pin(pins, "LED bay3_remove", 13, 0, 0); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; + if (devhandle_type(devhandle) != DEVHANDLE_TYPE_OF) { + return false; } + return devhandle_to_of(devhandle) == phandle; } -void -add_gpio_props_e250(device_t dev, void *aux) +static int onboard_scsi_phandle __read_mostly; + +static void +onboard_scsi_fixup(device_t dev, void *aux) { - struct i2c_attach_args *ia = aux; - prop_dictionary_t dict = device_properties(dev); - prop_array_t pins; + /* We're just going to remember the phandle for now. */ + devhandle_t devhandle = device_handle(dev); + KASSERT(devhandle_type(devhandle) == DEVHANDLE_TYPE_OF); + onboard_scsi_phandle = devhandle_to_of(devhandle); +} - switch (ia->ia_addr) { - case 0x38: /* interrupt status */ - pins = prop_array_create(); - add_gpio_pin(pins, "ALERT high_temp", 1, 0, 30); - add_gpio_pin(pins, "ALERT disk_event", 2, 0, 30); - add_gpio_pin(pins, "ALERT fan_fail", 4, 0, 30); - add_gpio_pin(pins, "ALERT key_event", 5, 0, 30); - add_gpio_pin(pins, "ALERT psu_event", 6, 0, 30); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; - case 0x39: /* PSU status */ - pins = prop_array_create(); - add_gpio_pin(pins, "INDICATOR psu0_present", 0, 0, -1); - add_gpio_pin(pins, "INDICATOR psu1_present", 1, 0, -1); - add_gpio_pin(pins, "INDICATOR psu0_fault", 4, 0, -1); - add_gpio_pin(pins, "INDICATOR psu1_fault", 5, 0, -1); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; - case 0x3d: /* disk status */ - pins = prop_array_create(); - add_gpio_pin(pins, "INDICATOR disk0_present", - 0, 0, -1); - add_gpio_pin(pins, "INDICATOR disk1_present", - 1, 0, -1); - add_gpio_pin(pins, "INDICATOR disk2_present", - 2, 0, -1); - add_gpio_pin(pins, "INDICATOR disk3_present", - 3, 0, -1); - add_gpio_pin(pins, "INDICATOR disk4_present", - 4, 0, -1); - add_gpio_pin(pins, "INDICATOR disk5_present", - 5, 0, -1); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; - case 0x3e: /* front panel LEDs (E250/E450) */ - pins = prop_array_create(); - add_gpio_pin(pins, "LED disk_fault", 0, 0, -1); - add_gpio_pin(pins, "LED psu_fault", 1, 0, -1); - add_gpio_pin(pins, "LED overtemp", 2, 0, -1); - add_gpio_pin(pins, "LED fault", 3, 0, -1); - add_gpio_pin(pins, "LED activity", 4, 0, -1); - /* Pin 5 is power LED, but not controllable */ - add_gpio_pin(pins, "INDICATOR key_normal", 6, 0, -1); - add_gpio_pin(pins, "INDICATOR key_diag", 7, 0, -1); - /* If not "normal" or "diag", key is "lock" */ - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; - case 0x3f: /* disk fault LEDs */ - pins = prop_array_create(); - add_gpio_pin(pins, "LED disk0_fault", 0, 0, -1); - add_gpio_pin(pins, "LED disk1_fault", 1, 0, -1); - add_gpio_pin(pins, "LED disk2_fault", 2, 0, -1); - add_gpio_pin(pins, "LED disk3_fault", 3, 0, -1); - add_gpio_pin(pins, "LED disk4_fault", 4, 0, -1); - add_gpio_pin(pins, "LED disk5_fault", 5, 0, -1); - prop_dictionary_set(dict, "pins", pins); - prop_object_release(pins); - break; +static bool +device_is_on_onboard_scsi(device_t dev) +{ + device_t parent = device_parent(dev); + + if (onboard_scsi_phandle == 0) { + return false; + } + + if (!device_is_a(parent, "scsibus")) { + return false; } + + device_t grandparent = device_parent(parent); + KASSERT(grandparent != NULL); + + return device_handle_matches_phandle(grandparent, onboard_scsi_phandle); } -void -add_drivebay_props(device_t dev, int ofnode, void *aux) +/***************************************************************************** + * System fixups for SUNW,Sun-Fire-V210 and SUNW,Sun-Fire-V240 + *****************************************************************************/ + +static void +v210_drive_bay_0_1_fixup(device_t dev, void *aux) { - struct scsipibus_attach_args *sa = aux; - int target = sa->sa_periph->periph_target; - prop_dictionary_t dict = device_properties(dev); - char path[256]= ""; - char name[16]; - int nbays; - - if ((strcmp(machine_model, "SUNW,Sun-Fire-V210") == 0) || - (strcmp(machine_model, "SUNW,Sun-Fire-V240") == 0)) { - OF_package_to_path(ofnode, path, sizeof(path)); - - /* see if we're on the onboard controller's 1st channel */ - if (strcmp(path, "/pci@1c,600000/scsi@2") != 0) - return; - - /* yes, yes we are */ - if (strcmp(machine_model, "SUNW,Sun-Fire-V240") == 0) - nbays = 4; - else - nbays = 2; - if ( target < nbays) { + static const struct gpio_pin_fixup addpins[] = { + { .name = "LED bay0_fault", .num = 10, .act = 0, .def = 0 }, + { .name = "LED bay1_fault", .num = 11, .act = 0, .def = 0 }, + { .name = "LED bay0_remove", .num = 12, .act = 0, .def = 0 }, + { .name = "LED bay1_remove", .num = 13, .act = 0, .def = 0 }, + }; + add_gpio_pins(dev, addpins, __arraycount(addpins)); +} + +static void +v240_drive_bay_2_3_fixup(device_t dev, void *aux) +{ + static const struct gpio_pin_fixup addpins[] = { + { .name = "LED bay2_fault", .num = 10, .act = 0, .def = 0 }, + { .name = "LED bay3_fault", .num = 11, .act = 0, .def = 0 }, + { .name = "LED bay2_remove", .num = 12, .act = 0, .def = 0 }, + { .name = "LED bay3_remove", .num = 13, .act = 0, .def = 0 }, + }; + add_gpio_pins(dev, addpins, __arraycount(addpins)); +} + +static void +v210_front_panel_fixup(device_t dev, void *aux) +{ + static const struct gpio_pin_fixup addpins[] = { + { .name = "LED indicator", .num = 7, .act = 0, .def = -1 }, + { .name = "LED fault", .num = 5, .act = 0, .def = 0 }, + { .name = "LED power", .num = 4, .act = 0, .def = 1 }, + }; + add_gpio_pins(dev, addpins, __arraycount(addpins)); +} + +static int v210_env_sensors_i2c_phandle __read_mostly; + +static void +v210_env_sensors_fixup(device_t dev, void *aux) +{ + static const struct i2c_deventry i2c_adds[] = { + { .name = "hardware-monitor", + .compat = "i2c-adm1026", .addr = 0x2e }, + + { .name = "temperature-sensor", + .compat = "i2c-lm75", .addr = 0x4e }, + }; + + /* Squirrel away the phandle for later. */ + KASSERT(v210_env_sensors_i2c_phandle == 0); + devhandle_t devhandle = device_handle(dev); + v210_env_sensors_i2c_phandle = devhandle_to_of(devhandle); + + add_i2c_devices(dev, i2c_adds, __arraycount(i2c_adds)); +} + +static const struct device_compatible_entry dtnode_fixup_table_v210[] = { + { .compat = "/pci/isa@7/i2c@0,320/gpio@0,46", + .data = v210_drive_bay_0_1_fixup }, + + /* V240 only */ + { .compat = "/pci/isa@7/i2c@0,320/gpio@0,4a", + .data = v240_drive_bay_2_3_fixup }, + + { .compat = "/pci/isa@7/i2c@0,320/gpio@0,70", + .data = v210_front_panel_fixup }, + + { .compat = "/pci/isa@7/i2c@0,320", + .data = v210_env_sensors_fixup }, + + { .compat = "/pci@1c,600000/scsi@2", + .data = onboard_scsi_fixup }, + + DEVICE_COMPAT_EOL +}; + +static void +v210_v240_special_fixups_onboard_scsi(device_t dev, void *aux, int nbays) +{ + if (device_is_on_onboard_scsi(dev)) { + struct scsipibus_attach_args *sa = aux; + int target = sa->sa_periph->periph_target; + prop_dictionary_t dict = device_properties(dev); + char name[16]; + + if (target < nbays) { snprintf(name, sizeof(name), "bay%d", target); prop_dictionary_set_string(dict, "location", name); } } +} - if (!strcmp(machine_model, "SUNW,Ultra-250")) { - OF_package_to_path(ofnode, path, sizeof(path)); +static void +v210_special_fixups_hardware_monitor(device_t dev, void *aux) +{ + device_t parent = device_parent(dev); - /* see if we're on the onboard controller's 1st channel */ - if (strcmp(path, "/pci@1f,4000/scsi@3") != 0) - return; - - /* disk 0 is target 0 */ - if (!target) { - strncpy(name, "bay0", sizeof(name)); - prop_dictionary_set_string(dict, "location", name); - /* disks 1 - 5 are targets 8 - 12 */ - } else if ( target < 13) { - snprintf(name, sizeof(name), "bay%d", target - 7); - prop_dictionary_set_string(dict, "location", name); - } + /* Set some properties on the fan sensor. */ + + if (!device_is_a(parent, "iic")) { + return; + } + + device_t grandparent = device_parent(parent); + KASSERT(grandparent != NULL); + + + if (! device_handle_matches_phandle(grandparent, + v210_env_sensors_i2c_phandle)) { + return; + } + + struct i2c_attach_args *ia = aux; + if (ia->ia_addr == 0x2e) { + prop_dictionary_t props = device_properties(dev); + prop_dictionary_set_uint8(props, "fan_div2", 0x55); + prop_dictionary_set_bool(props, "multi_read", true); } } -/* - * Add SPARCle spdmem devices (0x50 and 0x51) that are not in the OFW tree - */ -void -add_spdmem_props_sparcle(device_t busdev) +static void +v210_special_fixups(device_t dev, void *aux) { - prop_array_t cfg; - int i; + v210_v240_special_fixups_onboard_scsi(dev, aux, 2); + v210_special_fixups_hardware_monitor(dev, aux); +} + +static void +v240_special_fixups(device_t dev, void *aux) +{ + v210_v240_special_fixups_onboard_scsi(dev, aux, 4); + v210_special_fixups_hardware_monitor(dev, aux); +} - DPRINTF(ACDB_PROBE, ("\nAdding spdmem for SPARCle ")); +static const struct system_fixup system_fixups_v210 = { + .dtnode_fixups = dtnode_fixup_table_v210, + .special_fixups = v210_special_fixups, +}; - cfg = create_i2c_dict(busdev); - for (i = 0x50; i <= 0x51; i++) - add_i2c_device(cfg, "dimm-spd", NULL, i, 0); - prop_object_release(cfg); +static const struct system_fixup system_fixups_v240 = { + .dtnode_fixups = dtnode_fixup_table_v210, + .special_fixups = v240_special_fixups, +}; + +/***************************************************************************** + * System fixups for SUNW,Sun-Fire-V440 + *****************************************************************************/ + +static void +v440_hardware_monitor_fixup(device_t dev, void *aux) +{ + prop_dictionary_t props = device_properties(dev); + prop_dictionary_set_bool(props, "multi_read", true); } -/* - * Add V210/V240 environmental sensors that are not in the OFW tree. - */ -void -add_env_sensors_v210(device_t busdev) +static const struct device_compatible_entry dtnode_fixup_table_v440[] = { + { .compat = "/pci/isa@7/i2c@0,320/hardware-monitor@0,5c", + .data = v440_hardware_monitor_fixup }, + + DEVICE_COMPAT_EOL +}; + +static const struct system_fixup system_fixups_v440 = { + .dtnode_fixups = dtnode_fixup_table_v440, +}; + +/***************************************************************************** + * System fixups for SUNW,Ultra-250 + *****************************************************************************/ + +static int e250_envctrltwo_phandle __read_mostly; + +static void +e250_envctrltwo_fixup(device_t dev, void *aux) { - prop_array_t cfg; + static const struct i2c_deventry i2c_adds[] = { + /* PSU temperature / CPU fan */ + { .name = "PSU", .compat = "ecadc", .addr = 0x4a }, + + /* CPU and system board temperature */ + { .name = "CPU", .compat = "ecadc", .addr = 0x4f }, - DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); - cfg = create_i2c_dict(busdev); + /* GPIOs */ + { .name = "gpio", .compat = "i2c-pcf8574", .addr = 0x38 }, + { .name = "gpio", .compat = "i2c-pcf8574", .addr = 0x39 }, + { .name = "gpio", .compat = "i2c-pcf8574", .addr = 0x3d }, + { .name = "gpio", .compat = "i2c-pcf8574", .addr = 0x3e }, + { .name = "gpio", .compat = "i2c-pcf8574", .addr = 0x3f }, - /* ADM1026 at 0x2e */ - add_i2c_device(cfg, "hardware-monitor", "i2c-adm1026", 0x2e, 0); - /* LM75 at 0x4e */ - add_i2c_device(cfg, "temperature-sensor", "i2c-lm75", 0x4e, 0); + /* NVRAM */ + { .name = "nvram", .compat = "i2c-at24c02", .addr = 0x52 }, + + /* RSC clock */ + { .name = "rscrtc", .compat = "i2c-ds1307", .addr = 0x68 }, + }; + devhandle_t devhandle = device_handle(dev); + KASSERT(devhandle_type(devhandle) == DEVHANDLE_TYPE_OF); + + /* Squirrel away the phandle for later. */ + KASSERT(e250_envctrltwo_phandle == 0); + e250_envctrltwo_phandle = devhandle_to_of(devhandle); + + add_i2c_devices(dev, i2c_adds, __arraycount(i2c_adds)); } -/* Sensors and GPIO's for E450 and E250 */ -void -add_i2c_props_e450(device_t busdev, uint64_t node) -{ - prop_array_t cfg; +static const struct device_compatible_entry dtnode_fixup_table_e250[] = { + { .compat = "/pci@1f,4000/ebus@1/SUNW,envctrltwo@14,600000", + .data = e250_envctrltwo_fixup }, - DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); - cfg = create_i2c_dict(busdev); + { .compat = "/pci@1f,4000/scsi@3", + .data = onboard_scsi_fixup, }, - /* Power supply 1 temperature. */ - add_i2c_device(cfg, "PSU-1", "ecadc", 0x48, node); + DEVICE_COMPAT_EOL +}; - /* Power supply 2 temperature. */ - add_i2c_device(cfg, "PSU-2", "ecadc", 0x49, node); +static void +e250_special_fixups_envctrltwo(device_t dev, void *aux) +{ + /* interrupt status */ + static const struct gpio_pin_fixup gpio_0x38_addpins[] = { + { .name = "ALERT high_temp", .num = 1, .act = 0, .def = 30 }, + { .name = "ALERT disk_event", .num = 2, .act = 0, .def = 30 }, + { .name = "ALERT fan_fail", .num = 4, .act = 0, .def = 30 }, + { .name = "ALERT key_event", .num = 5, .act = 0, .def = 30 }, + { .name = "ALERT psu_event", .num = 6, .act = 0, .def = 30 }, + }; + + /* PSU status */ + static const struct gpio_pin_fixup gpio_0x39_addpins[] = { + { .name = "INDICATOR psu0_present", + .num = 0, .act = 0, .def = -1 }, + { .name = "INDICATOR psu1_present", + .num = 1, .act = 0, .def = -1 }, + { .name = "INDICATOR psu0_fault", + .num = 4, .act = 0, .def = -1 }, + { .name = "INDICATOR psu1_fault", + .num = 5, .act = 0, .def = -1 }, + }; + + /* disk status */ + static const struct gpio_pin_fixup gpio_0x3d_addpins[] = { + { .name = "INDICATOR disk0_present", + .num = 0, .act = 0, .def = -1 }, + { .name = "INDICATOR disk1_present", + .num = 1, .act = 0, .def = -1 }, + { .name = "INDICATOR disk2_present", + .num = 2, .act = 0, .def = -1 }, + { .name = "INDICATOR disk3_present", + .num = 3, .act = 0, .def = -1 }, + { .name = "INDICATOR disk4_present", + .num = 4, .act = 0, .def = -1 }, + { .name = "INDICATOR disk5_present", + .num = 5, .act = 0, .def = -1 }, + }; + + /* front panel */ + static const struct gpio_pin_fixup gpio_0x3e_addpins[] = { + { .name = "LED disk_fault", + .num = 0, .act = 0, .def = -1 }, + { .name = "LED psu_fault", + .num = 1, .act = 0, .def = -1 }, + { .name = "LED overtemp", + .num = 2, .act = 0, .def = -1 }, + { .name = "LED fault", + .num = 3, .act = 0, .def = -1 }, + { .name = "LED activity", + .num = 4, .act = 0, .def = -1 }, + + /* Pin 5 is power LED, but is not controllable. */ + + { .name = "INDICATOR key_normal", + .num = 6, .act = 0, .def = -1 }, + { .name = "INDICATOR key_diag", + .num = 7, .act = 0, .def = -1 }, + /* If not "normal" or "diag", key is "lock". */ + }; + + /* disk fault LEDs */ + static const struct gpio_pin_fixup gpio_0x3f_addpins[] = { + { .name = "LED disk0_fault", .num = 0, .act = 0, .def = -1 }, + { .name = "LED disk1_fault", .num = 1, .act = 0, .def = -1 }, + { .name = "LED disk2_fault", .num = 2, .act = 0, .def = -1 }, + { .name = "LED disk3_fault", .num = 3, .act = 0, .def = -1 }, + { .name = "LED disk4_fault", .num = 4, .act = 0, .def = -1 }, + { .name = "LED disk5_fault", .num = 5, .act = 0, .def = -1 }, + }; + + /* + * We need to fix up GPIO pin properties for GPIO controllers + * that don't appear in the device tree. + * + * We need to check if our parent is "iic" and grandparent + * is the envctrltwo node. + */ + device_t parent = device_parent(dev); + if (!device_is_a(parent, "iic")) { + return; + } - /* Power supply 3 temperature. */ - add_i2c_device(cfg, "PSU-3", "ecadc", 0x4a, node); + device_t grandparent = device_parent(dev); + KASSERT(grandparent != NULL); - /* Ambient temperature. */ - add_i2c_device(cfg, "ambient", "i2c-lm75", 0x4d, node); + if (! device_handle_matches_phandle(grandparent, + e250_envctrltwo_phandle)) { + return; + } - /* CPU temperatures. */ - add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node); + struct i2c_attach_args *ia = aux; + const struct gpio_pin_fixup *addpins; + int naddpins; - prop_object_release(cfg); + switch (ia->ia_addr) { + case 0x38: + addpins = gpio_0x38_addpins; + naddpins = __arraycount(gpio_0x38_addpins); + break; + + case 0x39: + addpins = gpio_0x39_addpins; + naddpins = __arraycount(gpio_0x39_addpins); + break; + + case 0x3d: + addpins = gpio_0x3d_addpins; + naddpins = __arraycount(gpio_0x3d_addpins); + break; + + case 0x3e: + addpins = gpio_0x3e_addpins; + naddpins = __arraycount(gpio_0x3e_addpins); + break; + + case 0x3f: + addpins = gpio_0x3f_addpins; + naddpins = __arraycount(gpio_0x3f_addpins); + break; + + default: + /* No fixups. */ + return; + } + add_gpio_pins(dev, addpins, naddpins); } -void -add_i2c_props_e250(device_t busdev, uint64_t node) +static void +e250_special_fixups_onboard_scsi(device_t dev, void *aux) { - prop_array_t cfg; - int i; + if (device_is_on_onboard_scsi(dev)) { + struct scsipibus_attach_args *sa = aux; + int target = sa->sa_periph->periph_target; + prop_dictionary_t dict = device_properties(dev); + char name[16]; + + /* + * disk 0 is target 0. + * disks 1 - 5 are targets 8 - 12. + */ + if (target == 0) { + prop_dictionary_set_string(dict, "location", "bay0"); + } else if (target >= 8 && target <= 12) { + snprintf(name, sizeof(name), "bay%d", target - 7); + prop_dictionary_set_string(dict, "location", name); + } + } +} - DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); - cfg = create_i2c_dict(busdev); +static void +e250_special_fixups(device_t dev, void *aux) +{ + e250_special_fixups_envctrltwo(dev, aux); + e250_special_fixups_onboard_scsi(dev, aux); +} + +static const struct system_fixup system_fixups_e250 = { + .dtnode_fixups = dtnode_fixup_table_e250, + .special_fixups = e250_special_fixups, +}; - /* PSU temperature / CPU fan */ - add_i2c_device(cfg, "PSU", "ecadc", 0x4a, node); +/***************************************************************************** + * System fixups for SUNW,Ultra-4 + *****************************************************************************/ - /* CPU & system board temperature */ - add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node); +static void +e450_envctrl_fixup(device_t dev, void *aux) +{ + static const struct i2c_deventry i2c_adds[] = { + /* Power supply 1 temperature. */ + { .name = "PSU-1", .compat = "ecadc", .addr = 0x48 }, - /* GPIO's */ - for (i = 0x38; i <= 0x39; i++) - add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node); - for (i = 0x3d; i <= 0x3f; i++) - add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node); + /* Power supply 2 temperature. */ + { .name = "PSU-2", .compat = "ecadc", .addr = 0x49 }, - /* NVRAM */ - add_i2c_device(cfg, "nvram", "i2c-at24c02", 0x52, node); + /* Power supply 3 temperature. */ + { .name = "PSU-3", .compat = "ecadc", .addr = 0x4a }, - /* RSC clock */ - add_i2c_device(cfg, "rscrtc", "i2c-ds1307", 0x68, node); + /* Ambient temperature. */ + { .name = "ambient", .compat = "i2c-lm75", .addr = 0x4d }, - prop_object_release(cfg); + /* CPU temperatures. */ + { .name = "CPU", .compat = "ecadc", .addr = 0x4f }, + }; + devhandle_t devhandle = device_handle(dev); + KASSERT(devhandle_type(devhandle) == DEVHANDLE_TYPE_OF); + add_i2c_devices(dev, i2c_adds, __arraycount(i2c_adds)); } -/* Hardware specific device properties */ -void -set_hw_props(device_t dev) +static const struct device_compatible_entry dtnode_fixup_table_e450[] = { + { .compat = "/pci/ebus@1/SUNW,envctrl@14,600000", + .data = e450_envctrl_fixup }, + + DEVICE_COMPAT_EOL +}; + +static const struct system_fixup system_fixups_e450 = { + .dtnode_fixups = dtnode_fixup_table_e450, +}; + +/***************************************************************************** + * System fixups for TAD,SPARCLE + *****************************************************************************/ + +static void +sparcle_smbus_fixup(device_t dev, void *aux) { - device_t busdev = device_parent(dev); + static const struct i2c_deventry i2c_adds[] = { + { .name = "dimm-spd", .addr = 0x50 }, + { .name = "dimm-spd", .addr = 0x51 }, + }; + devhandle_t devhandle = device_handle(dev); + KASSERT(devhandle_type(devhandle) == DEVHANDLE_TYPE_OF); + add_i2c_devices(dev, i2c_adds, __arraycount(i2c_adds)); +}; + +static const struct device_compatible_entry dtnode_fixup_table_sparcle[] = { + { .compat = "/pci/ebut@11", + .data = sparcle_smbus_fixup }, + + DEVICE_COMPAT_EOL +}; + +static const struct system_fixup system_fixups_sparcle = { + .dtnode_fixups = dtnode_fixup_table_sparcle, +}; + +/***************************************************************************** + * System fixups for NATE,Meso-999 + *****************************************************************************/ - if ((!strcmp(machine_model, "SUNW,Sun-Fire-V240") || - !strcmp(machine_model, "SUNW,Sun-Fire-V210"))) { - device_t busparent = device_parent(busdev); - prop_dictionary_t props = device_properties(dev); +static void +meso999_edid_fixup(device_t dev, void *aux) +{ + prop_dictionary_t props = device_properties(dev); + prop_dictionary_set_data(props, "EDID:1", + edid_meso999, sizeof(edid_meso999)); +} + +static const struct device_compatible_entry dtnode_fixup_table_meso999[] = { + { .compat = "/pci/SUNW,XVR-100@2", + .data = meso999_edid_fixup }, - if (busparent != NULL && device_is_a(busparent, "pcfiic") && - device_is_a(dev, "adm1026hm") && props != NULL) { - prop_dictionary_set_uint8(props, "fan_div2", 0x55); - prop_dictionary_set_bool(props, "multi_read", true); + DEVICE_COMPAT_EOL +}; + +static const struct system_fixup system_fixups_meso999 = { + .dtnode_fixups = dtnode_fixup_table_meso999, +}; + +/***************************************************************************** + * End of system-specific data + *****************************************************************************/ + +/* + * Some systems are missing some important information in the + * OpenFirmware device tree, or the tree has some quirks we need + * to deal with. + */ +static const struct device_compatible_entry system_fixup_table[] = { + { .compat = "SUNW,Sun-Fire-V210", .data = &system_fixups_v210 }, + { .compat = "SUNW,Sun-Fire-V240", .data = &system_fixups_v240 }, + + { .compat = "SUNW,Sun-Fire-V440", .data = &system_fixups_v440 }, + + { .compat = "SUNW,Ultra-250", .data = &system_fixups_e250 }, + + { .compat = "SUNW,Ultra-4", .data = &system_fixups_e450 }, + + { .compat = "TAD,SPARCLE", .data = &system_fixups_sparcle }, + + { .compat = "NATE,Meso-999", .data = &system_fixups_meso999 }, + + DEVICE_COMPAT_EOL +}; + +#define MAX_PACKAGE_PATH 512 + +void +sparc64_device_tree_fixup(device_t dev, void *aux) +{ + static const struct system_fixup *system_fixup_entry; + static bool system_fixup_entry_initialized; + const struct device_compatible_entry *dce; + void (*fn)(device_t, void *); + devhandle_t devhandle; + + devhandle = device_handle(dev); + + if (! system_fixup_entry_initialized) { + dce = device_compatible_lookup((const char **)&machine_model, 1, + system_fixup_table); + if (dce != NULL) { + system_fixup_entry = dce->data; } + system_fixup_entry_initialized = true; + } + if (system_fixup_entry == NULL) { + /* No fixups for this machine. */ + return; } - if (!strcmp(machine_model, "SUNW,Sun-Fire-V440")) { - device_t busparent = device_parent(busdev); - prop_dictionary_t props = device_properties(dev); - if (busparent != NULL && device_is_a(busparent, "pcfiic") && - device_is_a(dev, "adm1026hm") && props != NULL) { - prop_dictionary_set_bool(props, "multi_read", true); + /* + * First apply any applicable fixups to this device based + * on its node in the device tree. + */ + if (system_fixup_entry->dtnode_fixups != NULL && + devhandle_type(devhandle) == DEVHANDLE_TYPE_OF) { + int phandle = devhandle_to_of(devhandle); + char *package_path = kmem_zalloc(MAX_PACKAGE_PATH, KM_SLEEP); + int path_size; + + path_size = OF_package_to_path(phandle, package_path, + MAX_PACKAGE_PATH); + package_path[MAX_PACKAGE_PATH - 1] = '\0'; /* sanity */ + if (path_size > 0) { + const char *ccp = package_path; + dce = device_compatible_lookup(&ccp, 1, + system_fixup_entry->dtnode_fixups); + if (dce != NULL && (fn = dce->data) != NULL) { + (*fn)(dev, aux); + } } + kmem_free(package_path, MAX_PACKAGE_PATH); } -} - -/* Static EDID definitions */ -void -set_static_edid(prop_dictionary_t dict) -{ - if (!strcmp(machine_model, "NATE,Meso-999")) { - prop_data_t edid; - DPRINTF(ACDB_PROBE, ("\nAdding EDID for Meso-999 ")); - edid = prop_data_create_copy(edid_meso999, - sizeof(edid_meso999)); - prop_dictionary_set(dict, "EDID:1", edid); - prop_object_release(edid); + /* + * Now apply any special fixups (this is mainly applicable to + * deivces that do not have nodes in the device tree. + */ + if (system_fixup_entry->special_fixups != NULL) { + (*system_fixup_entry->special_fixups)(dev, aux); } } Index: arch/sparc64/sparc64/ofw_patch.h =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/ofw_patch.h,v retrieving revision 1.4 retrieving revision 1.4.16.1 diff -u -p -r1.4 -r1.4.16.1 --- arch/sparc64/sparc64/ofw_patch.h 29 Oct 2020 06:47:38 -0000 1.4 +++ arch/sparc64/sparc64/ofw_patch.h 9 Aug 2021 00:30:08 -0000 1.4.16.1 @@ -1,4 +1,4 @@ -/* $NetBSD: ofw_patch.h,v 1.4 2020/10/29 06:47:38 jdc Exp $ */ +/* $NetBSD: ofw_patch.h,v 1.4.16.1 2021/08/09 00:30:08 thorpej Exp $ */ /*- * Copyright (c) 2020 The NetBSD Foundation, Inc. @@ -41,14 +41,6 @@ extern int autoconf_debug; #define DPRINTF(l, s) #endif -void add_gpio_props_v210(device_t, void *); -void add_gpio_props_e250(device_t, void *); -void add_drivebay_props(device_t, int, void *); -void add_spdmem_props_sparcle(device_t); -void add_env_sensors_v210(device_t); -void add_i2c_props_e450(device_t, uint64_t); -void add_i2c_props_e250(device_t, uint64_t); -void set_hw_props(device_t); -void set_static_edid(prop_dictionary_t); +void sparc64_device_tree_fixup(device_t, void *); #endif /* OFW_PATCH_H */