1*eb7eaf8dSmpi /* $OpenBSD: pcfiic_ebus.c,v 1.15 2021/10/24 17:05:03 mpi Exp $ */
2c74283e0Sdlg
3c74283e0Sdlg /*
4c74283e0Sdlg * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
5c74283e0Sdlg *
6c74283e0Sdlg * Permission to use, copy, modify, and distribute this software for any
7c74283e0Sdlg * purpose with or without fee is hereby granted, provided that the above
8c74283e0Sdlg * copyright notice and this permission notice appear in all copies.
9c74283e0Sdlg *
10c74283e0Sdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c74283e0Sdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c74283e0Sdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c74283e0Sdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c74283e0Sdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c74283e0Sdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c74283e0Sdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c74283e0Sdlg */
18c74283e0Sdlg
19c74283e0Sdlg /*
20c74283e0Sdlg * Device specific driver for the EBus i2c devices found on some sun4u
21c74283e0Sdlg * systems. On systems not having a boot-bus controller the i2c devices
22c74283e0Sdlg * are PCF8584.
23c74283e0Sdlg */
24c74283e0Sdlg
25c74283e0Sdlg #include <sys/param.h>
26c74283e0Sdlg #include <sys/systm.h>
27c74283e0Sdlg #include <sys/device.h>
28c74283e0Sdlg #include <sys/kernel.h>
29ee945c99Sjsg #include <sys/rwlock.h>
30c74283e0Sdlg
31c74283e0Sdlg #include <machine/bus.h>
32c74283e0Sdlg #include <machine/openfirm.h>
333e5c8021Sderaadt #include <machine/autoconf.h>
34c74283e0Sdlg
35c74283e0Sdlg #include <sparc64/dev/ebusreg.h>
36c74283e0Sdlg #include <sparc64/dev/ebusvar.h>
37c74283e0Sdlg
38c74283e0Sdlg #include <dev/i2c/i2cvar.h>
39c74283e0Sdlg #include <sparc64/dev/ofwi2cvar.h>
40c74283e0Sdlg
41c74283e0Sdlg #include <dev/ic/pcf8584var.h>
42c74283e0Sdlg
43c74283e0Sdlg int pcfiic_ebus_match(struct device *, void *, void *);
44c74283e0Sdlg void pcfiic_ebus_attach(struct device *, struct device *, void *);
45c74283e0Sdlg
46c74283e0Sdlg struct pcfiic_ebus_softc {
47c74283e0Sdlg struct pcfiic_softc esc_sc;
48c74283e0Sdlg
49c74283e0Sdlg int esc_node;
50c74283e0Sdlg void *esc_ih;
51c74283e0Sdlg };
52c74283e0Sdlg
53*eb7eaf8dSmpi const struct cfattach pcfiic_ebus_ca = {
54c74283e0Sdlg sizeof(struct pcfiic_ebus_softc), pcfiic_ebus_match, pcfiic_ebus_attach
55c74283e0Sdlg };
56c74283e0Sdlg
57d7f1f1d8Skettenis void envctrl_scan(struct device *, struct i2cbus_attach_args *, void *);
58f620116fSkettenis void envctrltwo_scan(struct device *, struct i2cbus_attach_args *, void *);
59f620116fSkettenis
60c74283e0Sdlg int
pcfiic_ebus_match(struct device * parent,void * match,void * aux)61c74283e0Sdlg pcfiic_ebus_match(struct device *parent, void *match, void *aux)
62c74283e0Sdlg {
63c74283e0Sdlg struct ebus_attach_args *ea = aux;
64c74283e0Sdlg char compat[32];
65c74283e0Sdlg
66d7f1f1d8Skettenis if (strcmp(ea->ea_name, "SUNW,envctrl") == 0 ||
67d7f1f1d8Skettenis strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)
68f620116fSkettenis return (1);
69f620116fSkettenis
70c74283e0Sdlg if (strcmp(ea->ea_name, "i2c") != 0)
71c74283e0Sdlg return (0);
72c74283e0Sdlg
73c74283e0Sdlg if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == -1)
74c74283e0Sdlg return (0);
75c74283e0Sdlg
764cda44c6Skettenis if (strcmp(compat, "pcf8584") == 0 ||
774cda44c6Skettenis strcmp(compat, "i2cpcf,8584") == 0 ||
78d8858115Skettenis strcmp(compat, "SUNW,i2c-pic16f747") == 0 ||
79f620116fSkettenis strcmp(compat, "SUNW,bbc-i2c") == 0)
80c74283e0Sdlg return (1);
819c6a5656Sderaadt
829c6a5656Sderaadt return (0);
83c74283e0Sdlg }
84c74283e0Sdlg
85c74283e0Sdlg void
pcfiic_ebus_attach(struct device * parent,struct device * self,void * aux)86c74283e0Sdlg pcfiic_ebus_attach(struct device *parent, struct device *self, void *aux)
87c74283e0Sdlg {
88c74283e0Sdlg struct pcfiic_ebus_softc *esc = (struct pcfiic_ebus_softc *)self;
89c74283e0Sdlg struct pcfiic_softc *sc = &esc->esc_sc;
90c74283e0Sdlg struct ebus_attach_args *ea = aux;
913e5c8021Sderaadt char compat[32];
92c74283e0Sdlg u_int64_t addr;
936c4a4b0dSderaadt u_int8_t clock = PCF_CLOCK_12 | PCF_FREQ_90;
943e5c8021Sderaadt int swapregs = 0;
95c74283e0Sdlg
969c6a5656Sderaadt if (ea->ea_nregs < 1 || ea->ea_nregs > 2) {
979c6a5656Sderaadt printf(": expected 1 or 2 registers, got %d\n", ea->ea_nregs);
98c74283e0Sdlg return;
99c74283e0Sdlg }
100c74283e0Sdlg
101d7f1f1d8Skettenis if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 &&
102d7f1f1d8Skettenis strcmp(compat, "SUNW,bbc-i2c") == 0) {
1033e5c8021Sderaadt /*
1043e5c8021Sderaadt * On BBC-based machines, Sun swapped the order of
1053e5c8021Sderaadt * the registers on their clone pcf, plus they feed
1063e5c8021Sderaadt * it a non-standard clock.
1073e5c8021Sderaadt */
1083e5c8021Sderaadt int clk = getpropint(findroot(), "clock-frequency", 0);
1093e5c8021Sderaadt
1103e5c8021Sderaadt if (clk < 105000000)
1116c4a4b0dSderaadt clock = PCF_CLOCK_3 | PCF_FREQ_90;
1123e5c8021Sderaadt else if (clk < 160000000)
1136c4a4b0dSderaadt clock = PCF_CLOCK_4_43 | PCF_FREQ_90;
1143e5c8021Sderaadt swapregs = 1;
1153e5c8021Sderaadt }
1163e5c8021Sderaadt
117c74283e0Sdlg if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) {
1189c6a5656Sderaadt addr = 0xaa;
1199c6a5656Sderaadt } else if (addr == 0x00 || addr > 0xff) {
1209c6a5656Sderaadt printf(": invalid address on I2C bus");
121c74283e0Sdlg return;
122c74283e0Sdlg }
123c74283e0Sdlg
124392880e3Sderaadt /* Prefer prom mapping, then memory mapping, then io mapping */
125392880e3Sderaadt if (ea->ea_nvaddrs) {
126392880e3Sderaadt if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
127392880e3Sderaadt BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) != 0)
128392880e3Sderaadt goto fail;
129392880e3Sderaadt sc->sc_iot = ea->ea_memtag;
130392880e3Sderaadt } else if (ebus_bus_map(ea->ea_memtag, 0,
131392880e3Sderaadt EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
132392880e3Sderaadt ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
133392880e3Sderaadt sc->sc_iot = ea->ea_memtag;
134392880e3Sderaadt } else if (ebus_bus_map(ea->ea_iotag, 0,
135392880e3Sderaadt EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
136392880e3Sderaadt ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
137392880e3Sderaadt sc->sc_iot = ea->ea_iotag;
138392880e3Sderaadt } else {
139392880e3Sderaadt fail:
140c74283e0Sdlg printf(": can't map register space\n");
141c74283e0Sdlg return;
142c74283e0Sdlg }
143c74283e0Sdlg
1449c6a5656Sderaadt if (ea->ea_nregs == 2) {
145392880e3Sderaadt /*
146392880e3Sderaadt * Second register only occurs on BBC-based machines,
147392880e3Sderaadt * and is likely not prom mapped
148392880e3Sderaadt */
1499c6a5656Sderaadt if (ebus_bus_map(sc->sc_iot, 0, EBUS_PADDR_FROM_REG(&ea->ea_regs[1]),
1509c6a5656Sderaadt ea->ea_regs[1].size, 0, 0, &sc->sc_ioh2) != 0) {
1519c6a5656Sderaadt printf(": can't map 2nd register space\n");
1529c6a5656Sderaadt return;
1539c6a5656Sderaadt }
1549c6a5656Sderaadt sc->sc_master = 1;
1559c6a5656Sderaadt }
1569c6a5656Sderaadt
157c74283e0Sdlg if (ea->ea_nintrs >= 1)
158c74283e0Sdlg esc->esc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
159c74283e0Sdlg IPL_BIO, 0, pcfiic_intr, sc, self->dv_xname);
160c74283e0Sdlg else
161c74283e0Sdlg esc->esc_ih = NULL;
162c74283e0Sdlg
163c74283e0Sdlg
164c74283e0Sdlg if (esc->esc_ih == NULL)
165c74283e0Sdlg sc->sc_poll = 1;
166c74283e0Sdlg
167d7f1f1d8Skettenis if (strcmp(ea->ea_name, "SUNW,envctrl") == 0)
168d7f1f1d8Skettenis pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0,
169d7f1f1d8Skettenis envctrl_scan, &ea->ea_node);
170d7f1f1d8Skettenis else if (strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)
171f620116fSkettenis pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0,
172f620116fSkettenis envctrltwo_scan, &ea->ea_node);
173f620116fSkettenis else
1743e5c8021Sderaadt pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock, swapregs,
1753e5c8021Sderaadt ofwiic_scan, &ea->ea_node);
176c74283e0Sdlg }
177f620116fSkettenis
178f620116fSkettenis void
envctrl_scan(struct device * self,struct i2cbus_attach_args * iba,void * aux)179d7f1f1d8Skettenis envctrl_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
180d7f1f1d8Skettenis {
181d7f1f1d8Skettenis extern int iic_print(void *, const char *);
182d7f1f1d8Skettenis struct i2c_attach_args ia;
183d7f1f1d8Skettenis
184d7f1f1d8Skettenis memset(&ia, 0, sizeof(ia));
185d7f1f1d8Skettenis ia.ia_tag = iba->iba_tag;
186d7f1f1d8Skettenis ia.ia_cookie = aux;
187d7f1f1d8Skettenis
188d7f1f1d8Skettenis /* Power supply 1 temperature. */
189d7f1f1d8Skettenis ia.ia_addr = 0x48;
190d7f1f1d8Skettenis ia.ia_name = "ecadc";
191d7f1f1d8Skettenis config_found(self, &ia, iic_print);
192d7f1f1d8Skettenis
19336fd90dcSjsg /* Power supply 2 temperature. */
194d7f1f1d8Skettenis ia.ia_addr = 0x49;
195d7f1f1d8Skettenis ia.ia_name = "ecadc";
196d7f1f1d8Skettenis config_found(self, &ia, iic_print);
197d7f1f1d8Skettenis
19836fd90dcSjsg /* Power supply 3 temperature. */
199d7f1f1d8Skettenis ia.ia_addr = 0x4a;
200d7f1f1d8Skettenis ia.ia_name = "ecadc";
201d7f1f1d8Skettenis config_found(self, &ia, iic_print);
202d7f1f1d8Skettenis
20336fd90dcSjsg /* Ambient temperature. */
204d7f1f1d8Skettenis ia.ia_addr = 0x4d;
205d7f1f1d8Skettenis ia.ia_name = "lm75";
206d7f1f1d8Skettenis config_found(self, &ia, iic_print);
207d7f1f1d8Skettenis
208d7f1f1d8Skettenis /* CPU temperatures. */
209d7f1f1d8Skettenis ia.ia_addr = 0x4f;
210d7f1f1d8Skettenis ia.ia_name = "ecadc";
211d7f1f1d8Skettenis config_found(self, &ia, iic_print);
212d7f1f1d8Skettenis }
213d7f1f1d8Skettenis
214d7f1f1d8Skettenis void
envctrltwo_scan(struct device * self,struct i2cbus_attach_args * iba,void * aux)215f620116fSkettenis envctrltwo_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
216f620116fSkettenis {
217f620116fSkettenis extern int iic_print(void *, const char *);
218f620116fSkettenis struct i2c_attach_args ia;
219f620116fSkettenis
220f620116fSkettenis memset(&ia, 0, sizeof(ia));
221f620116fSkettenis ia.ia_tag = iba->iba_tag;
222f620116fSkettenis ia.ia_cookie = aux;
223f620116fSkettenis
224f620116fSkettenis ia.ia_addr = 0x4a;
225f620116fSkettenis ia.ia_name = "ecadc";
226f620116fSkettenis config_found(self, &ia, iic_print);
227f620116fSkettenis
228f620116fSkettenis ia.ia_addr = 0x4f;
229f620116fSkettenis ia.ia_name = "ecadc";
230f620116fSkettenis config_found(self, &ia, iic_print);
231f620116fSkettenis }
232