#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pcfiic_ebus.c,v 1.12 2026/07/09 14:55:03 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>
#include <sys/bus.h>
#include <machine/openfirm.h>
#include <machine/autoconf.h>
#include <dev/ebus/ebusreg.h>
#include <dev/ebus/ebusvar.h>
#include <dev/i2c/i2cvar.h>
#include <dev/ic/pcf8584var.h>
#include <dev/ic/pcf8584reg.h>
int pcfiic_ebus_match(device_t, struct cfdata *, void *);
void pcfiic_ebus_attach(device_t, device_t, void *);
struct pcfiic_ebus_softc {
struct pcfiic_softc esc_sc;
int esc_node;
void *esc_ih;
};
CFATTACH_DECL_NEW(pcfiic_ebus, sizeof(struct pcfiic_ebus_softc),
pcfiic_ebus_match, pcfiic_ebus_attach, NULL, NULL);
int
pcfiic_ebus_match(device_t parent, struct cfdata *match, void *aux)
{
struct ebus_attach_args *ea = aux;
char compat[32];
if (strcmp(ea->ea_name, "SUNW,envctrl") == 0 ||
strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)
return (1);
if (strcmp(ea->ea_name, "i2c") != 0)
return (0);
if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == -1)
return (0);
if (strcmp(compat, "pcf8584") == 0 ||
strcmp(compat, "i2cpcf,8584") == 0 ||
strcmp(compat, "SUNW,i2c-pic16f747") == 0 ||
strcmp(compat, "SUNW,bbc-i2c") == 0)
return (1);
return (0);
}
void
pcfiic_ebus_attach(device_t parent, device_t self, void *aux)
{
struct pcfiic_ebus_softc *esc = device_private(self);
struct pcfiic_softc *sc = &esc->esc_sc;
struct ebus_attach_args *ea = aux;
char compat[32];
u_int64_t addr;
u_int8_t clock = PCF8584_CLK_12 | PCF8584_SCL_90;
if (ea->ea_nreg < 1 || ea->ea_nreg > 2) {
printf(": expected 1 or 2 registers, got %d\n", ea->ea_nreg);
return;
}
sc->sc_regmap[PCF8584_S0] = PCF8584_S0;
sc->sc_regmap[PCF8584_S1] = PCF8584_S1;
if ((strcmp(ea->ea_name, "SUNW,envctrl") == 0) ||
(strcmp(ea->ea_name, "SUNW,envctrltwo") == 0))
clock = PCF8584_CLK_12 | PCF8584_SCL_45;
sc->sc_dev = self;
if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 &&
strcmp(compat, "SUNW,bbc-i2c") == 0) {
int clk = prom_getpropint(findroot(), "clock-frequency", 0);
if (clk < 105000000)
clock = PCF8584_CLK_3 | PCF8584_SCL_90;
else if (clk < 160000000)
clock = PCF8584_CLK_4_43 | PCF8584_SCL_90;
sc->sc_regmap[PCF8584_S0] ^= 1;
sc->sc_regmap[PCF8584_S1] ^= 1;
}
if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 &&
strcmp(compat, "SUNW,i2c-pic16f747") == 0) {
sc->sc_delay = 10;
}
if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) {
addr = 0xaa;
} else if (addr == 0x00 || addr > 0xff) {
printf(": invalid address on I2C bus");
return;
}
if (bus_space_map(ea->ea_bustag,
EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
ea->ea_reg[0].size, 0, &sc->sc_ioh) == 0) {
sc->sc_iot = ea->ea_bustag;
} else {
printf(": can't map register space\n");
return;
}
if (ea->ea_nreg == 2) {
if (bus_space_map(sc->sc_iot, EBUS_ADDR_FROM_REG(&ea->ea_reg[1]),
ea->ea_reg[1].size, 0, &sc->sc_mux_ioh) != 0) {
printf(": can't map mux registers\n");
return;
}
sc->sc_has_mux = true;
printf(": iic mux present");
}
if (ea->ea_nintr >= 1)
esc->esc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intr[0],
IPL_BIO, pcfiic_intr, sc);
else
esc->esc_ih = NULL;
if (esc->esc_ih == NULL)
sc->sc_poll = true;
printf("\n");
pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock);
}