1 /* $NetBSD: pcfiic_ebus.c,v 1.13 2026/09/16 06:40:12 jdc Exp $ */ 2 /* $OpenBSD: pcfiic_ebus.c,v 1.13 2008/06/08 03:07:40 deraadt Exp $ */ 3 4 /* 5 * Copyright (c) 2006 David Gwynne <dlg (at) openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 __KERNEL_RCSID(0, "$NetBSD: pcfiic_ebus.c,v 1.13 2026/09/16 06:40:12 jdc Exp $"); 22 23 /* 24 * Device specific driver for the EBus i2c devices found on some sun4u 25 * systems. On systems not having a boot-bus controller the i2c devices 26 * are PCF8584. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/device.h> 32 #include <sys/kernel.h> 33 #include <sys/rwlock.h> 34 35 #include <sys/bus.h> 36 #include <machine/openfirm.h> 37 #include <machine/autoconf.h> 38 39 #include <dev/ebus/ebusreg.h> 40 #include <dev/ebus/ebusvar.h> 41 42 #include <dev/i2c/i2cvar.h> 43 44 #include <dev/ic/pcf8584var.h> 45 #include <dev/ic/pcf8584reg.h> 46 47 int pcfiic_ebus_match(device_t, struct cfdata *, void *); 48 void pcfiic_ebus_attach(device_t, device_t, void *); 49 50 struct pcfiic_ebus_softc { 51 struct pcfiic_softc esc_sc; 52 53 int esc_node; 54 void *esc_ih; 55 }; 56 57 CFATTACH_DECL_NEW(pcfiic_ebus, sizeof(struct pcfiic_ebus_softc), 58 pcfiic_ebus_match, pcfiic_ebus_attach, NULL, NULL); 59 60 int 61 pcfiic_ebus_match(device_t parent, struct cfdata *match, void *aux) 62 { 63 struct ebus_attach_args *ea = aux; 64 char compat[32]; 65 66 if (strcmp(ea->ea_name, "SUNW,envctrl") == 0 || 67 strcmp(ea->ea_name, "SUNW,envctrltwo") == 0) 68 return (1); 69 70 if (strcmp(ea->ea_name, "i2c") != 0) 71 return (0); 72 73 if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == -1) 74 return (0); 75 76 if (strcmp(compat, "pcf8584") == 0 || 77 strcmp(compat, "i2cpcf,8584") == 0 || 78 strcmp(compat, "SUNW,i2c-pic16f747") == 0 || 79 strcmp(compat, "SUNW,bbc-i2c") == 0) 80 return (1); 81 82 return (0); 83 } 84 85 void 86 pcfiic_ebus_attach(device_t parent, device_t self, void *aux) 87 { 88 struct pcfiic_ebus_softc *esc = device_private(self); 89 struct pcfiic_softc *sc = &esc->esc_sc; 90 struct ebus_attach_args *ea = aux; 91 char compat[32]; 92 u_int64_t addr; 93 u_int8_t clock = PCF8584_CLK_12 | PCF8584_SCL_90; 94 95 if (ea->ea_nreg < 1 || ea->ea_nreg > 2) { 96 printf(": expected 1 or 2 registers, got %d\n", ea->ea_nreg); 97 return; 98 } 99 100 /* 101 * Default to the standard register offsets. 102 */ 103 sc->sc_regmap[PCF8584_S0] = PCF8584_S0; 104 sc->sc_regmap[PCF8584_S1] = PCF8584_S1; 105 106 /* E450 and E250 have a different clock and no interrupts */ 107 if ((strcmp(ea->ea_name, "SUNW,envctrl") == 0) || 108 (strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)) { 109 clock = PCF8584_CLK_12 | PCF8584_SCL_45; 110 sc->sc_poll = true; 111 } 112 113 sc->sc_dev = self; 114 if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 && 115 strcmp(compat, "SUNW,bbc-i2c") == 0) { 116 /* 117 * On BBC-based machines, Sun swapped the order of 118 * the registers on their clone pcf, plus they feed 119 * it a non-standard clock. 120 */ 121 int clk = prom_getpropint(findroot(), "clock-frequency", 0); 122 123 if (clk < 105000000) 124 clock = PCF8584_CLK_3 | PCF8584_SCL_90; 125 else if (clk < 160000000) 126 clock = PCF8584_CLK_4_43 | PCF8584_SCL_90; 127 sc->sc_regmap[PCF8584_S0] ^= 1; 128 sc->sc_regmap[PCF8584_S1] ^= 1; 129 } 130 131 if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 && 132 strcmp(compat, "SUNW,i2c-pic16f747") == 0) { 133 /* U45 needs a delay after reads/writes */ 134 sc->sc_delay = 10; 135 } 136 137 if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) { 138 addr = 0xaa; 139 } else if (addr == 0x00 || addr > 0xff) { 140 printf(": invalid address on I2C bus"); 141 return; 142 } 143 144 if (bus_space_map(ea->ea_bustag, 145 EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), 146 ea->ea_reg[0].size, 0, &sc->sc_ioh) == 0) { 147 sc->sc_iot = ea->ea_bustag; 148 } else { 149 printf(": can't map register space\n"); 150 return; 151 } 152 153 if (ea->ea_nreg == 2) { 154 /* 155 * Second register only occurs on BBC-based machines, 156 * and is likely not prom mapped 157 */ 158 if (bus_space_map(sc->sc_iot, EBUS_ADDR_FROM_REG(&ea->ea_reg[1]), 159 ea->ea_reg[1].size, 0, &sc->sc_mux_ioh) != 0) { 160 printf(": can't map mux registers\n"); 161 return; 162 } 163 sc->sc_has_mux = true; 164 printf(": iic mux present"); 165 } 166 167 if (ea->ea_nintr >= 1) 168 esc->esc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intr[0], 169 IPL_BIO, pcfiic_intr, sc); 170 else 171 esc->esc_ih = NULL; 172 173 174 if (esc->esc_ih == NULL) 175 sc->sc_poll = true; 176 177 printf("\n"); 178 179 pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock); 180 } 181