I had only positive feedback on the proposal so far. Attached is a updated version of the MI parts.
I did a bit of cleanup and added a way to pass device dependend "cookies" trough to the i2c devices - I need this in a machine specific driver, and it seems to be the simplest way to do this (actually I haven't found any other sensible way). For machines using OpenFirmware this would be the OF node of the i2c device, for others it could be a pointer to some ACPI table entry or whatever. I intend to commit this sometime later next week, it is blocking the addition of a few drivers urgently needed to make some machines usefull. Martin
Index: dev/ofw/openfirm.h =================================================================== RCS file: /cvsroot/src/sys/dev/ofw/openfirm.h,v retrieving revision 1.27 diff -c -u -p -r1.27 openfirm.h --- dev/ofw/openfirm.h 11 Nov 2009 16:56:52 -0000 1.27 +++ dev/ofw/openfirm.h 21 Feb 2010 13:25:53 -0000 @@ -115,4 +115,6 @@ boolean_t of_to_dataprop(prop_dictionary int *of_network_decode_media(int, int *, int *); char *of_get_mode_string(char *, int); +void of_enter_i2c_devs(prop_dictionary_t, int); + #endif /*_OPENFIRM_H_*/ Index: dev/ofw/ofw_subr.c =================================================================== RCS file: /cvsroot/src/sys/dev/ofw/ofw_subr.c,v retrieving revision 1.16 diff -c -u -p -r1.16 ofw_subr.c --- dev/ofw/ofw_subr.c 21 Jan 2010 15:56:08 -0000 1.16 +++ dev/ofw/ofw_subr.c 21 Feb 2010 13:25:53 -0000 @@ -325,3 +325,60 @@ of_get_mode_string(char *buffer, int len strncpy(buffer, pos + 2, len); return buffer; } + +/* + * Iterate over the subtree of a i2c controller node. + * Add all sub-devices into an array as part of the controller's + * device properties. + * This is used by the i2c bus attach code to do direct configuration. + */ +void +of_enter_i2c_devs(prop_dictionary_t props, int ofnode) +{ + int node, len; + char name[32]; + uint64_t r64; + uint64_t r32; + uint8_t smr[24]; + prop_array_t array; + prop_dictionary_t dev; + + array = prop_array_create(); + + for (node = OF_child(ofnode); node; node = OF_peer(node)) { + if (OF_getprop(node, "name", name, sizeof(name)) <= 0) + continue; + len = OF_getproplen(node, "reg"); + if (len == sizeof(r64)) { + if (OF_getprop(node, "reg", &r64, sizeof(r64)) + != sizeof(r64)) + continue; + r32 = r64; + } else if (len == sizeof(r32)) { + if (OF_getprop(node, "reg", &r32, sizeof(r32)) + != sizeof(r32)) + continue; + } else if (len == 24) { + if (OF_getprop(node, "reg", smr, sizeof(smr)) + != sizeof(smr)) + continue; + /* smbus reg property */ + r32 = smr[7]; + } else { + panic("unexpected \"reg\" size %d for \"%s\", " + "parent %x, node %x", + len, name, ofnode, node); + } + + dev = prop_dictionary_create(); + prop_dictionary_set_cstring(dev, "name", name); + prop_dictionary_set_uint32(dev, "addr", r32 >> 1); + prop_dictionary_set_uint64(dev, "cookie", node); + of_to_dataprop(dev, node, "compatible", "compatible"); + prop_array_add(array, dev); + prop_object_release(dev); + } + + prop_dictionary_set(props, "i2c-child-devices", array); + prop_object_release(array); +} Index: dev/i2c/i2cvar.h =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/i2cvar.h,v retrieving revision 1.6 diff -c -u -p -r1.6 i2cvar.h --- dev/i2c/i2cvar.h 9 Jul 2007 21:00:33 -0000 1.6 +++ dev/i2c/i2cvar.h 21 Feb 2010 13:25:53 -0000 @@ -118,12 +118,29 @@ struct i2c_attach_args { i2c_addr_t ia_addr; /* address of device */ int ia_size; /* size (for EEPROMs) */ int ia_type; /* bus type */ + /* only set if using direct config */ + const char * ia_name; /* name of the device */ + int ia_ncompat; /* number of pointers in the + ia_compat array */ + const char ** ia_compat; /* chip names */ + /* + * The following is of limited usefullness and should only be used + * in rare cases where we realy know what we are doing. Example: + * a machine depended i2c driver (located in sys/arch/$arch/dev) + * needing to access some firmware properties. + * Depending on the firmware in use, an identifier for the device + * may be present. Example: on OpenFirmware machines the device + * tree OF node - if available. This info is hard to transport + * down to MD drivers through the MI i2c bus otherwise. + */ + uintptr_t ia_cookie; /* OF node in openfirmware machines */ }; /* * API presented to i2c controllers. */ int iicbus_print(void *, const char *); +int iic_compat_match(struct i2c_attach_args*, const char **); #ifdef _I2C_PRIVATE /* Index: dev/i2c/i2c.c =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/i2c.c,v retrieving revision 1.23 diff -c -u -p -r1.23 i2c.c --- dev/i2c/i2c.c 3 Feb 2009 16:41:31 -0000 1.23 +++ dev/i2c/i2c.c 21 Feb 2010 13:25:53 -0000 @@ -59,6 +59,8 @@ struct iic_softc { }; static void iic_smbus_intr_thread(void *); +static void iic_fill_compat(struct i2c_attach_args*, const char*, + size_t, char **); int iicbus_print(void *aux, const char *pnp) @@ -71,6 +73,20 @@ iicbus_print(void *aux, const char *pnp) } static int +iic_print_direct(void *aux, const char *pnp) +{ + struct i2c_attach_args *ia = aux; + + if (pnp != NULL) + aprint_normal("%s at %s addr 0x%02x", ia->ia_name, pnp, + ia->ia_addr); + else + aprint_normal(" addr 0x%02x", ia->ia_addr); + + return UNCONF; +} + +static int iic_print(void *aux, const char *pnp) { struct i2c_attach_args *ia = aux; @@ -110,6 +126,8 @@ iic_attach(device_t parent, device_t sel { struct iic_softc *sc = device_private(self); struct i2cbus_attach_args *iba = aux; + prop_array_t child_devices; + char *buf; i2c_tag_t ic; int rv; @@ -196,11 +214,65 @@ iic_attach(device_t parent, device_t sel if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); - /* - * Attach all i2c devices described in the kernel - * configuration file. - */ - config_search_ia(iic_search, self, "iic", NULL); + child_devices = prop_dictionary_get(device_properties(parent), + "i2c-child-devices"); + if (child_devices) { + unsigned int i, count; + prop_dictionary_t dev; + prop_data_t cdata; + uint32_t addr, size; + uint64_t cookie; + const char *name; + struct i2c_attach_args ia; + int loc[2]; + + memset(loc, 0, sizeof loc); + count = prop_array_count(child_devices); + for (i = 0; i < count; i++) { + dev = prop_array_get(child_devices, i); + if (!dev) continue; + if (!prop_dictionary_get_cstring_nocopy( + dev, "name", &name)) + continue; + if (!prop_dictionary_get_uint32(dev, "addr", &addr)) + continue; + if (!prop_dictionary_get_uint64(dev, "cookie", &cookie)) + cookie = 0; + loc[0] = addr; + if (prop_dictionary_get_uint32(dev, "size", &size)) + loc[1] = size; + else + loc[1] = -1; + + memset(&ia, 0, sizeof ia); + ia.ia_addr = addr; + ia.ia_type = sc->sc_type; + ia.ia_tag = ic; + ia.ia_name = name; + ia.ia_cookie = cookie; + + buf = NULL; + cdata = prop_dictionary_get(dev, "compatible"); + if (cdata) + iic_fill_compat(&ia, + prop_data_data_nocopy(cdata), + prop_data_size(cdata), &buf); + + config_found_sm_loc(self, "iic", loc, &ia, + iic_print_direct, NULL); + + if (ia.ia_compat) + free(ia.ia_compat, M_TEMP); + if (buf) + free(buf, M_TEMP); + } + } else { + /* + * Attach all i2c devices described in the kernel + * configuration file. + */ + config_search_ia(iic_search, self, "iic", NULL); + } } static void @@ -303,5 +375,55 @@ iic_smbus_intr(i2c_tag_t ic) return 1; } +static void +iic_fill_compat(struct i2c_attach_args *ia, const char *compat, size_t len, + char **buffer) +{ + int count, i; + const char *c, *start, **ptr; + + *buffer = NULL; + for (i = count = 0, c = compat; i < len; i++, c++) + if (*c == 0) + count++; + count += 2; + ptr = malloc(sizeof(char*)*count, M_TEMP, M_WAITOK); + if (!ptr) return; + + for (i = count = 0, start = c = compat; i < len; i++, c++) { + if (*c == 0) { + ptr[count++] = start; + start = c+1; + } + } + if (start < compat+len) { + /* last string not 0 terminated */ + size_t l = c-start; + *buffer = malloc(l+1, M_TEMP, M_WAITOK); + memcpy(*buffer, start, l); + (*buffer)[l] = 0; + ptr[count++] = *buffer; + } + ptr[count] = NULL; + + ia->ia_compat = ptr; + ia->ia_ncompat = count; +} + +int +iic_compat_match(struct i2c_attach_args *ia, const char ** compats) +{ + int i; + + for (; compats && *compats; compats++) { + for (i = 0; i < ia->ia_ncompat; i++) { + if (strcmp(*compats, ia->ia_compat[i]) == 0) + return 1; + } + } + return 0; +} + + CFATTACH_DECL_NEW(iic, sizeof(struct iic_softc), iic_match, iic_attach, NULL, NULL); Index: dev/i2c/spdmem.c =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/spdmem.c,v retrieving revision 1.15 diff -c -u -p -r1.15 spdmem.c --- dev/i2c/spdmem.c 9 May 2009 17:32:27 -0000 1.15 +++ dev/i2c/spdmem.c 21 Feb 2010 13:25:53 -0000 @@ -154,6 +154,7 @@ static uint16_t spdcrc16 (struct spdmem_ } return (crc & 0xFFFF); } + static int spdmem_match(device_t parent, cfdata_t match, void *aux) { @@ -164,8 +165,17 @@ spdmem_match(device_t parent, cfdata_t m int spd_len, spd_crc_cover; uint16_t crc_calc, crc_spd; - if ((ia->ia_addr & SPDMEM_ADDRMASK) != SPDMEM_ADDR) - return 0; + if (ia->ia_name) { + /* add other names as we find more firmware variations */ + if (strcmp(ia->ia_name, "dimm-spd")) + return 0; + } + + /* only do this lame test when not using direct config */ + if (ia->ia_name == NULL) { + if ((ia->ia_addr & SPDMEM_ADDRMASK) != SPDMEM_ADDR) + return 0; + } sc.sc_tag = ia->ia_tag; sc.sc_addr = ia->ia_addr; Index: dev/i2c/adm1021.c =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/adm1021.c,v retrieving revision 1.3 diff -c -u -p -r1.3 adm1021.c --- dev/i2c/adm1021.c 5 Jun 2009 12:42:43 -0000 1.3 +++ dev/i2c/adm1021.c 21 Feb 2010 13:25:53 -0000 @@ -62,18 +62,35 @@ void admtemp_refresh(struct sysmon_envsy CFATTACH_DECL_NEW(admtemp, sizeof(struct admtemp_softc), admtemp_match, admtemp_attach, NULL, NULL); +static const char * admtemp_compats[] = { + "i2c-max1617", + NULL +}; int admtemp_match(device_t parent, cfdata_t match, void *aux) { struct i2c_attach_args *ia = aux; - if (((ia->ia_addr >= 0x18) && (ia->ia_addr <= 0x1a)) || - ((ia->ia_addr >= 0x29) && (ia->ia_addr <= 0x2b)) || - ((ia->ia_addr >= 0x4c) && (ia->ia_addr <= 0x4e))) - return (1); + if (ia->ia_name == NULL) { + /* + * Indirect config - not much we can do! + * Check typical addresses. + */ + if (((ia->ia_addr >= 0x18) && (ia->ia_addr <= 0x1a)) || + ((ia->ia_addr >= 0x29) && (ia->ia_addr <= 0x2b)) || + ((ia->ia_addr >= 0x4c) && (ia->ia_addr <= 0x4e))) + return (1); + } else { + /* + * Direct config - match via the list of compatible + * hardware. + */ + if (iic_compat_match(ia, admtemp_compats)) + return 1; + } - return (0); + return 0; } Index: dev/i2c/lm75.c =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/lm75.c,v retrieving revision 1.20 diff -c -u -p -r1.20 lm75.c --- dev/i2c/lm75.c 9 Jan 2009 17:20:31 -0000 1.20 +++ dev/i2c/lm75.c 21 Feb 2010 13:25:53 -0000 @@ -71,6 +71,16 @@ static uint32_t lmtemp_decode_lm75(const static uint32_t lmtemp_decode_ds75(const uint8_t *); static uint32_t lmtemp_decode_lm77(const uint8_t *); + +static const char * lmtemp_compats[] = { + "i2c-lm75", + /* + * see XXX in _attach() below: add code once non-lm75 matches are + * added here! + */ + NULL +}; + enum { lmtemp_lm75 = 0, lmtemp_ds75, @@ -100,15 +110,28 @@ lmtemp_match(device_t parent, cfdata_t c struct i2c_attach_args *ia = aux; int i; - for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) - if (lmtemptbl[i].lmtemp_type == cf->cf_flags) - break; - if (lmtemptbl[i].lmtemp_type == -1) - return 0; - - if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == - lmtemptbl[i].lmtemp_addr) - return 1; + if (ia->ia_name == NULL) { + /* + * Indirect config - not much we can do! + */ + for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) + if (lmtemptbl[i].lmtemp_type == cf->cf_flags) + break; + if (lmtemptbl[i].lmtemp_type == -1) + return 0; + + if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == + lmtemptbl[i].lmtemp_addr) + return 1; + } else { + /* + * Direct config - match via the list of compatible + * hardware. + */ + if (iic_compat_match(ia, lmtemp_compats)) + return 1; + } + return 0; } @@ -120,16 +143,27 @@ lmtemp_attach(device_t parent, device_t struct i2c_attach_args *ia = aux; int i; - for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) - if (lmtemptbl[i].lmtemp_type == - device_cfdata(self)->cf_flags) - break; + if (ia->ia_name == NULL) { + for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++) + if (lmtemptbl[i].lmtemp_type == + device_cfdata(self)->cf_flags) + break; + } else { + /* XXX - add code when adding other direct matches! */ + i = 0; + } sc->sc_tag = ia->ia_tag; sc->sc_address = ia->ia_addr; aprint_naive(": Temperature Sensor\n"); - aprint_normal(": %s Temperature Sensor\n", lmtemptbl[i].lmtemp_name); + if (ia->ia_name) { + aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, + lmtemptbl[i].lmtemp_name); + } else { + aprint_normal(": %s Temperature Sensor\n", + lmtemptbl[i].lmtemp_name); + } /* Set the configuration of the LM75 to defaults. */ iic_acquire_bus(sc->sc_tag, I2C_F_POLL); @@ -143,7 +177,8 @@ lmtemp_attach(device_t parent, device_t sc->sc_sme = sysmon_envsys_create(); /* Initialize sensor data. */ sc->sc_sensor.units = ENVSYS_STEMP; - (void)strlcpy(sc->sc_sensor.desc, device_xname(self), + (void)strlcpy(sc->sc_sensor.desc, + ia->ia_name? ia->ia_name : device_xname(self), sizeof(sc->sc_sensor.desc)); if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { sysmon_envsys_destroy(sc->sc_sme);