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
98struct 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
108int	alipm_match(struct device *, void *, void *);
109void	alipm_attach(struct device *, struct device *, void *);
110
111int	alipm_smb_acquire_bus(void *, int);
112void	alipm_smb_release_bus(void *, int);
113int	alipm_smb_exec(void *, i2c_op_t, i2c_addr_t, const void *,
114	    size_t, void *, size_t, int);
115
116const struct cfattach alipm_ca = {
117	sizeof(struct alipm_softc),
118	alipm_match,
119	alipm_attach
120};
121
122struct cfdriver alipm_cd = {
123	NULL, "alipm", DV_DULL
124};
125
126int
127alipm_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
137void
138alipm_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
229fail:
230	bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
231}
232
233int
234alipm_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
244void
245alipm_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
255int
256alipm_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
402done:
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