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