1 /* $NetBSD: pcfiic_ebus.c,v 1.1 2010/02/28 11:49:44 martin 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@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 /* 21 * Device specific driver for the EBus i2c devices found on some sun4u 22 * systems. On systems not having a boot-bus controller the i2c devices 23 * are PCF8584. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/device.h> 29 #include <sys/kernel.h> 30 #include <sys/rwlock.h> 31 32 #include <machine/bus.h> 33 #include <machine/openfirm.h> 34 #include <machine/autoconf.h> 35 36 #include <dev/ebus/ebusreg.h> 37 #include <dev/ebus/ebusvar.h> 38 39 #include <dev/i2c/i2cvar.h> 40 41 #include <dev/ic/pcf8584var.h> 42 43 int pcfiic_ebus_match(device_t, struct cfdata *, void *); 44 void pcfiic_ebus_attach(device_t, device_t, void *); 45 46 struct pcfiic_ebus_softc { 47 struct pcfiic_softc esc_sc; 48 49 int esc_node; 50 void *esc_ih; 51 }; 52 53 CFATTACH_DECL_NEW(pcfiic, sizeof(struct pcfiic_ebus_softc), 54 pcfiic_ebus_match, pcfiic_ebus_attach, NULL, NULL); 55 56 static prop_array_t create_dict(device_t); 57 static void add_prop(prop_array_t, const char *, const char *, u_int, int); 58 static void envctrl_props(prop_array_t, int); 59 static void envctrltwo_props(prop_array_t, int); 60 61 int 62 pcfiic_ebus_match(device_t parent, struct cfdata *match, void *aux) 63 { 64 struct ebus_attach_args *ea = aux; 65 char compat[32]; 66 67 if (strcmp(ea->ea_name, "SUNW,envctrl") == 0 || 68 strcmp(ea->ea_name, "SUNW,envctrltwo") == 0) 69 return (1); 70 71 if (strcmp(ea->ea_name, "i2c") != 0) 72 return (0); 73 74 if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == -1) 75 return (0); 76 77 if (strcmp(compat, "pcf8584") == 0 || 78 strcmp(compat, "i2cpcf,8584") == 0 || 79 strcmp(compat, "SUNW,i2c-pic16f747") == 0 || 80 strcmp(compat, "SUNW,bbc-i2c") == 0) 81 return (1); 82 83 return (0); 84 } 85 86 void 87 pcfiic_ebus_attach(device_t parent, device_t self, void *aux) 88 { 89 struct pcfiic_ebus_softc *esc = device_private(self); 90 struct pcfiic_softc *sc = &esc->esc_sc; 91 struct ebus_attach_args *ea = aux; 92 char compat[32]; 93 u_int64_t addr; 94 u_int8_t clock = PCF_CLOCK_12 | PCF_FREQ_90; 95 int swapregs = 0; 96 97 if (ea->ea_nreg < 1 || ea->ea_nreg > 2) { 98 printf(": expected 1 or 2 registers, got %d\n", ea->ea_nreg); 99 return; 100 } 101 102 sc->sc_dev = self; 103 if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 && 104 strcmp(compat, "SUNW,bbc-i2c") == 0) { 105 /* 106 * On BBC-based machines, Sun swapped the order of 107 * the registers on their clone pcf, plus they feed 108 * it a non-standard clock. 109 */ 110 int clk = prom_getpropint(findroot(), "clock-frequency", 0); 111 112 if (clk < 105000000) 113 clock = PCF_CLOCK_3 | PCF_FREQ_90; 114 else if (clk < 160000000) 115 clock = PCF_CLOCK_4_43 | PCF_FREQ_90; 116 swapregs = 1; 117 } 118 119 if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) { 120 addr = 0xaa; 121 } else if (addr == 0x00 || addr > 0xff) { 122 printf(": invalid address on I2C bus"); 123 return; 124 } 125 126 if (bus_space_map(ea->ea_bustag, 127 EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), 128 ea->ea_reg[0].size, 0, &sc->sc_ioh) == 0) { 129 sc->sc_iot = ea->ea_bustag; 130 } else { 131 printf(": can't map register space\n"); 132 return; 133 } 134 135 if (ea->ea_nreg == 2) { 136 /* 137 * Second register only occurs on BBC-based machines, 138 * and is likely not prom mapped 139 */ 140 if (bus_space_map(sc->sc_iot, EBUS_ADDR_FROM_REG(&ea->ea_reg[1]), 141 ea->ea_reg[1].size, 0, &sc->sc_ioh2) != 0) { 142 printf(": can't map 2nd register space\n"); 143 return; 144 } 145 sc->sc_master = 1; 146 } 147 148 if (ea->ea_nintr >= 1) 149 esc->esc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intr[0], 150 IPL_BIO, pcfiic_intr, sc); 151 else 152 esc->esc_ih = NULL; 153 154 155 if (esc->esc_ih == NULL) 156 sc->sc_poll = 1; 157 158 if (strcmp(ea->ea_name, "SUNW,envctrl") == 0) { 159 envctrl_props(create_dict(self), ea->ea_node); 160 pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0); 161 } else if (strcmp(ea->ea_name, "SUNW,envctrltwo") == 0) { 162 envctrltwo_props(create_dict(self), ea->ea_node); 163 pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0); 164 } else 165 pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock, swapregs); 166 } 167 168 static prop_array_t 169 create_dict(device_t parent) 170 { 171 prop_dictionary_t props = device_properties(parent); 172 prop_array_t cfg = prop_dictionary_get(props, "i2c-child-devices"); 173 if (cfg) return cfg; 174 cfg = prop_array_create(); 175 prop_dictionary_set(props, "i2c-child-devices", cfg); 176 prop_object_release(cfg); 177 return cfg; 178 } 179 180 static void 181 add_prop(prop_array_t c, const char *name, const char *compat, u_int addr, 182 int node) 183 { 184 prop_dictionary_t dev; 185 prop_data_t data; 186 187 dev = prop_dictionary_create(); 188 prop_dictionary_set_cstring(dev, "name", name); 189 data = prop_data_create_data(compat, strlen(compat)+1); 190 prop_dictionary_set(dev, "compatible", data); 191 prop_object_release(data); 192 prop_dictionary_set_uint32(dev, "addr", addr); 193 prop_dictionary_set_uint64(dev, "cookie", node); 194 prop_array_add(c, dev); 195 prop_object_release(dev); 196 } 197 198 static void 199 envctrl_props(prop_array_t c, int node) 200 { 201 /* Power supply 1 temperature. */ 202 add_prop(c, "PSU-1", "ecadc", 0x48, node); 203 204 /* Power supply 2 termperature. */ 205 add_prop(c, "PSU-2", "ecadc", 0x49, node); 206 207 /* Power supply 3 tempterature. */ 208 add_prop(c, "PSU-3", "ecadc", 0x4a, node); 209 210 /* Ambient tempterature. */ 211 add_prop(c, "ambient", "i2c-lm75", 0x4d, node); 212 213 /* CPU temperatures. */ 214 add_prop(c, "CPU", "ecadc", 0x4f, node); 215 } 216 217 static void 218 envctrltwo_props(prop_array_t c, int node) 219 { 220 add_prop(c, "PSU", "ecadc", 0x4a, node); 221 add_prop(c, "CPU", "ecadc", 0x4f, node); 222 } 223