xref: /netbsd/sys/arch/sparc64/dev/pcfiic_ebus.c (revision 6550d01e)
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