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