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