1 /* $NetBSD: ofw_patch.c,v 1.9 2025/09/19 13:19:25 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julian Coleman. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: ofw_patch.c,v 1.9 2025/09/19 13:19:25 thorpej Exp $"); 33 34 #include <sys/param.h> 35 36 #include <dev/i2c/i2cvar.h> 37 #include <dev/scsipi/scsipiconf.h> 38 39 #include <machine/autoconf.h> 40 #include <machine/openfirm.h> 41 #include <sparc64/sparc64/ofw_patch.h> 42 #include <sparc64/sparc64/static_edid.h> 43 44 static void 45 add_gpio_pin(prop_array_t pins, const char *name, int num, int act, int def) 46 { 47 prop_dictionary_t pin = prop_dictionary_create(); 48 prop_dictionary_set_string(pin, "name", name); 49 prop_dictionary_set_uint32(pin, "pin", num); 50 prop_dictionary_set_bool(pin, "active_high", act); 51 if (def != -1) 52 prop_dictionary_set_int32(pin, "default_state", def); 53 prop_array_add(pins, pin); 54 prop_object_release(pin); 55 } 56 57 static prop_array_t 58 create_i2c_dict(device_t busdev) 59 { 60 prop_dictionary_t props = device_properties(busdev); 61 prop_array_t cfg = NULL; 62 63 cfg = prop_dictionary_get(props, "i2c-child-devices"); 64 if (!cfg) { 65 DPRINTF(ACDB_PROBE, ("\nCreating new i2c-child-devices\n")); 66 cfg = prop_array_create(); 67 prop_dictionary_set(props, "i2c-child-devices", cfg); 68 prop_dictionary_set_bool(props, "i2c-indirect-config", false); 69 } 70 return cfg; 71 } 72 73 static void 74 add_i2c_device(prop_array_t cfg, const char *name, const char *compat, 75 uint32_t addr, uint64_t node) 76 { 77 prop_dictionary_t dev; 78 devhandle_t child_devhandle; 79 80 DPRINTF(ACDB_PROBE, ("\nAdding i2c device: %s (%s) @ 0x%x (%lx)\n", 81 name, compat == NULL ? "NULL" : compat, addr, node & 0xffffffff)); 82 dev = prop_dictionary_create(); 83 prop_dictionary_set_string(dev, "name", name); 84 if (compat != NULL) 85 prop_dictionary_set_data(dev, "compatible", compat, 86 strlen(compat) + 1); 87 prop_dictionary_set_uint32(dev, "addr", addr); 88 if (node != 0) { 89 child_devhandle = 90 devhandle_from_of(devhandle_invalid(), node); 91 } else { 92 child_devhandle = devhandle_invalid(); 93 } 94 prop_dictionary_set_data(dev, "devhandle", 95 &child_devhandle, sizeof(child_devhandle)); 96 97 prop_array_add(cfg, dev); 98 prop_object_release(dev); 99 } 100 101 void 102 add_gpio_props_v210(device_t dev, void *aux) 103 { 104 struct i2c_attach_args *ia = aux; 105 prop_dictionary_t dict = device_properties(dev); 106 prop_array_t pins; 107 108 switch (ia->ia_addr) { 109 case 0x38: /* front panel LEDs */ 110 pins = prop_array_create(); 111 add_gpio_pin(pins, "LED indicator", 7, 0, -1); 112 add_gpio_pin(pins, "LED fault", 5, 0, 0); 113 add_gpio_pin(pins, "LED power", 4, 0, 1); 114 prop_dictionary_set(dict, "pins", pins); 115 prop_object_release(pins); 116 break; 117 case 0x23: /* drive bay O/1 LEDs */ 118 pins = prop_array_create(); 119 add_gpio_pin(pins, "LED bay0_fault", 10, 0, 0); 120 add_gpio_pin(pins, "LED bay1_fault", 11, 0, 0); 121 add_gpio_pin(pins, "LED bay0_remove", 12, 0, 0); 122 add_gpio_pin(pins, "LED bay1_remove", 13, 0, 0); 123 prop_dictionary_set(dict, "pins", pins); 124 prop_object_release(pins); 125 break; 126 case 0x25: /* drive bay 2/3 LEDs (v240 only)*/ 127 pins = prop_array_create(); 128 add_gpio_pin(pins, "LED bay2_fault", 10, 0, 0); 129 add_gpio_pin(pins, "LED bay3_fault", 11, 0, 0); 130 add_gpio_pin(pins, "LED bay2_remove", 12, 0, 0); 131 add_gpio_pin(pins, "LED bay3_remove", 13, 0, 0); 132 prop_dictionary_set(dict, "pins", pins); 133 prop_object_release(pins); 134 break; 135 } 136 } 137 138 void 139 add_gpio_props_e250(device_t dev, void *aux) 140 { 141 struct i2c_attach_args *ia = aux; 142 prop_dictionary_t dict = device_properties(dev); 143 prop_array_t pins; 144 145 switch (ia->ia_addr) { 146 case 0x38: /* interrupt status */ 147 pins = prop_array_create(); 148 add_gpio_pin(pins, "ALERT high_temp", 1, 0, 30); 149 add_gpio_pin(pins, "ALERT disk_event", 2, 0, 30); 150 add_gpio_pin(pins, "ALERT fan_fail", 4, 0, 30); 151 add_gpio_pin(pins, "ALERT key_event", 5, 0, 30); 152 add_gpio_pin(pins, "ALERT psu_event", 6, 0, 30); 153 prop_dictionary_set(dict, "pins", pins); 154 prop_object_release(pins); 155 break; 156 case 0x39: /* PSU status */ 157 pins = prop_array_create(); 158 add_gpio_pin(pins, "INDICATOR psu0_present", 0, 0, -1); 159 add_gpio_pin(pins, "INDICATOR psu1_present", 1, 0, -1); 160 add_gpio_pin(pins, "INDICATOR psu0_fault", 4, 0, -1); 161 add_gpio_pin(pins, "INDICATOR psu1_fault", 5, 0, -1); 162 prop_dictionary_set(dict, "pins", pins); 163 prop_object_release(pins); 164 break; 165 case 0x3d: /* disk status */ 166 pins = prop_array_create(); 167 add_gpio_pin(pins, "INDICATOR disk0_present", 168 0, 0, -1); 169 add_gpio_pin(pins, "INDICATOR disk1_present", 170 1, 0, -1); 171 add_gpio_pin(pins, "INDICATOR disk2_present", 172 2, 0, -1); 173 add_gpio_pin(pins, "INDICATOR disk3_present", 174 3, 0, -1); 175 add_gpio_pin(pins, "INDICATOR disk4_present", 176 4, 0, -1); 177 add_gpio_pin(pins, "INDICATOR disk5_present", 178 5, 0, -1); 179 prop_dictionary_set(dict, "pins", pins); 180 prop_object_release(pins); 181 break; 182 case 0x3e: /* front panel LEDs (E250/E450) */ 183 pins = prop_array_create(); 184 add_gpio_pin(pins, "LED disk_fault", 0, 0, -1); 185 add_gpio_pin(pins, "LED psu_fault", 1, 0, -1); 186 add_gpio_pin(pins, "LED overtemp", 2, 0, -1); 187 add_gpio_pin(pins, "LED fault", 3, 0, -1); 188 add_gpio_pin(pins, "LED activity", 4, 0, -1); 189 /* Pin 5 is power LED, but not controllable */ 190 add_gpio_pin(pins, "INDICATOR key_normal", 6, 0, -1); 191 add_gpio_pin(pins, "INDICATOR key_diag", 7, 0, -1); 192 /* If not "normal" or "diag", key is "lock" */ 193 prop_dictionary_set(dict, "pins", pins); 194 prop_object_release(pins); 195 break; 196 case 0x3f: /* disk fault LEDs */ 197 pins = prop_array_create(); 198 add_gpio_pin(pins, "LED disk0_fault", 0, 0, -1); 199 add_gpio_pin(pins, "LED disk1_fault", 1, 0, -1); 200 add_gpio_pin(pins, "LED disk2_fault", 2, 0, -1); 201 add_gpio_pin(pins, "LED disk3_fault", 3, 0, -1); 202 add_gpio_pin(pins, "LED disk4_fault", 4, 0, -1); 203 add_gpio_pin(pins, "LED disk5_fault", 5, 0, -1); 204 prop_dictionary_set(dict, "pins", pins); 205 prop_object_release(pins); 206 break; 207 } 208 } 209 210 void 211 add_drivebay_props(device_t dev, int ofnode, void *aux) 212 { 213 struct scsipibus_attach_args *sa = aux; 214 int target = sa->sa_periph->periph_target; 215 prop_dictionary_t dict = device_properties(dev); 216 char path[256]= ""; 217 char name[16]; 218 int nbays; 219 220 if ((strcmp(machine_model, "SUNW,Sun-Fire-V210") == 0) || 221 (strcmp(machine_model, "SUNW,Sun-Fire-V240") == 0)) { 222 OF_package_to_path(ofnode, path, sizeof(path)); 223 224 /* see if we're on the onboard controller's 1st channel */ 225 if (strcmp(path, "/pci@1c,600000/scsi@2") != 0) 226 return; 227 228 /* yes, yes we are */ 229 if (strcmp(machine_model, "SUNW,Sun-Fire-V240") == 0) 230 nbays = 4; 231 else 232 nbays = 2; 233 if ( target < nbays) { 234 snprintf(name, sizeof(name), "bay%d", target); 235 prop_dictionary_set_string(dict, "location", name); 236 } 237 } 238 239 if (!strcmp(machine_model, "SUNW,Ultra-250")) { 240 OF_package_to_path(ofnode, path, sizeof(path)); 241 242 /* see if we're on the onboard controller's 1st channel */ 243 if (strcmp(path, "/pci@1f,4000/scsi@3") != 0) 244 return; 245 246 /* disk 0 is target 0 */ 247 if (!target) { 248 strncpy(name, "bay0", sizeof(name)); 249 prop_dictionary_set_string(dict, "location", name); 250 /* disks 1 - 5 are targets 8 - 12 */ 251 } else if ( target < 13) { 252 snprintf(name, sizeof(name), "bay%d", target - 7); 253 prop_dictionary_set_string(dict, "location", name); 254 } 255 } 256 } 257 258 /* 259 * Add SPARCle spdmem devices (0x50 and 0x51) that are not in the OFW tree 260 */ 261 void 262 add_spdmem_props_sparcle(device_t busdev) 263 { 264 prop_array_t cfg; 265 int i; 266 267 DPRINTF(ACDB_PROBE, ("\nAdding spdmem for SPARCle ")); 268 269 cfg = create_i2c_dict(busdev); 270 for (i = 0x50; i <= 0x51; i++) 271 add_i2c_device(cfg, "dimm-spd", NULL, i, 0); 272 prop_object_release(cfg); 273 } 274 275 /* 276 * Add V210/V240 environmental sensors that are not in the OFW tree. 277 */ 278 void 279 add_env_sensors_v210(device_t busdev) 280 { 281 prop_array_t cfg; 282 283 DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); 284 cfg = create_i2c_dict(busdev); 285 286 /* ADM1026 at 0x2e */ 287 add_i2c_device(cfg, "hardware-monitor", "i2c-adm1026", 0x2e, 0); 288 /* LM75 at 0x4e */ 289 add_i2c_device(cfg, "temperature-sensor", "i2c-lm75", 0x4e, 0); 290 } 291 292 /* Sensors and GPIO's for E450 and E250 */ 293 void 294 add_i2c_props_e450(device_t busdev, uint64_t node) 295 { 296 prop_array_t cfg; 297 298 DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); 299 cfg = create_i2c_dict(busdev); 300 301 /* Power supply 1 temperature. */ 302 add_i2c_device(cfg, "PSU-1", "ecadc", 0x48, node); 303 304 /* Power supply 2 temperature. */ 305 add_i2c_device(cfg, "PSU-2", "ecadc", 0x49, node); 306 307 /* Power supply 3 temperature. */ 308 add_i2c_device(cfg, "PSU-3", "ecadc", 0x4a, node); 309 310 /* Ambient temperature. */ 311 add_i2c_device(cfg, "ambient", "i2c-lm75", 0x4d, node); 312 313 /* CPU temperatures. */ 314 add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node); 315 316 prop_object_release(cfg); 317 } 318 319 void 320 add_i2c_props_e250(device_t busdev, uint64_t node) 321 { 322 prop_array_t cfg; 323 int i; 324 325 DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); 326 cfg = create_i2c_dict(busdev); 327 328 /* PSU temperature / CPU fan */ 329 add_i2c_device(cfg, "PSU", "ecadc", 0x4a, node); 330 331 /* CPU & system board temperature */ 332 add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node); 333 334 /* GPIO's */ 335 for (i = 0x38; i <= 0x39; i++) 336 add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node); 337 for (i = 0x3d; i <= 0x3f; i++) 338 add_i2c_device(cfg, "gpio", "i2c-pcf8574", i, node); 339 340 /* NVRAM */ 341 add_i2c_device(cfg, "nvram", "i2c-at24c02", 0x52, node); 342 343 /* RSC clock */ 344 add_i2c_device(cfg, "rscrtc", "i2c-ds1307", 0x68, node); 345 346 prop_object_release(cfg); 347 } 348 349 /* Hardware specific device properties */ 350 void 351 set_hw_props(device_t dev) 352 { 353 device_t busdev = device_parent(dev); 354 355 if ((!strcmp(machine_model, "SUNW,Sun-Fire-V240") || 356 !strcmp(machine_model, "SUNW,Sun-Fire-V210"))) { 357 device_t busparent = device_parent(busdev); 358 prop_dictionary_t props = device_properties(dev); 359 360 if (busparent != NULL && device_is_a(busparent, "pcfiic") && 361 device_is_a(dev, "adm1026hm") && props != NULL) { 362 prop_dictionary_set_uint8(props, "fan_div2", 0x55); 363 prop_dictionary_set_bool(props, "multi_read", true); 364 } 365 } 366 367 if (!strcmp(machine_model, "SUNW,Sun-Fire-V440")) { 368 device_t busparent = device_parent(busdev); 369 prop_dictionary_t props = device_properties(dev); 370 if (busparent != NULL && device_is_a(busparent, "pcfiic") && 371 device_is_a(dev, "adm1026hm") && props != NULL) { 372 prop_dictionary_set_bool(props, "multi_read", true); 373 } 374 } 375 } 376 377 /* Static EDID definitions */ 378 void 379 set_static_edid(prop_dictionary_t dict) 380 { 381 if (!strcmp(machine_model, "NATE,Meso-999")) { 382 prop_data_t edid; 383 384 DPRINTF(ACDB_PROBE, ("\nAdding EDID for Meso-999 ")); 385 edid = prop_data_create_copy(edid_meso999, 386 sizeof(edid_meso999)); 387 prop_dictionary_set(dict, "EDID:1", edid); 388 prop_object_release(edid); 389 } 390 } 391