1 /* $OpenBSD: alipm.c,v 1.18 2024/05/24 06:02:53 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2005 Mark Kettenis
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/rwlock.h>
22 #include <sys/systm.h>
23
24 #include <dev/i2c/i2cvar.h>
25
26 #include <dev/pci/pcidevs.h>
27 #include <dev/pci/pcireg.h>
28 #include <dev/pci/pcivar.h>
29
30 #ifdef __sparc64__
31 #include <arch/sparc64/dev/ofwi2cvar.h>
32 #endif
33
34 /*
35 * Acer Labs M7101 Power register definitions.
36 */
37
38 /* PCI configuration registers. */
39 #define ALIPM_CONF 0xd0 /* general configuration */
40 #define ALIPM_CONF_SMBEN 0x0400 /* enable SMBus */
41 #define ALIPM_BASE 0xe0 /* ACPI and SMBus base address */
42 #define ALIPM_SMB_HOSTC 0xf0 /* host configuration */
43 #define ALIPM_SMB_HOSTC_HSTEN 0x00000001 /* enable host controller */
44 #define ALIPM_SMB_HOSTC_CLOCK 0x00e00000 /* clock speed */
45 #define ALIPM_SMB_HOSTC_149K 0x00000000 /* 149 KHz clock */
46 #define ALIPM_SMB_HOSTC_74K 0x00200000 /* 74 KHz clock */
47 #define ALIPM_SMB_HOSTC_37K 0x00400000 /* 37 KHz clock */
48 #define ALIPM_SMB_HOSTC_223K 0x00800000 /* 223 KHz clock */
49 #define ALIPM_SMB_HOSTC_111K 0x00a00000 /* 111 KHz clock */
50 #define ALIPM_SMB_HOSTC_55K 0x00c00000 /* 55 KHz clock */
51
52 #define ALIPM_SMB_SIZE 32 /* SMBus I/O space size */
53
54 /* SMBus I/O registers */
55 #define ALIPM_SMB_HS 0x00 /* host status */
56 #define ALIPM_SMB_HS_IDLE 0x04
57 #define ALIPM_SMB_HS_BUSY 0x08 /* running a command */
58 #define ALIPM_SMB_HS_DONE 0x10 /* command completed */
59 #define ALIPM_SMB_HS_DEVERR 0x20 /* command error */
60 #define ALIPM_SMB_HS_BUSERR 0x40 /* transaction collision */
61 #define ALIPM_SMB_HS_FAILED 0x80 /* failed bus transaction */
62 #define ALIPM_SMB_HS_BITS \
63 "\020\003IDLE\004BUSY\005DONE\006DEVERR\007BUSERR\010FAILED"
64 #define ALIPM_SMB_HC 0x01 /* host control */
65 #define ALIPM_SMB_HC_KILL 0x04 /* kill command */
66 #define ALIPM_SMB_HC_RESET 0x08 /* reset bus */
67 #define ALIPM_SMB_HC_CMD_QUICK 0x00 /* QUICK command */
68 #define ALIPM_SMB_HC_CMD_BYTE 0x10 /* BYTE command */
69 #define ALIPM_SMB_HC_CMD_BDATA 0x20 /* BYTE DATA command */
70 #define ALIPM_SMB_HC_CMD_WDATA 0x30 /* WORD DATA command */
71 #define ALIPM_SMB_HC_CMD_BLOCK 0x40 /* BLOCK command */
72 #define ALIPM_SMB_START 0x02 /* start command */
73 #define ALIPM_SMB_TXSLVA 0x03 /* transmit slave address */
74 #define ALIPM_SMB_TXSLVA_READ (1 << 0) /* read direction */
75 #define ALIPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
76 #define ALIPM_SMB_HD0 0x04 /* host data 0 */
77 #define ALIPM_SMB_HD1 0x05 /* host data 1 */
78 #define ALIPM_SMB_HBDB 0x06 /* host block data byte */
79 #define ALIPM_SMB_HCMD 0x07 /* host command */
80
81 /*
82 * Newer chips have a more standard, but different PCI configuration
83 * register layout.
84 */
85
86 #define ALIPM_SMB_BASE 0x14 /* SMBus base address */
87 #define ALIPM_SMB_HOSTX 0xe0 /* host configuration */
88
89 #ifdef ALIPM_DEBUG
90 #define DPRINTF(x) printf x
91 #else
92 #define DPRINTF(x)
93 #endif
94
95 #define ALIPM_DELAY 100
96 #define ALIPM_TIMEOUT 1
97
98 struct alipm_softc {
99 struct device sc_dev;
100
101 bus_space_tag_t sc_iot;
102 bus_space_handle_t sc_ioh;
103
104 struct i2c_controller sc_smb_tag;
105 struct rwlock sc_smb_lock;
106 };
107
108 int alipm_match(struct device *, void *, void *);
109 void alipm_attach(struct device *, struct device *, void *);
110
111 int alipm_smb_acquire_bus(void *, int);
112 void alipm_smb_release_bus(void *, int);
113 int alipm_smb_exec(void *, i2c_op_t, i2c_addr_t, const void *,
114 size_t, void *, size_t, int);
115
116 const struct cfattach alipm_ca = {
117 sizeof(struct alipm_softc),
118 alipm_match,
119 alipm_attach
120 };
121
122 struct cfdriver alipm_cd = {
123 NULL, "alipm", DV_DULL
124 };
125
126 int
alipm_match(struct device * parent,void * match,void * aux)127 alipm_match(struct device *parent, void *match, void *aux)
128 {
129 struct pci_attach_args *pa = aux;
130
131 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
132 (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M7101))
133 return (1);
134 return (0);
135 }
136
137 void
alipm_attach(struct device * parent,struct device * self,void * aux)138 alipm_attach(struct device *parent, struct device *self, void *aux)
139 {
140 struct alipm_softc *sc = (struct alipm_softc *) self;
141 struct pci_attach_args *pa = aux;
142 struct i2cbus_attach_args iba;
143 pcireg_t iobase, reg;
144 bus_size_t iosize = ALIPM_SMB_SIZE;
145
146 /* Old chips don't have the PCI 2.2 Capabilities List. */
147 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
148 if ((reg & PCI_STATUS_CAPLIST_SUPPORT) == 0) {
149 /* Map I/O space */
150 iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_BASE);
151 sc->sc_iot = pa->pa_iot;
152 if (iobase == 0 ||
153 bus_space_map(sc->sc_iot, iobase >> 16,
154 iosize, 0, &sc->sc_ioh)) {
155 printf(": can't map i/o space\n");
156 return;
157 }
158
159 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_CONF);
160 if ((reg & ALIPM_CONF_SMBEN) == 0) {
161 printf(": SMBus disabled\n");
162 goto fail;
163 }
164
165 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTC);
166 if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
167 printf(": SMBus host disabled\n");
168 goto fail;
169 }
170 } else {
171 /* Map I/O space */
172 if (pci_mapreg_map(pa, ALIPM_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
173 &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, ALIPM_SMB_SIZE)) {
174 printf(": can't map i/o space\n");
175 return;
176 }
177
178 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTX);
179 if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
180 printf(": SMBus host disabled\n");
181 goto fail;
182 }
183 }
184
185 switch (reg & ALIPM_SMB_HOSTC_CLOCK) {
186 case ALIPM_SMB_HOSTC_149K:
187 printf(": 149KHz clock");
188 break;
189 case ALIPM_SMB_HOSTC_74K:
190 printf(": 74KHz clock");
191 break;
192 case ALIPM_SMB_HOSTC_37K:
193 printf(": 37KHz clock");
194 break;
195 case ALIPM_SMB_HOSTC_223K:
196 printf(": 223KHz clock");
197 break;
198 case ALIPM_SMB_HOSTC_111K:
199 printf(": 111KHz clock");
200 break;
201 case ALIPM_SMB_HOSTC_55K:
202 printf(": 55KHz clock");
203 break;
204 default:
205 printf(" unknown clock speed");
206 break;
207 }
208
209 printf("\n");
210
211 /* Attach I2C bus */
212 rw_init(&sc->sc_smb_lock, "alipm");
213 sc->sc_smb_tag.ic_cookie = sc;
214 sc->sc_smb_tag.ic_acquire_bus = alipm_smb_acquire_bus;
215 sc->sc_smb_tag.ic_release_bus = alipm_smb_release_bus;
216 sc->sc_smb_tag.ic_exec = alipm_smb_exec;
217
218 bzero(&iba, sizeof iba);
219 iba.iba_name = "iic";
220 iba.iba_tag = &sc->sc_smb_tag;
221 #ifdef __sparc64__
222 iba.iba_bus_scan = ofwiic_pci_scan;
223 iba.iba_bus_scan_arg = pa;
224 #endif
225 config_found(&sc->sc_dev, &iba, iicbus_print);
226
227 return;
228
229 fail:
230 bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
231 }
232
233 int
alipm_smb_acquire_bus(void * cookie,int flags)234 alipm_smb_acquire_bus(void *cookie, int flags)
235 {
236 struct alipm_softc *sc = cookie;
237
238 if (flags & I2C_F_POLL)
239 return (0);
240
241 return (rw_enter(&sc->sc_smb_lock, RW_WRITE | RW_INTR));
242 }
243
244 void
alipm_smb_release_bus(void * cookie,int flags)245 alipm_smb_release_bus(void *cookie, int flags)
246 {
247 struct alipm_softc *sc = cookie;
248
249 if (flags & I2C_F_POLL)
250 return;
251
252 rw_exit(&sc->sc_smb_lock);
253 }
254
255 int
alipm_smb_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)256 alipm_smb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
257 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
258 {
259 struct alipm_softc *sc = cookie;
260 u_int8_t *b;
261 u_int8_t ctl, st;
262 int retries, error = 0;
263
264 DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
265 "flags 0x%x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
266 len, flags));
267
268 if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
269 return (EOPNOTSUPP);
270
271 /* Clear status bits */
272 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS,
273 ALIPM_SMB_HS_DONE | ALIPM_SMB_HS_FAILED |
274 ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR);
275 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
276 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
277
278 /* Wait until bus is idle */
279 for (retries = 1000; retries > 0; retries--) {
280 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
281 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
282 BUS_SPACE_BARRIER_READ);
283 if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
284 ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
285 break;
286 DELAY(ALIPM_DELAY);
287 }
288 if (retries == 0) {
289 printf("%s: timeout st 0x%b\n", sc->sc_dev.dv_xname,
290 st, ALIPM_SMB_HS_BITS);
291 return (ETIMEDOUT);
292 }
293 if (st & (ALIPM_SMB_HS_FAILED |
294 ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
295 printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
296 st, ALIPM_SMB_HS_BITS);
297 return (EIO);
298 }
299
300 /* Set slave address and transfer direction. */
301 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_TXSLVA,
302 ALIPM_SMB_TXSLVA_ADDR(addr) |
303 (I2C_OP_READ_P(op) ? ALIPM_SMB_TXSLVA_READ : 0));
304
305 b = (void *)cmdbuf;
306 if (cmdlen > 0)
307 /* Set command byte */
308 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
309 ALIPM_SMB_HCMD, b[0]);
310
311 if (I2C_OP_WRITE_P(op)) {
312 /* Write data. */
313 b = buf;
314 if (len > 0)
315 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
316 ALIPM_SMB_HD0, b[0]);
317 if (len > 1)
318 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
319 ALIPM_SMB_HD1, b[1]);
320 }
321
322 /* Set SMBus command */
323 if (len == 0)
324 ctl = ALIPM_SMB_HC_CMD_BYTE;
325 else if (len == 1)
326 ctl = ALIPM_SMB_HC_CMD_BDATA;
327 else if (len == 2)
328 ctl = ALIPM_SMB_HC_CMD_WDATA;
329 else
330 panic("%s: unexpected len %zd", __func__, len);
331 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC, ctl);
332
333 /* Start transaction */
334 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
335 BUS_SPACE_BARRIER_WRITE);
336 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_START, 0xff);
337 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
338 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
339
340 /* Poll for completion */
341 DELAY(ALIPM_DELAY);
342 for (retries = 1000; retries > 0; retries--) {
343 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
344 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
345 BUS_SPACE_BARRIER_READ);
346 if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
347 ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
348 break;
349 DELAY(ALIPM_DELAY);
350 }
351 if (retries == 0) {
352 printf("%s: timeout st 0x%b, resetting\n",
353 sc->sc_dev.dv_xname, st, ALIPM_SMB_HS_BITS);
354 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
355 ALIPM_SMB_HC_RESET);
356 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
357 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
358 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
359 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
360 BUS_SPACE_BARRIER_READ);
361 error = ETIMEDOUT;
362 goto done;
363 }
364
365 if ((st & ALIPM_SMB_HS_DONE) == 0) {
366 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
367 ALIPM_SMB_HC_KILL);
368 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
369 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
370 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
371 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
372 BUS_SPACE_BARRIER_READ);
373 if ((st & ALIPM_SMB_HS_FAILED) == 0)
374 printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
375 st, ALIPM_SMB_HS_BITS);
376 }
377
378 /* Check for errors */
379 if (st & (ALIPM_SMB_HS_FAILED |
380 ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
381 error = EIO;
382 goto done;
383 }
384
385 if (I2C_OP_READ_P(op)) {
386 /* Read data */
387 b = buf;
388 if (len > 0) {
389 b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
390 ALIPM_SMB_HD0);
391 bus_space_barrier(sc->sc_iot, sc->sc_ioh,
392 ALIPM_SMB_HD0, 1, BUS_SPACE_BARRIER_READ);
393 }
394 if (len > 1) {
395 b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
396 ALIPM_SMB_HD1);
397 bus_space_barrier(sc->sc_iot, sc->sc_ioh,
398 ALIPM_SMB_HD1, 1, BUS_SPACE_BARRIER_READ);
399 }
400 }
401
402 done:
403 /* Clear status bits */
404 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, st);
405
406 return (error);
407 }
408