#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ofw_patch.c,v 1.15 2026/07/08 09:34:32 jdc Exp $");
#include <sys/param.h>
#include <dev/i2c/i2cvar.h>
#include <dev/scsipi/scsipiconf.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <sparc64/sparc64/ofw_patch.h>
#include <sparc64/sparc64/static_edid.h>
static void
add_gpio_pin(prop_array_t pins, const char *name, int num, int act, int def)
{
prop_dictionary_t pin = prop_dictionary_create();
prop_dictionary_set_string(pin, "name", name);
prop_dictionary_set_uint32(pin, "pin", num);
prop_dictionary_set_bool(pin, "active_high", act);
if (def != -1)
prop_dictionary_set_int32(pin, "default_state", def);
prop_array_add(pins, pin);
prop_object_release(pin);
}
static prop_array_t
create_i2c_dict(device_t busdev)
{
prop_dictionary_t props = device_properties(busdev);
prop_array_t cfg = NULL;
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;
}
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;
devhandle_t child_devhandle;
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);
if (node != 0) {
child_devhandle =
devhandle_from_of(devhandle_invalid(), node);
} else {
child_devhandle = devhandle_invalid();
}
prop_dictionary_set_data(dev, "devhandle",
&child_devhandle, sizeof(child_devhandle));
prop_array_add(cfg, dev);
prop_object_release(dev);
}
static void
add_gpio_props_v210(device_t dev, void *aux)
{
struct i2c_attach_args *ia = aux;
prop_dictionary_t dict = device_properties(dev);
prop_array_t pins;
switch (ia->ia_addr) {
case 0x38:
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:
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:
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;
}
}
static void
add_gpio_props_v245(device_t dev, void *aux)
{
struct i2c_attach_args *ia = aux;
prop_dictionary_t dict = device_properties(dev);
prop_array_t pins;
switch (ia->ia_addr) {
case 0x22:
pins = prop_array_create();
add_gpio_pin(pins,
"INDICATOR HDD 0 present", 8, 1, -1);
add_gpio_pin(pins,
"INDICATOR HDD 1 present", 9, 1, -1);
add_gpio_pin(pins,
"INDICATOR HDD 2 present", 10, 1, -1);
add_gpio_pin(pins,
"INDICATOR HDD 3 present", 11, 1, -1);
prop_dictionary_set(dict, "pins", pins);
prop_object_release(pins);
break;
}
}
static void
add_gpio_props_u45(device_t dev, void *aux)
{
struct i2c_attach_args *ia = aux;
prop_dictionary_t dict = device_properties(dev);
prop_array_t pins;
switch (ia->ia_addr) {
case 0x18:
pins = prop_array_create();
add_gpio_pin(pins, "LED power", 0, 1, -1);
add_gpio_pin(pins, "LED fault", 1, 0, -1);
prop_dictionary_set(dict, "pins", pins);
prop_object_release(pins);
break;
}
}
static void
add_gpio_props_e250(device_t dev, void *aux)
{
struct i2c_attach_args *ia = aux;
prop_dictionary_t dict = device_properties(dev);
prop_array_t pins;
switch (ia->ia_addr) {
case 0x38:
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:
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:
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:
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);
add_gpio_pin(pins, "INDICATOR key_normal", 6, 0, -1);
add_gpio_pin(pins, "INDICATOR key_diag", 7, 0, -1);
prop_dictionary_set(dict, "pins", pins);
prop_object_release(pins);
break;
case 0x3f:
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;
}
}
void
add_drivebay_props(device_t dev, int ofnode, 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));
if (strcmp(path, "/pci@1c,600000/scsi@2") != 0)
return;
if (strcmp(machine_model, "SUNW,Sun-Fire-V240") == 0)
nbays = 4;
else
nbays = 2;
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));
if (strcmp(path, "/pci@1f,4000/scsi@3") != 0)
return;
if (!target) {
strncpy(name, "bay0", sizeof(name));
prop_dictionary_set_string(dict, "location", name);
} else if ( target < 13) {
snprintf(name, sizeof(name), "bay%d", target - 7);
prop_dictionary_set_string(dict, "location", name);
}
}
}
static void
add_spdmem_props_sparcle(device_t busdev)
{
prop_array_t cfg;
int i;
DPRINTF(ACDB_PROBE, ("\nAdding spdmem for SPARCle "));
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 void
add_env_sensors_v210(device_t busdev)
{
prop_array_t cfg;
DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
cfg = create_i2c_dict(busdev);
add_i2c_device(cfg, "hardware-monitor", "i2c-adm1026", 0x2e, 0);
add_i2c_device(cfg, "temperature-sensor", "i2c-lm75", 0x4e, 0);
}
static void
add_env_sensors_u45(device_t busdev)
{
prop_array_t cfg;
DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
cfg = create_i2c_dict(busdev);
add_i2c_device(cfg, "temperature-sensor", "i2c-lm95221", 0x2b, 0);
add_i2c_device(cfg, "temperature-sensor", "i2c-lm75a", 0x4f, 0);
}
static void
add_env_sensors_v245(device_t busdev)
{
prop_array_t cfg;
DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
cfg = create_i2c_dict(busdev);
add_i2c_device(cfg, "temperature-sensor", "i2c-pca9555", 0x22, 0);
add_i2c_device(cfg, "temperature-sensor", "i2c-lm95221", 0x2b, 0);
add_i2c_device(cfg, "hardware-monitor", "i2c-adt7475", 0x2e, 0);
}
static void
add_i2c_props_e450(device_t busdev, uint64_t node)
{
prop_array_t cfg;
DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
cfg = create_i2c_dict(busdev);
add_i2c_device(cfg, "PSU-1", "ecadc", 0x48, node);
add_i2c_device(cfg, "PSU-2", "ecadc", 0x49, node);
add_i2c_device(cfg, "PSU-3", "ecadc", 0x4a, node);
add_i2c_device(cfg, "ambient", "i2c-lm75", 0x4d, node);
add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node);
prop_object_release(cfg);
}
static void
add_i2c_props_e250(device_t busdev, uint64_t node)
{
prop_array_t cfg;
int i;
DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model));
cfg = create_i2c_dict(busdev);
add_i2c_device(cfg, "PSU", "ecadc", 0x4a, node);
add_i2c_device(cfg, "CPU", "ecadc", 0x4f, node);
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);
add_i2c_device(cfg, "nvram", "i2c-at24c02", 0x52, node);
prop_object_release(cfg);
}
static void
fix_properties_u45(device_t busdev)
{
prop_dictionary_t props = device_properties(busdev);
prop_array_t cfg;
prop_object_t dev;
uint32_t addr;
const char *name;
int i, n;
cfg = prop_dictionary_get(props, "i2c-child-devices");
if (!cfg)
return;
n = prop_array_count(cfg);
for (i = 0; i < n; i++) {
dev = prop_array_get(cfg, i);
if (prop_object_type(dev) == PROP_TYPE_DICTIONARY &&
prop_dictionary_get_uint32(dev, "addr", &addr) &&
prop_dictionary_get_string(dev, "name", &name)) {
if (addr == 0x57)
prop_dictionary_set_data(dev, "compatible",
"i2c-at24c02", strlen("i2c-at24c02") + 1);
if ((addr == 0x2b || addr == 0x48 || addr == 0x4f) &&
!strcmp(name, "temperature")) {
prop_array_remove(cfg, i);
i -= 1;
n -= 1;
}
}
}
}
static void
fix_properties_v245(device_t busdev)
{
prop_dictionary_t props = device_properties(busdev);
prop_array_t cfg;
prop_object_t dev;
uint32_t addr;
const char *name;
int i, n;
cfg = prop_dictionary_get(props, "i2c-child-devices");
if (!cfg)
return;
n = prop_array_count(cfg);
for (i = 0; i < n; i++) {
dev = prop_array_get(cfg, i);
if (prop_object_type(dev) == PROP_TYPE_DICTIONARY &&
prop_dictionary_get_uint32(dev, "addr", &addr) &&
prop_dictionary_get_string(dev, "name", &name)) {
if (addr == 0x36 || addr == 0x37)
prop_dictionary_set_data(dev, "compatible",
"i2c-at24c02", strlen("i2c-at24c02") + 1);
}
}
}
void
set_i2c_bus_props(device_t busdev, uint64_t busnode)
{
if (!strcmp(machine_model, "TAD,SPARCLE"))
add_spdmem_props_sparcle(busdev);
if (device_is_a(busdev, "pcfiic")) {
if (!strcmp(machine_model, "SUNW,Sun-Fire-V240") ||
!strcmp(machine_model, "SUNW,Sun-Fire-V210"))
add_env_sensors_v210(busdev);
if (!strcmp(machine_model, "SUNW,A70") ||
!strcmp(machine_model, "SUNW,Ultra-25")) {
add_env_sensors_u45(busdev);
fix_properties_u45(busdev);
}
if (!strcmp(machine_model, "SUNW,Ultra-4"))
add_i2c_props_e450(busdev, busnode);
if (!strcmp(machine_model, "SUNW,Ultra-250"))
add_i2c_props_e250(busdev, busnode);
}
if (device_is_a(busdev, "firei2c")) {
if (!strcmp(machine_model, "SUNW,Sun-Fire-V245") ||
!strcmp(machine_model, "SUNW,Sun-Fire-V215")) {
add_env_sensors_v245(busdev);
fix_properties_v245(busdev);
}
}
}
void
set_i2c_dev_props(device_t dev, device_t busdev, void *aux)
{
if ((!strcmp(machine_model, "SUNW,Sun-Fire-V240") ||
!strcmp(machine_model, "SUNW,Sun-Fire-V210"))) {
if (device_is_a(dev, "pcagpio"))
add_gpio_props_v210(dev, aux);
if (device_is_a(dev, "adm1026hm")) {
prop_dictionary_t props = device_properties(dev);
prop_dictionary_set_uint8(props, "fan_div2", 0x55);
if (!strcmp(machine_model, "SUNW,Sun-Fire-V240"))
prop_dictionary_set_uint8(props,
"fan_mask", 0x08);
}
}
if (!strcmp(machine_model, "SUNW,Sun-Fire-V245"))
if (device_is_a(dev, "pcagpio"))
add_gpio_props_v245(dev, aux);
if (!strcmp(machine_model, "SUNW,A70")) {
if (device_is_a(dev, "adt7462sm")){
prop_dictionary_t props = device_properties(dev);
prop_dictionary_set_uint8(props, "fan_conf", 0x1f);
}
}
if (!strcmp(machine_model, "SUNW,A70") ||
!strcmp(machine_model, "SUNW,Ultra-25"))
if (device_is_a(dev, "pcagpio") &&
device_is_a(busdev, "firei2c"))
add_gpio_props_u45(dev, aux);
if (!strcmp(machine_model, "SUNW,Ultra-250"))
if (device_is_a(dev, "pcf8574io"))
add_gpio_props_e250(dev, aux);
if (device_is_a(dev, "dsrtc")) {
prop_dictionary_t props = device_properties(dev);
prop_dictionary_set_uint(props, "start-year", 2000);
}
}
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);
}
}