1221961Sbrix/*-
2221961Sbrix * Copyright (c) 2011 Henrik Brix Andersen <brix@FreeBSD.org>
3221961Sbrix * All rights reserved.
4221961Sbrix *
5221961Sbrix * Redistribution and use in source and binary forms, with or without
6221961Sbrix * modification, are permitted provided that the following conditions
7221961Sbrix * are met:
8221961Sbrix * 1. Redistributions of source code must retain the above copyright
9221961Sbrix *    notice, this list of conditions and the following disclaimer.
10221961Sbrix * 2. Redistributions in binary form must reproduce the above copyright
11221961Sbrix *    notice, this list of conditions and the following disclaimer in the
12221961Sbrix *    documentation and/or other materials provided with the distribution.
13221961Sbrix *
14221961Sbrix * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15221961Sbrix * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16221961Sbrix * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17221961Sbrix * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18221961Sbrix * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19221961Sbrix * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20221961Sbrix * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21221961Sbrix * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22221961Sbrix * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23221961Sbrix * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24221961Sbrix */
25221961Sbrix
26221961Sbrix#include <sys/cdefs.h>
27221961Sbrix__FBSDID("$FreeBSD$");
28221961Sbrix/*
29221961Sbrix * AMD Geode LX CS5536 System Management Bus controller.
30221961Sbrix *
31221961Sbrix * Although AMD refers to this device as an SMBus controller, it
32221961Sbrix * really is an I2C controller (It lacks SMBus ALERT# and Alert
33221961Sbrix * Response support).
34221961Sbrix *
35221961Sbrix * The driver is implemented as an interrupt-driven state machine,
36221961Sbrix * supporting both master and slave mode.
37221961Sbrix */
38221961Sbrix#include <sys/param.h>
39221961Sbrix#include <sys/systm.h>
40221961Sbrix#include <sys/bus.h>
41221961Sbrix#include <sys/kernel.h>
42221961Sbrix#include <sys/module.h>
43221961Sbrix#include <sys/lock.h>
44221961Sbrix#include <sys/mutex.h>
45221961Sbrix#include <sys/sysctl.h>
46221961Sbrix#ifdef GLXIIC_DEBUG
47221961Sbrix#include <sys/syslog.h>
48221961Sbrix#endif
49221961Sbrix
50221961Sbrix#include <dev/pci/pcireg.h>
51221961Sbrix#include <dev/pci/pcivar.h>
52221961Sbrix
53221961Sbrix#include <machine/bus.h>
54221961Sbrix#include <sys/rman.h>
55221961Sbrix#include <machine/resource.h>
56221961Sbrix
57221961Sbrix#include <dev/iicbus/iiconf.h>
58221961Sbrix#include <dev/iicbus/iicbus.h>
59221961Sbrix
60221961Sbrix#include "iicbus_if.h"
61221961Sbrix
62221961Sbrix/* CS5536 PCI-ISA ID. */
63221961Sbrix#define	GLXIIC_CS5536_DEV_ID		0x20901022
64221961Sbrix
65221961Sbrix/* MSRs. */
66221961Sbrix#define	GLXIIC_MSR_PIC_YSEL_HIGH	0x51400021
67221961Sbrix
68221961Sbrix/* Bus speeds. */
69221961Sbrix#define	GLXIIC_SLOW	0x0258	/*  10 kHz. */
70221961Sbrix#define	GLXIIC_FAST	0x0078	/*  50 kHz. */
71221961Sbrix#define	GLXIIC_FASTEST	0x003c	/* 100 kHz. */
72221961Sbrix
73221961Sbrix/* Default bus activity timeout in milliseconds. */
74221961Sbrix#define GLXIIC_DEFAULT_TIMEOUT	35
75221961Sbrix
76221961Sbrix/* GPIO register offsets. */
77221961Sbrix#define	GLXIIC_GPIOL_OUT_AUX1_SEL	0x10
78221961Sbrix#define	GLXIIC_GPIOL_IN_AUX1_SEL	0x34
79221961Sbrix
80221961Sbrix/* GPIO 14 (SMB_CLK) and 15 (SMB_DATA) bitmasks. */
81221961Sbrix#define	GLXIIC_GPIO_14_15_ENABLE	0x0000c000
82221961Sbrix#define	GLXIIC_GPIO_14_15_DISABLE	0xc0000000
83221961Sbrix
84221961Sbrix/* SMB register offsets. */
85221961Sbrix#define	GLXIIC_SMB_SDA				0x00
86221961Sbrix#define	GLXIIC_SMB_STS				0x01
87221961Sbrix#define		GLXIIC_SMB_STS_SLVSTP_BIT	(1 << 7)
88221961Sbrix#define		GLXIIC_SMB_STS_SDAST_BIT	(1 << 6)
89221961Sbrix#define		GLXIIC_SMB_STS_BER_BIT		(1 << 5)
90221961Sbrix#define		GLXIIC_SMB_STS_NEGACK_BIT	(1 << 4)
91221961Sbrix#define		GLXIIC_SMB_STS_STASTR_BIT	(1 << 3)
92221961Sbrix#define		GLXIIC_SMB_STS_NMATCH_BIT	(1 << 2)
93221961Sbrix#define		GLXIIC_SMB_STS_MASTER_BIT	(1 << 1)
94221961Sbrix#define		GLXIIC_SMB_STS_XMIT_BIT		(1 << 0)
95221961Sbrix#define	GLXIIC_SMB_CTRL_STS			0x02
96221961Sbrix#define		GLXIIC_SMB_CTRL_STS_TGSCL_BIT	(1 << 5)
97221961Sbrix#define		GLXIIC_SMB_CTRL_STS_TSDA_BIT	(1 << 4)
98221961Sbrix#define		GLXIIC_SMB_CTRL_STS_GCMTCH_BIT	(1 << 3)
99221961Sbrix#define		GLXIIC_SMB_CTRL_STS_MATCH_BIT	(1 << 2)
100221961Sbrix#define		GLXIIC_SMB_CTRL_STS_BB_BIT	(1 << 1)
101221961Sbrix#define		GLXIIC_SMB_CTRL_STS_BUSY_BIT	(1 << 0)
102221961Sbrix#define	GLXIIC_SMB_CTRL1			0x03
103221961Sbrix#define		GLXIIC_SMB_CTRL1_STASTRE_BIT	(1 << 7)
104221961Sbrix#define		GLXIIC_SMB_CTRL1_NMINTE_BIT	(1 << 6)
105221961Sbrix#define		GLXIIC_SMB_CTRL1_GCMEN_BIT	(1 << 5)
106221961Sbrix#define		GLXIIC_SMB_CTRL1_ACK_BIT	(1 << 4)
107221961Sbrix#define		GLXIIC_SMB_CTRL1_INTEN_BIT	(1 << 2)
108221961Sbrix#define		GLXIIC_SMB_CTRL1_STOP_BIT	(1 << 1)
109221961Sbrix#define		GLXIIC_SMB_CTRL1_START_BIT	(1 << 0)
110221961Sbrix#define	GLXIIC_SMB_ADDR				0x04
111221961Sbrix#define		GLXIIC_SMB_ADDR_SAEN_BIT	(1 << 7)
112221961Sbrix#define	GLXIIC_SMB_CTRL2			0x05
113221961Sbrix#define		GLXIIC_SMB_CTRL2_EN_BIT		(1 << 0)
114221961Sbrix#define	GLXIIC_SMB_CTRL3			0x06
115221961Sbrix
116221961Sbrixtypedef enum {
117221961Sbrix	GLXIIC_STATE_IDLE,
118221961Sbrix	GLXIIC_STATE_SLAVE_TX,
119221961Sbrix	GLXIIC_STATE_SLAVE_RX,
120221961Sbrix	GLXIIC_STATE_MASTER_ADDR,
121221961Sbrix	GLXIIC_STATE_MASTER_TX,
122221961Sbrix	GLXIIC_STATE_MASTER_RX,
123221961Sbrix	GLXIIC_STATE_MASTER_STOP,
124221961Sbrix	GLXIIC_STATE_MAX,
125221961Sbrix} glxiic_state_t;
126221961Sbrix
127221961Sbrixstruct glxiic_softc {
128221961Sbrix	device_t	 dev;		/* Myself. */
129221961Sbrix	device_t	 iicbus;	/* IIC bus. */
130221961Sbrix	struct mtx	 mtx;		/* Lock. */
131221961Sbrix	glxiic_state_t	 state;		/* Driver state. */
132221961Sbrix	struct callout	 callout;	/* Driver state timeout callout. */
133221961Sbrix	int		 timeout;	/* Driver state timeout (ms). */
134221961Sbrix
135221961Sbrix	int		 smb_rid;	/* SMB controller resource ID. */
136221961Sbrix	struct resource *smb_res;	/* SMB controller resource. */
137221961Sbrix	int		 gpio_rid;	/* GPIO resource ID. */
138221961Sbrix	struct resource *gpio_res;	/* GPIO resource. */
139221961Sbrix
140221961Sbrix	int		 irq_rid;	/* IRQ resource ID. */
141221961Sbrix	struct resource *irq_res;	/* IRQ resource. */
142221961Sbrix	void		*irq_handler;	/* IRQ handler cookie. */
143221961Sbrix	int		 old_irq;	/* IRQ mapped by board firmware. */
144221961Sbrix
145221961Sbrix	struct iic_msg	*msg;		/* Current master mode message. */
146221961Sbrix	uint32_t	 nmsgs;		/* Number of messages remaining. */
147221961Sbrix	uint8_t		*data;		/* Current master mode data byte. */
148221961Sbrix	uint16_t	 ndata;		/* Number of data bytes remaining. */
149221961Sbrix	int		 error;		/* Last master mode error. */
150221961Sbrix
151221961Sbrix	uint8_t		 addr;		/* Own address. */
152221961Sbrix	uint16_t	 sclfrq;	/* Bus frequency. */
153221961Sbrix};
154221961Sbrix
155221961Sbrix#ifdef GLXIIC_DEBUG
156221971Sbrix#define GLXIIC_DEBUG_LOG(fmt, args...)	\
157221971Sbrix	log(LOG_DEBUG, "%s: " fmt "\n" , __func__ , ## args)
158221961Sbrix#else
159221971Sbrix#define GLXIIC_DEBUG_LOG(fmt, args...)
160221961Sbrix#endif
161221961Sbrix
162221961Sbrix#define	GLXIIC_SCLFRQ(n)		((n << 1))
163221961Sbrix#define	GLXIIC_SMBADDR(n)		((n >> 1))
164221961Sbrix#define	GLXIIC_SMB_IRQ_TO_MAP(n)	((n << 16))
165221961Sbrix#define	GLXIIC_MAP_TO_SMB_IRQ(n)	((n >> 16) & 0xf)
166221961Sbrix
167221961Sbrix#define	GLXIIC_LOCK(_sc)		mtx_lock(&_sc->mtx)
168221961Sbrix#define	GLXIIC_UNLOCK(_sc)		mtx_unlock(&_sc->mtx)
169221961Sbrix#define	GLXIIC_LOCK_INIT(_sc)		\
170221961Sbrix	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "glxiic", MTX_DEF)
171221961Sbrix#define	GLXIIC_SLEEP(_sc)		\
172221961Sbrix	mtx_sleep(_sc, &_sc->mtx, IICPRI, "glxiic", 0)
173221961Sbrix#define	GLXIIC_WAKEUP(_sc)		wakeup(_sc);
174221961Sbrix#define	GLXIIC_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
175221961Sbrix#define	GLXIIC_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
176221961Sbrix
177221961Sbrixtypedef	int (glxiic_state_callback_t)(struct glxiic_softc *sc,
178221961Sbrix    uint8_t status);
179221961Sbrix
180221961Sbrixstatic glxiic_state_callback_t	glxiic_state_idle_callback;
181221961Sbrixstatic glxiic_state_callback_t	glxiic_state_slave_tx_callback;
182221961Sbrixstatic glxiic_state_callback_t	glxiic_state_slave_rx_callback;
183221961Sbrixstatic glxiic_state_callback_t	glxiic_state_master_addr_callback;
184221961Sbrixstatic glxiic_state_callback_t	glxiic_state_master_tx_callback;
185221961Sbrixstatic glxiic_state_callback_t	glxiic_state_master_rx_callback;
186221961Sbrixstatic glxiic_state_callback_t	glxiic_state_master_stop_callback;
187221961Sbrix
188221961Sbrixstruct glxiic_state_table_entry {
189221961Sbrix	glxiic_state_callback_t *callback;
190221961Sbrix	boolean_t master;
191221961Sbrix};
192221961Sbrixtypedef struct glxiic_state_table_entry glxiic_state_table_entry_t;
193221961Sbrix
194221961Sbrixstatic glxiic_state_table_entry_t glxiic_state_table[GLXIIC_STATE_MAX] = {
195221961Sbrix	[GLXIIC_STATE_IDLE] = {
196221961Sbrix		.callback = &glxiic_state_idle_callback,
197221961Sbrix		.master = FALSE,
198221961Sbrix	},
199221961Sbrix
200221961Sbrix	[GLXIIC_STATE_SLAVE_TX] = {
201221961Sbrix		.callback = &glxiic_state_slave_tx_callback,
202221961Sbrix		.master = FALSE,
203221961Sbrix	},
204221961Sbrix
205221961Sbrix	[GLXIIC_STATE_SLAVE_RX] = {
206221961Sbrix		.callback = &glxiic_state_slave_rx_callback,
207221961Sbrix		.master = FALSE,
208221961Sbrix	},
209221961Sbrix
210221961Sbrix	[GLXIIC_STATE_MASTER_ADDR] = {
211221961Sbrix		.callback = &glxiic_state_master_addr_callback,
212221961Sbrix		.master = TRUE,
213221961Sbrix	},
214221961Sbrix
215221961Sbrix	[GLXIIC_STATE_MASTER_TX] = {
216221961Sbrix		.callback = &glxiic_state_master_tx_callback,
217221961Sbrix		.master = TRUE,
218221961Sbrix	},
219221961Sbrix
220221961Sbrix	[GLXIIC_STATE_MASTER_RX] = {
221221961Sbrix		.callback = &glxiic_state_master_rx_callback,
222221961Sbrix		.master = TRUE,
223221961Sbrix	},
224221961Sbrix
225221961Sbrix	[GLXIIC_STATE_MASTER_STOP] = {
226221961Sbrix		.callback = &glxiic_state_master_stop_callback,
227221961Sbrix		.master = TRUE,
228221961Sbrix	},
229221961Sbrix};
230221961Sbrix
231221961Sbrixstatic void	glxiic_identify(driver_t *driver, device_t parent);
232221961Sbrixstatic int	glxiic_probe(device_t dev);
233221961Sbrixstatic int	glxiic_attach(device_t dev);
234221961Sbrixstatic int	glxiic_detach(device_t dev);
235221961Sbrix
236221961Sbrixstatic uint8_t	glxiic_read_status_locked(struct glxiic_softc *sc);
237221961Sbrixstatic void	glxiic_stop_locked(struct glxiic_softc *sc);
238221961Sbrixstatic void	glxiic_timeout(void *arg);
239221961Sbrixstatic void	glxiic_start_timeout_locked(struct glxiic_softc *sc);
240221961Sbrixstatic void	glxiic_set_state_locked(struct glxiic_softc *sc,
241221961Sbrix    glxiic_state_t state);
242221961Sbrixstatic int	glxiic_handle_slave_match_locked(struct glxiic_softc *sc,
243221961Sbrix    uint8_t status);
244221961Sbrixstatic void	glxiic_intr(void *arg);
245221961Sbrix
246221961Sbrixstatic int	glxiic_reset(device_t dev, u_char speed, u_char addr,
247221961Sbrix    u_char *oldaddr);
248221961Sbrixstatic int	glxiic_transfer(device_t dev, struct iic_msg *msgs,
249221961Sbrix    uint32_t nmsgs);
250221961Sbrix
251221961Sbrixstatic void	glxiic_smb_map_interrupt(int irq);
252221961Sbrixstatic void 	glxiic_gpio_enable(struct glxiic_softc *sc);
253221961Sbrixstatic void 	glxiic_gpio_disable(struct glxiic_softc *sc);
254221961Sbrixstatic void	glxiic_smb_enable(struct glxiic_softc *sc, uint8_t speed,
255221961Sbrix    uint8_t addr);
256221961Sbrixstatic void	glxiic_smb_disable(struct glxiic_softc *sc);
257221961Sbrix
258221961Sbrixstatic device_method_t glxiic_methods[] = {
259221961Sbrix	DEVMETHOD(device_identify,	glxiic_identify),
260221961Sbrix	DEVMETHOD(device_probe,		glxiic_probe),
261221961Sbrix	DEVMETHOD(device_attach,	glxiic_attach),
262221961Sbrix	DEVMETHOD(device_detach,	glxiic_detach),
263221961Sbrix
264221961Sbrix	DEVMETHOD(iicbus_reset,		glxiic_reset),
265221961Sbrix	DEVMETHOD(iicbus_transfer,	glxiic_transfer),
266221961Sbrix	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
267221961Sbrix
268221961Sbrix	{ 0, 0 }
269221961Sbrix};
270221961Sbrix
271221961Sbrixstatic driver_t glxiic_driver = {
272221961Sbrix	"glxiic",
273221961Sbrix	glxiic_methods,
274221961Sbrix	sizeof(struct glxiic_softc),
275221961Sbrix};
276221961Sbrix
277221961Sbrixstatic devclass_t glxiic_devclass;
278221961Sbrix
279221961SbrixDRIVER_MODULE(glxiic, isab, glxiic_driver, glxiic_devclass, 0, 0);
280221961SbrixDRIVER_MODULE(iicbus, glxiic, iicbus_driver, iicbus_devclass, 0, 0);
281221961SbrixMODULE_DEPEND(glxiic, iicbus, 1, 1, 1);
282221961Sbrix
283221961Sbrixstatic void
284221961Sbrixglxiic_identify(driver_t *driver, device_t parent)
285221961Sbrix{
286221961Sbrix
287221961Sbrix	/* Prevent child from being added more than once. */
288221961Sbrix	if (device_find_child(parent, driver->name, -1) != NULL)
289221961Sbrix		return;
290221961Sbrix
291221961Sbrix	if (pci_get_devid(parent) == GLXIIC_CS5536_DEV_ID) {
292221961Sbrix		if (device_add_child(parent, driver->name, -1) == NULL)
293221961Sbrix			device_printf(parent, "Could not add glxiic child\n");
294221961Sbrix	}
295221961Sbrix}
296221961Sbrix
297221961Sbrixstatic int
298221961Sbrixglxiic_probe(device_t dev)
299221961Sbrix{
300221961Sbrix
301241885Seadler	if (resource_disabled("glxiic", device_get_unit(dev)))
302241885Seadler		return (ENXIO);
303241885Seadler
304221961Sbrix	device_set_desc(dev, "AMD Geode CS5536 SMBus controller");
305221961Sbrix
306221961Sbrix	return (BUS_PROBE_DEFAULT);
307221961Sbrix}
308221961Sbrix
309221961Sbrixstatic int
310221961Sbrixglxiic_attach(device_t dev)
311221961Sbrix{
312221961Sbrix	struct glxiic_softc *sc;
313221961Sbrix	struct sysctl_ctx_list *ctx;
314221961Sbrix	struct sysctl_oid *tree;
315221961Sbrix	int error, irq, unit;
316221961Sbrix	uint32_t irq_map;
317221961Sbrix	char tn[32];
318221961Sbrix
319221961Sbrix	sc = device_get_softc(dev);
320221961Sbrix	sc->dev = dev;
321221961Sbrix	sc->state = GLXIIC_STATE_IDLE;
322221961Sbrix	error = 0;
323221961Sbrix
324221961Sbrix	GLXIIC_LOCK_INIT(sc);
325221961Sbrix	callout_init_mtx(&sc->callout, &sc->mtx, 0);
326221961Sbrix
327221961Sbrix	sc->smb_rid = PCIR_BAR(0);
328221961Sbrix	sc->smb_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->smb_rid,
329221961Sbrix	    RF_ACTIVE);
330221961Sbrix	if (sc->smb_res == NULL) {
331221961Sbrix		device_printf(dev, "Could not allocate SMBus I/O port\n");
332221961Sbrix		error = ENXIO;
333221961Sbrix		goto out;
334221961Sbrix	}
335221961Sbrix
336221961Sbrix	sc->gpio_rid = PCIR_BAR(1);
337221961Sbrix	sc->gpio_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
338221961Sbrix	    &sc->gpio_rid, RF_SHAREABLE | RF_ACTIVE);
339221961Sbrix	if (sc->gpio_res == NULL) {
340221961Sbrix		device_printf(dev, "Could not allocate GPIO I/O port\n");
341221961Sbrix		error = ENXIO;
342221961Sbrix		goto out;
343221961Sbrix	}
344221961Sbrix
345221961Sbrix	/* Ensure the controller is not enabled by firmware. */
346221961Sbrix	glxiic_smb_disable(sc);
347221961Sbrix
348221961Sbrix	/* Read the existing IRQ map. */
349221961Sbrix	irq_map = rdmsr(GLXIIC_MSR_PIC_YSEL_HIGH);
350221961Sbrix	sc->old_irq = GLXIIC_MAP_TO_SMB_IRQ(irq_map);
351221961Sbrix
352221961Sbrix	unit = device_get_unit(dev);
353221961Sbrix	if (resource_int_value("glxiic", unit, "irq", &irq) == 0) {
354221961Sbrix		if (irq < 1 || irq > 15) {
355221961Sbrix			device_printf(dev, "Bad value %d for glxiic.%d.irq\n",
356221961Sbrix			    irq, unit);
357221961Sbrix			error = ENXIO;
358221961Sbrix			goto out;
359221961Sbrix		}
360221961Sbrix
361221961Sbrix		if (bootverbose)
362221961Sbrix			device_printf(dev, "Using irq %d set by hint\n", irq);
363221961Sbrix	} else if (sc->old_irq != 0) {
364221961Sbrix		if (bootverbose)
365221961Sbrix			device_printf(dev, "Using irq %d set by firmware\n",
366221961Sbrix			    irq);
367221961Sbrix		irq = sc->old_irq;
368221961Sbrix	} else {
369221961Sbrix		device_printf(dev, "No irq mapped by firmware");
370221961Sbrix		printf(" and no glxiic.%d.irq hint provided\n", unit);
371221961Sbrix		error = ENXIO;
372221961Sbrix		goto out;
373221961Sbrix	}
374221961Sbrix
375221961Sbrix	/* Map the SMBus interrupt to the requested legacy IRQ. */
376221961Sbrix	glxiic_smb_map_interrupt(irq);
377221961Sbrix
378221961Sbrix	sc->irq_rid = 0;
379221961Sbrix	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
380221961Sbrix	    irq, irq, 1, RF_SHAREABLE | RF_ACTIVE);
381221961Sbrix	if (sc->irq_res == NULL) {
382221961Sbrix		device_printf(dev, "Could not allocate IRQ %d\n", irq);
383221961Sbrix		error = ENXIO;
384221961Sbrix		goto out;
385221961Sbrix	}
386221961Sbrix
387221961Sbrix	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
388221961Sbrix	    NULL, glxiic_intr, sc, &(sc->irq_handler));
389221961Sbrix	if (error != 0) {
390221961Sbrix		device_printf(dev, "Could not setup IRQ handler\n");
391221961Sbrix		error = ENXIO;
392221961Sbrix		goto out;
393221961Sbrix	}
394221961Sbrix
395221961Sbrix	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) {
396221961Sbrix		device_printf(dev, "Could not allocate iicbus instance\n");
397221961Sbrix		error = ENXIO;
398221961Sbrix		goto out;
399221961Sbrix	}
400221961Sbrix
401221961Sbrix	ctx = device_get_sysctl_ctx(dev);
402221961Sbrix	tree = device_get_sysctl_tree(dev);
403221961Sbrix
404221961Sbrix	sc->timeout = GLXIIC_DEFAULT_TIMEOUT;
405221961Sbrix	snprintf(tn, sizeof(tn), "dev.glxiic.%d.timeout", unit);
406221961Sbrix	TUNABLE_INT_FETCH(tn, &sc->timeout);
407221961Sbrix	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
408221961Sbrix	    "timeout", CTLFLAG_RW | CTLFLAG_TUN, &sc->timeout, 0,
409221961Sbrix	    "activity timeout in ms");
410221961Sbrix
411221961Sbrix	glxiic_gpio_enable(sc);
412221961Sbrix	glxiic_smb_enable(sc, IIC_FASTEST, 0);
413221961Sbrix
414221961Sbrix	error = bus_generic_attach(dev);
415221961Sbrix	if (error != 0) {
416221961Sbrix		device_printf(dev, "Could not probe and attach children\n");
417221961Sbrix		error = ENXIO;
418221961Sbrix	}
419221961Sbrixout:
420221961Sbrix	if (error != 0) {
421221961Sbrix		callout_drain(&sc->callout);
422221961Sbrix
423221961Sbrix		if (sc->iicbus != NULL)
424221961Sbrix			device_delete_child(dev, sc->iicbus);
425221961Sbrix		if (sc->smb_res != NULL) {
426221961Sbrix			glxiic_smb_disable(sc);
427221961Sbrix			bus_release_resource(dev, SYS_RES_IOPORT, sc->smb_rid,
428221961Sbrix			    sc->smb_res);
429221961Sbrix		}
430221961Sbrix		if (sc->gpio_res != NULL) {
431221961Sbrix			glxiic_gpio_disable(sc);
432221961Sbrix			bus_release_resource(dev, SYS_RES_IOPORT, sc->gpio_rid,
433221961Sbrix			    sc->gpio_res);
434221961Sbrix		}
435221961Sbrix		if (sc->irq_handler != NULL)
436221961Sbrix			bus_teardown_intr(dev, sc->irq_res, sc->irq_handler);
437221961Sbrix		if (sc->irq_res != NULL)
438221961Sbrix			bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
439221961Sbrix			    sc->irq_res);
440221961Sbrix
441221961Sbrix		/* Restore the old SMBus interrupt mapping. */
442221961Sbrix		glxiic_smb_map_interrupt(sc->old_irq);
443221961Sbrix
444221961Sbrix		GLXIIC_LOCK_DESTROY(sc);
445221961Sbrix	}
446221961Sbrix
447221961Sbrix	return (error);
448221961Sbrix}
449221961Sbrix
450221961Sbrixstatic int
451221961Sbrixglxiic_detach(device_t dev)
452221961Sbrix{
453221961Sbrix	struct glxiic_softc *sc;
454221961Sbrix	int error;
455221961Sbrix
456221961Sbrix	sc = device_get_softc(dev);
457221961Sbrix
458221961Sbrix	error = bus_generic_detach(dev);
459221961Sbrix	if (error != 0)
460221961Sbrix		goto out;
461221961Sbrix	if (sc->iicbus != NULL)
462221961Sbrix		error = device_delete_child(dev, sc->iicbus);
463221961Sbrix
464221961Sbrixout:
465221961Sbrix	callout_drain(&sc->callout);
466221961Sbrix
467221961Sbrix	if (sc->smb_res != NULL) {
468221961Sbrix		glxiic_smb_disable(sc);
469221961Sbrix		bus_release_resource(dev, SYS_RES_IOPORT, sc->smb_rid,
470221961Sbrix		    sc->smb_res);
471221961Sbrix	}
472221961Sbrix	if (sc->gpio_res != NULL) {
473221961Sbrix		glxiic_gpio_disable(sc);
474221961Sbrix		bus_release_resource(dev, SYS_RES_IOPORT, sc->gpio_rid,
475221961Sbrix		    sc->gpio_res);
476221961Sbrix	}
477221961Sbrix	if (sc->irq_handler != NULL)
478221961Sbrix		bus_teardown_intr(dev, sc->irq_res, sc->irq_handler);
479221961Sbrix	if (sc->irq_res != NULL)
480221961Sbrix		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
481221961Sbrix		    sc->irq_res);
482221961Sbrix
483221961Sbrix	/* Restore the old SMBus interrupt mapping. */
484221961Sbrix	glxiic_smb_map_interrupt(sc->old_irq);
485221961Sbrix
486221961Sbrix	GLXIIC_LOCK_DESTROY(sc);
487221961Sbrix
488221961Sbrix	return (error);
489221961Sbrix}
490221961Sbrix
491221961Sbrixstatic uint8_t
492221961Sbrixglxiic_read_status_locked(struct glxiic_softc *sc)
493221961Sbrix{
494221961Sbrix	uint8_t status;
495221961Sbrix
496221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
497221961Sbrix
498221961Sbrix	status = bus_read_1(sc->smb_res, GLXIIC_SMB_STS);
499221961Sbrix
500221961Sbrix	/* Clear all status flags except SDAST and STASTR after reading. */
501221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_STS, (GLXIIC_SMB_STS_SLVSTP_BIT |
502221961Sbrix		GLXIIC_SMB_STS_BER_BIT | GLXIIC_SMB_STS_NEGACK_BIT |
503221961Sbrix		GLXIIC_SMB_STS_NMATCH_BIT));
504221961Sbrix
505221961Sbrix	return (status);
506221961Sbrix}
507221961Sbrix
508221961Sbrixstatic void
509221961Sbrixglxiic_stop_locked(struct glxiic_softc *sc)
510221961Sbrix{
511221961Sbrix	uint8_t status, ctrl1;
512221961Sbrix
513221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
514221961Sbrix
515221961Sbrix	status = glxiic_read_status_locked(sc);
516221961Sbrix
517221961Sbrix	ctrl1 = bus_read_1(sc->smb_res, GLXIIC_SMB_CTRL1);
518221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_CTRL1,
519221961Sbrix	    ctrl1 | GLXIIC_SMB_CTRL1_STOP_BIT);
520221961Sbrix
521221961Sbrix	/*
522221961Sbrix	 * Perform a dummy read of SDA in master receive mode to clear
523221961Sbrix	 * SDAST if set.
524221961Sbrix	 */
525221961Sbrix	if ((status & GLXIIC_SMB_STS_XMIT_BIT) == 0 &&
526221961Sbrix	    (status & GLXIIC_SMB_STS_SDAST_BIT) != 0)
527221961Sbrix	 	bus_read_1(sc->smb_res, GLXIIC_SMB_SDA);
528221961Sbrix
529221961Sbrix	/* Check stall after start bit and clear if needed */
530221961Sbrix	if ((status & GLXIIC_SMB_STS_STASTR_BIT) != 0) {
531221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_STS,
532221961Sbrix		    GLXIIC_SMB_STS_STASTR_BIT);
533221961Sbrix	}
534221961Sbrix}
535221961Sbrix
536221961Sbrixstatic void
537221961Sbrixglxiic_timeout(void *arg)
538221961Sbrix{
539221961Sbrix	struct glxiic_softc *sc;
540221961Sbrix	uint8_t error;
541221961Sbrix
542221961Sbrix	sc = (struct glxiic_softc *)arg;
543221961Sbrix
544221971Sbrix	GLXIIC_DEBUG_LOG("timeout in state %d", sc->state);
545221961Sbrix
546221961Sbrix	if (glxiic_state_table[sc->state].master) {
547221961Sbrix		sc->error = IIC_ETIMEOUT;
548221961Sbrix		GLXIIC_WAKEUP(sc);
549221961Sbrix	} else {
550221961Sbrix		error = IIC_ETIMEOUT;
551221961Sbrix		iicbus_intr(sc->iicbus, INTR_ERROR, &error);
552221961Sbrix	}
553221961Sbrix
554221961Sbrix	glxiic_smb_disable(sc);
555221961Sbrix	glxiic_smb_enable(sc, IIC_UNKNOWN, sc->addr);
556221961Sbrix	glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
557221961Sbrix}
558221961Sbrix
559221961Sbrixstatic void
560221961Sbrixglxiic_start_timeout_locked(struct glxiic_softc *sc)
561221961Sbrix{
562221961Sbrix
563221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
564221961Sbrix
565221961Sbrix	callout_reset(&sc->callout, sc->timeout * 1000 / hz, glxiic_timeout,
566221961Sbrix	    sc);
567221961Sbrix}
568221961Sbrix
569221961Sbrixstatic void
570221961Sbrixglxiic_set_state_locked(struct glxiic_softc *sc, glxiic_state_t state)
571221961Sbrix{
572221961Sbrix
573221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
574221961Sbrix
575221961Sbrix	if (state == GLXIIC_STATE_IDLE)
576221961Sbrix		callout_stop(&sc->callout);
577221961Sbrix	else if (sc->timeout > 0)
578221961Sbrix		glxiic_start_timeout_locked(sc);
579221961Sbrix
580221961Sbrix	sc->state = state;
581221961Sbrix}
582221961Sbrix
583221961Sbrixstatic int
584221961Sbrixglxiic_handle_slave_match_locked(struct glxiic_softc *sc, uint8_t status)
585221961Sbrix{
586221961Sbrix	uint8_t ctrl_sts, addr;
587221961Sbrix
588221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
589221961Sbrix
590221961Sbrix	ctrl_sts = bus_read_1(sc->smb_res, GLXIIC_SMB_CTRL_STS);
591221961Sbrix
592221961Sbrix	if ((ctrl_sts & GLXIIC_SMB_CTRL_STS_MATCH_BIT) != 0) {
593221961Sbrix		if ((status & GLXIIC_SMB_STS_XMIT_BIT) != 0) {
594221961Sbrix			addr = sc->addr | LSB;
595221961Sbrix			glxiic_set_state_locked(sc,
596221961Sbrix			    GLXIIC_STATE_SLAVE_TX);
597221961Sbrix		} else {
598221961Sbrix			addr = sc->addr & ~LSB;
599221961Sbrix			glxiic_set_state_locked(sc,
600221961Sbrix			    GLXIIC_STATE_SLAVE_RX);
601221961Sbrix		}
602221961Sbrix		iicbus_intr(sc->iicbus, INTR_START, &addr);
603221961Sbrix	} else if ((ctrl_sts & GLXIIC_SMB_CTRL_STS_GCMTCH_BIT) != 0) {
604221961Sbrix		addr = 0;
605221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_SLAVE_RX);
606221961Sbrix		iicbus_intr(sc->iicbus, INTR_GENERAL, &addr);
607221961Sbrix	} else {
608221971Sbrix		GLXIIC_DEBUG_LOG("unknown slave match");
609221961Sbrix		return (IIC_ESTATUS);
610221961Sbrix	}
611221961Sbrix
612221961Sbrix	return (IIC_NOERR);
613221961Sbrix}
614221961Sbrix
615221961Sbrixstatic int
616221961Sbrixglxiic_state_idle_callback(struct glxiic_softc *sc, uint8_t status)
617221961Sbrix{
618221961Sbrix
619221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
620221961Sbrix
621221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
622221971Sbrix		GLXIIC_DEBUG_LOG("bus error in idle");
623221961Sbrix		return (IIC_EBUSERR);
624221961Sbrix	}
625221961Sbrix
626221961Sbrix	if ((status & GLXIIC_SMB_STS_NMATCH_BIT) != 0) {
627221961Sbrix		return (glxiic_handle_slave_match_locked(sc, status));
628221961Sbrix	}
629221961Sbrix
630221961Sbrix	return (IIC_NOERR);
631221961Sbrix}
632221961Sbrix
633221961Sbrixstatic int
634221961Sbrixglxiic_state_slave_tx_callback(struct glxiic_softc *sc, uint8_t status)
635221961Sbrix{
636221961Sbrix	uint8_t data;
637221961Sbrix
638221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
639221961Sbrix
640221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
641221971Sbrix		GLXIIC_DEBUG_LOG("bus error in slave tx");
642221961Sbrix		return (IIC_EBUSERR);
643221961Sbrix	}
644221961Sbrix
645221961Sbrix	if ((status & GLXIIC_SMB_STS_SLVSTP_BIT) != 0) {
646221961Sbrix		iicbus_intr(sc->iicbus, INTR_STOP, NULL);
647221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
648221961Sbrix		return (IIC_NOERR);
649221961Sbrix	}
650221961Sbrix
651221961Sbrix	if ((status & GLXIIC_SMB_STS_NEGACK_BIT) != 0) {
652221961Sbrix		iicbus_intr(sc->iicbus, INTR_NOACK, NULL);
653221961Sbrix		return (IIC_NOERR);
654221961Sbrix	}
655221961Sbrix
656221961Sbrix	if ((status & GLXIIC_SMB_STS_NMATCH_BIT) != 0) {
657221961Sbrix		/* Handle repeated start in slave mode. */
658221961Sbrix		return (glxiic_handle_slave_match_locked(sc, status));
659221961Sbrix	}
660221961Sbrix
661221961Sbrix	if ((status & GLXIIC_SMB_STS_SDAST_BIT) == 0) {
662221971Sbrix		GLXIIC_DEBUG_LOG("not awaiting data in slave tx");
663221961Sbrix		return (IIC_ESTATUS);
664221961Sbrix	}
665221961Sbrix
666221961Sbrix	iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
667221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_SDA, data);
668221961Sbrix
669221961Sbrix	glxiic_start_timeout_locked(sc);
670221961Sbrix
671221961Sbrix	return (IIC_NOERR);
672221961Sbrix}
673221961Sbrix
674221961Sbrixstatic int
675221961Sbrixglxiic_state_slave_rx_callback(struct glxiic_softc *sc, uint8_t status)
676221961Sbrix{
677221961Sbrix	uint8_t data;
678221961Sbrix
679221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
680221961Sbrix
681221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
682221971Sbrix		GLXIIC_DEBUG_LOG("bus error in slave rx");
683221961Sbrix		return (IIC_EBUSERR);
684221961Sbrix	}
685221961Sbrix
686221961Sbrix	if ((status & GLXIIC_SMB_STS_SLVSTP_BIT) != 0) {
687221961Sbrix		iicbus_intr(sc->iicbus, INTR_STOP, NULL);
688221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
689221961Sbrix		return (IIC_NOERR);
690221961Sbrix	}
691221961Sbrix
692221961Sbrix	if ((status & GLXIIC_SMB_STS_NMATCH_BIT) != 0) {
693221961Sbrix		/* Handle repeated start in slave mode. */
694221961Sbrix		return (glxiic_handle_slave_match_locked(sc, status));
695221961Sbrix	}
696221961Sbrix
697221961Sbrix	if ((status & GLXIIC_SMB_STS_SDAST_BIT) == 0) {
698221971Sbrix		GLXIIC_DEBUG_LOG("no pending data in slave rx");
699221961Sbrix		return (IIC_ESTATUS);
700221961Sbrix	}
701221961Sbrix
702221961Sbrix	data = bus_read_1(sc->smb_res, GLXIIC_SMB_SDA);
703221961Sbrix	iicbus_intr(sc->iicbus, INTR_RECEIVE, &data);
704221961Sbrix
705221961Sbrix	glxiic_start_timeout_locked(sc);
706221961Sbrix
707221961Sbrix	return (IIC_NOERR);
708221961Sbrix}
709221961Sbrix
710221961Sbrixstatic int
711221961Sbrixglxiic_state_master_addr_callback(struct glxiic_softc *sc, uint8_t status)
712221961Sbrix{
713221961Sbrix	uint8_t slave;
714221961Sbrix
715221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
716221961Sbrix
717221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
718221971Sbrix		GLXIIC_DEBUG_LOG("bus error after master start");
719221961Sbrix		return (IIC_EBUSERR);
720221961Sbrix	}
721221961Sbrix
722221961Sbrix	if ((status & GLXIIC_SMB_STS_MASTER_BIT) == 0) {
723221971Sbrix		GLXIIC_DEBUG_LOG("not bus master after master start");
724221961Sbrix		return (IIC_ESTATUS);
725221961Sbrix	}
726221961Sbrix
727221961Sbrix	if ((status & GLXIIC_SMB_STS_SDAST_BIT) == 0) {
728221971Sbrix		GLXIIC_DEBUG_LOG("not awaiting address in master addr");
729221961Sbrix		return (IIC_ESTATUS);
730221961Sbrix	}
731221961Sbrix
732221961Sbrix	if ((sc->msg->flags & IIC_M_RD) != 0) {
733221961Sbrix		slave = sc->msg->slave | LSB;
734221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_RX);
735221961Sbrix	} else {
736221961Sbrix		slave = sc->msg->slave & ~LSB;
737221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_TX);
738221961Sbrix	}
739221961Sbrix
740221961Sbrix	sc->data = sc->msg->buf;
741221961Sbrix	sc->ndata = sc->msg->len;
742221961Sbrix
743221961Sbrix	/* Handle address-only transfer. */
744221961Sbrix	if (sc->ndata == 0)
745221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_STOP);
746221961Sbrix
747221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_SDA, slave);
748221961Sbrix
749221961Sbrix	return (IIC_NOERR);
750221961Sbrix}
751221961Sbrix
752221961Sbrixstatic int
753221961Sbrixglxiic_state_master_tx_callback(struct glxiic_softc *sc, uint8_t status)
754221961Sbrix{
755221961Sbrix
756221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
757221961Sbrix
758221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
759221971Sbrix		GLXIIC_DEBUG_LOG("bus error in master tx");
760221961Sbrix		return (IIC_EBUSERR);
761221961Sbrix	}
762221961Sbrix
763221961Sbrix	if ((status & GLXIIC_SMB_STS_MASTER_BIT) == 0) {
764221971Sbrix		GLXIIC_DEBUG_LOG("not bus master in master tx");
765221961Sbrix		return (IIC_ESTATUS);
766221961Sbrix	}
767221961Sbrix
768221961Sbrix	if ((status & GLXIIC_SMB_STS_NEGACK_BIT) != 0) {
769221971Sbrix		GLXIIC_DEBUG_LOG("slave nack in master tx");
770221961Sbrix		return (IIC_ENOACK);
771221961Sbrix	}
772221961Sbrix
773221961Sbrix	if ((status & GLXIIC_SMB_STS_STASTR_BIT) != 0) {
774221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_STS,
775221961Sbrix		    GLXIIC_SMB_STS_STASTR_BIT);
776221961Sbrix	}
777221961Sbrix
778221961Sbrix	if ((status & GLXIIC_SMB_STS_SDAST_BIT) == 0) {
779221971Sbrix		GLXIIC_DEBUG_LOG("not awaiting data in master tx");
780221961Sbrix		return (IIC_ESTATUS);
781221961Sbrix	}
782221961Sbrix
783221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_SDA, *sc->data++);
784221961Sbrix	if (--sc->ndata == 0)
785221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_STOP);
786221961Sbrix	else
787221961Sbrix		glxiic_start_timeout_locked(sc);
788221961Sbrix
789221961Sbrix	return (IIC_NOERR);
790221961Sbrix}
791221961Sbrix
792221961Sbrixstatic int
793221961Sbrixglxiic_state_master_rx_callback(struct glxiic_softc *sc, uint8_t status)
794221961Sbrix{
795221961Sbrix	uint8_t ctrl1;
796221961Sbrix
797221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
798221961Sbrix
799221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
800221971Sbrix		GLXIIC_DEBUG_LOG("bus error in master rx");
801221961Sbrix		return (IIC_EBUSERR);
802221961Sbrix	}
803221961Sbrix
804221961Sbrix	if ((status & GLXIIC_SMB_STS_MASTER_BIT) == 0) {
805221971Sbrix		GLXIIC_DEBUG_LOG("not bus master in master rx");
806221961Sbrix		return (IIC_ESTATUS);
807221961Sbrix	}
808221961Sbrix
809221961Sbrix	if ((status & GLXIIC_SMB_STS_NEGACK_BIT) != 0) {
810221971Sbrix		GLXIIC_DEBUG_LOG("slave nack in rx");
811221961Sbrix		return (IIC_ENOACK);
812221961Sbrix	}
813221961Sbrix
814221961Sbrix	if (sc->ndata == 1) {
815221961Sbrix		/* Last byte from slave, set NACK. */
816221961Sbrix		ctrl1 = bus_read_1(sc->smb_res, GLXIIC_SMB_CTRL1);
817221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_CTRL1,
818221961Sbrix		    ctrl1 | GLXIIC_SMB_CTRL1_ACK_BIT);
819221961Sbrix	}
820221961Sbrix
821221961Sbrix	if ((status & GLXIIC_SMB_STS_STASTR_BIT) != 0) {
822221961Sbrix		/* Bus is stalled, clear and wait for data. */
823221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_STS,
824221961Sbrix		    GLXIIC_SMB_STS_STASTR_BIT);
825221961Sbrix		return (IIC_NOERR);
826221961Sbrix	}
827221961Sbrix
828221961Sbrix	if ((status & GLXIIC_SMB_STS_SDAST_BIT) == 0) {
829221971Sbrix		GLXIIC_DEBUG_LOG("no pending data in master rx");
830221961Sbrix		return (IIC_ESTATUS);
831221961Sbrix	}
832221961Sbrix
833221961Sbrix	*sc->data++ = bus_read_1(sc->smb_res, GLXIIC_SMB_SDA);
834221961Sbrix	if (--sc->ndata == 0) {
835221961Sbrix		/* Proceed with stop on reading last byte. */
836221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_STOP);
837221961Sbrix		return (glxiic_state_table[sc->state].callback(sc, status));
838221961Sbrix	}
839221961Sbrix
840221961Sbrix	glxiic_start_timeout_locked(sc);
841221961Sbrix
842221961Sbrix	return (IIC_NOERR);
843221961Sbrix}
844221961Sbrix
845221961Sbrixstatic int
846221961Sbrixglxiic_state_master_stop_callback(struct glxiic_softc *sc, uint8_t status)
847221961Sbrix{
848221961Sbrix	uint8_t ctrl1;
849221961Sbrix
850221961Sbrix	GLXIIC_ASSERT_LOCKED(sc);
851221961Sbrix
852221961Sbrix	if ((status & GLXIIC_SMB_STS_BER_BIT) != 0) {
853221971Sbrix		GLXIIC_DEBUG_LOG("bus error in master stop");
854221961Sbrix		return (IIC_EBUSERR);
855221961Sbrix	}
856221961Sbrix
857221961Sbrix	if ((status & GLXIIC_SMB_STS_MASTER_BIT) == 0) {
858221971Sbrix		GLXIIC_DEBUG_LOG("not bus master in master stop");
859221961Sbrix		return (IIC_ESTATUS);
860221961Sbrix	}
861221961Sbrix
862221961Sbrix	if ((status & GLXIIC_SMB_STS_NEGACK_BIT) != 0) {
863221971Sbrix		GLXIIC_DEBUG_LOG("slave nack in master stop");
864221961Sbrix		return (IIC_ENOACK);
865221961Sbrix	}
866221961Sbrix
867221961Sbrix	if (--sc->nmsgs > 0) {
868221961Sbrix		/* Start transfer of next message. */
869221961Sbrix		if ((sc->msg->flags & IIC_M_NOSTOP) == 0) {
870221961Sbrix			glxiic_stop_locked(sc);
871221961Sbrix		}
872221961Sbrix
873221961Sbrix		ctrl1 = bus_read_1(sc->smb_res, GLXIIC_SMB_CTRL1);
874221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_CTRL1,
875221961Sbrix		    ctrl1 | GLXIIC_SMB_CTRL1_START_BIT);
876221961Sbrix
877221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_ADDR);
878221961Sbrix		sc->msg++;
879221961Sbrix	} else {
880221961Sbrix		/* Last message. */
881221961Sbrix		glxiic_stop_locked(sc);
882221961Sbrix		glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
883221961Sbrix		sc->error = IIC_NOERR;
884221961Sbrix		GLXIIC_WAKEUP(sc);
885221961Sbrix	}
886221961Sbrix
887221961Sbrix	return (IIC_NOERR);
888221961Sbrix}
889221961Sbrix
890221961Sbrixstatic void
891221961Sbrixglxiic_intr(void *arg)
892221961Sbrix{
893221961Sbrix	struct glxiic_softc *sc;
894221961Sbrix	int error;
895221961Sbrix	uint8_t status, data;
896221961Sbrix
897221961Sbrix	sc = (struct glxiic_softc *)arg;
898221961Sbrix
899221961Sbrix	GLXIIC_LOCK(sc);
900221961Sbrix
901221961Sbrix	status = glxiic_read_status_locked(sc);
902221961Sbrix
903221961Sbrix	/* Check if this interrupt originated from the SMBus. */
904221961Sbrix	if ((status &
905221961Sbrix		~(GLXIIC_SMB_STS_MASTER_BIT | GLXIIC_SMB_STS_XMIT_BIT)) != 0) {
906221961Sbrix
907221961Sbrix		error = glxiic_state_table[sc->state].callback(sc, status);
908221961Sbrix
909221961Sbrix		if (error != IIC_NOERR) {
910221961Sbrix			if (glxiic_state_table[sc->state].master) {
911221961Sbrix				glxiic_stop_locked(sc);
912221961Sbrix				glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
913221961Sbrix				sc->error = error;
914221961Sbrix				GLXIIC_WAKEUP(sc);
915221961Sbrix			} else {
916221961Sbrix				data = error & 0xff;
917221961Sbrix				iicbus_intr(sc->iicbus, INTR_ERROR, &data);
918221961Sbrix				glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
919221961Sbrix			}
920221961Sbrix		}
921221961Sbrix	}
922221961Sbrix
923221961Sbrix	GLXIIC_UNLOCK(sc);
924221961Sbrix}
925221961Sbrix
926221961Sbrixstatic int
927221961Sbrixglxiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
928221961Sbrix{
929221961Sbrix	struct glxiic_softc *sc;
930221961Sbrix
931221961Sbrix	sc = device_get_softc(dev);
932221961Sbrix
933221961Sbrix	GLXIIC_LOCK(sc);
934221961Sbrix
935221961Sbrix	if (oldaddr != NULL)
936221961Sbrix		*oldaddr = sc->addr;
937221961Sbrix	sc->addr = addr;
938221961Sbrix
939221961Sbrix	/* A disable/enable cycle resets the controller. */
940221961Sbrix	glxiic_smb_disable(sc);
941221961Sbrix	glxiic_smb_enable(sc, speed, addr);
942221961Sbrix
943221961Sbrix	if (glxiic_state_table[sc->state].master) {
944221961Sbrix		sc->error = IIC_ESTATUS;
945221961Sbrix		GLXIIC_WAKEUP(sc);
946221961Sbrix	}
947221961Sbrix	glxiic_set_state_locked(sc, GLXIIC_STATE_IDLE);
948221961Sbrix
949221961Sbrix	GLXIIC_UNLOCK(sc);
950221961Sbrix
951221961Sbrix	return (IIC_NOERR);
952221961Sbrix}
953221961Sbrix
954221961Sbrixstatic int
955221961Sbrixglxiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
956221961Sbrix{
957221961Sbrix	struct glxiic_softc *sc;
958221961Sbrix	int error;
959221961Sbrix	uint8_t ctrl1;
960221961Sbrix
961221961Sbrix	sc = device_get_softc(dev);
962221961Sbrix
963221961Sbrix	GLXIIC_LOCK(sc);
964221961Sbrix
965221961Sbrix	if (sc->state != GLXIIC_STATE_IDLE) {
966221961Sbrix		error = IIC_EBUSBSY;
967221961Sbrix		goto out;
968221961Sbrix	}
969221961Sbrix
970221961Sbrix	sc->msg = msgs;
971221961Sbrix	sc->nmsgs = nmsgs;
972221961Sbrix	glxiic_set_state_locked(sc, GLXIIC_STATE_MASTER_ADDR);
973221961Sbrix
974221961Sbrix	/* Set start bit and let glxiic_intr() handle the transfer. */
975221961Sbrix	ctrl1 = bus_read_1(sc->smb_res, GLXIIC_SMB_CTRL1);
976221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_CTRL1,
977221961Sbrix	    ctrl1 | GLXIIC_SMB_CTRL1_START_BIT);
978221961Sbrix
979221961Sbrix	GLXIIC_SLEEP(sc);
980221961Sbrix	error = sc->error;
981221961Sbrixout:
982221961Sbrix	GLXIIC_UNLOCK(sc);
983221961Sbrix
984221961Sbrix	return (error);
985221961Sbrix}
986221961Sbrix
987221961Sbrixstatic void
988221961Sbrixglxiic_smb_map_interrupt(int irq)
989221961Sbrix{
990221961Sbrix	uint32_t irq_map;
991221961Sbrix	int old_irq;
992221961Sbrix
993221961Sbrix	/* Protect the read-modify-write operation. */
994221961Sbrix	critical_enter();
995221961Sbrix
996221961Sbrix	irq_map = rdmsr(GLXIIC_MSR_PIC_YSEL_HIGH);
997221961Sbrix	old_irq = GLXIIC_MAP_TO_SMB_IRQ(irq_map);
998221961Sbrix
999221961Sbrix	if (irq != old_irq) {
1000221961Sbrix		irq_map &= ~GLXIIC_SMB_IRQ_TO_MAP(old_irq);
1001221961Sbrix		irq_map |= GLXIIC_SMB_IRQ_TO_MAP(irq);
1002221961Sbrix		wrmsr(GLXIIC_MSR_PIC_YSEL_HIGH, irq_map);
1003221961Sbrix	}
1004221961Sbrix
1005221961Sbrix	critical_exit();
1006221961Sbrix}
1007221961Sbrix
1008221961Sbrixstatic void
1009221961Sbrixglxiic_gpio_enable(struct glxiic_softc *sc)
1010221961Sbrix{
1011221961Sbrix
1012221961Sbrix	bus_write_4(sc->gpio_res, GLXIIC_GPIOL_IN_AUX1_SEL,
1013221961Sbrix	    GLXIIC_GPIO_14_15_ENABLE);
1014221961Sbrix	bus_write_4(sc->gpio_res, GLXIIC_GPIOL_OUT_AUX1_SEL,
1015221961Sbrix	    GLXIIC_GPIO_14_15_ENABLE);
1016221961Sbrix}
1017221961Sbrix
1018221961Sbrixstatic void
1019221961Sbrixglxiic_gpio_disable(struct glxiic_softc *sc)
1020221961Sbrix{
1021221961Sbrix
1022221961Sbrix	bus_write_4(sc->gpio_res, GLXIIC_GPIOL_OUT_AUX1_SEL,
1023221961Sbrix	    GLXIIC_GPIO_14_15_DISABLE);
1024221961Sbrix	bus_write_4(sc->gpio_res, GLXIIC_GPIOL_IN_AUX1_SEL,
1025221961Sbrix	    GLXIIC_GPIO_14_15_DISABLE);
1026221961Sbrix}
1027221961Sbrix
1028221961Sbrixstatic void
1029221961Sbrixglxiic_smb_enable(struct glxiic_softc *sc, uint8_t speed, uint8_t addr)
1030221961Sbrix{
1031221961Sbrix	uint8_t ctrl1;
1032221961Sbrix
1033221961Sbrix	ctrl1 = 0;
1034221961Sbrix
1035221961Sbrix	switch (speed) {
1036221961Sbrix	case IIC_SLOW:
1037221961Sbrix		sc->sclfrq = GLXIIC_SLOW;
1038221961Sbrix		break;
1039221961Sbrix	case IIC_FAST:
1040221961Sbrix		sc->sclfrq = GLXIIC_FAST;
1041221961Sbrix		break;
1042221961Sbrix	case IIC_FASTEST:
1043221961Sbrix		sc->sclfrq = GLXIIC_FASTEST;
1044221961Sbrix		break;
1045221961Sbrix	case IIC_UNKNOWN:
1046221961Sbrix	default:
1047221961Sbrix		/* Reuse last frequency. */
1048221961Sbrix		break;
1049221961Sbrix	}
1050221961Sbrix
1051221961Sbrix	/* Set bus speed and enable controller. */
1052221961Sbrix	bus_write_2(sc->smb_res, GLXIIC_SMB_CTRL2,
1053221961Sbrix	    GLXIIC_SCLFRQ(sc->sclfrq) | GLXIIC_SMB_CTRL2_EN_BIT);
1054221961Sbrix
1055221961Sbrix	if (addr != 0) {
1056221961Sbrix		/* Enable new match and global call match interrupts. */
1057221961Sbrix		ctrl1 |= GLXIIC_SMB_CTRL1_NMINTE_BIT |
1058221961Sbrix			GLXIIC_SMB_CTRL1_GCMEN_BIT;
1059221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_ADDR,
1060221961Sbrix		    GLXIIC_SMB_ADDR_SAEN_BIT | GLXIIC_SMBADDR(addr));
1061221961Sbrix	} else {
1062221961Sbrix		bus_write_1(sc->smb_res, GLXIIC_SMB_ADDR, 0);
1063221961Sbrix	}
1064221961Sbrix
1065221961Sbrix	/* Enable stall after start and interrupt. */
1066221961Sbrix	bus_write_1(sc->smb_res, GLXIIC_SMB_CTRL1,
1067221961Sbrix	    ctrl1 | GLXIIC_SMB_CTRL1_STASTRE_BIT | GLXIIC_SMB_CTRL1_INTEN_BIT);
1068221961Sbrix}
1069221961Sbrix
1070221961Sbrixstatic void
1071221961Sbrixglxiic_smb_disable(struct glxiic_softc *sc)
1072221961Sbrix{
1073221961Sbrix	uint16_t sclfrq;
1074221961Sbrix
1075221961Sbrix	sclfrq = bus_read_2(sc->smb_res, GLXIIC_SMB_CTRL2);
1076221961Sbrix	bus_write_2(sc->smb_res, GLXIIC_SMB_CTRL2,
1077221961Sbrix	    sclfrq & ~GLXIIC_SMB_CTRL2_EN_BIT);
1078221961Sbrix}
1079