1293675Sjimharris/*-
2293675Sjimharris * Copyright (C) 2014 Intel Corporation
3293675Sjimharris * All rights reserved.
4293675Sjimharris *
5293675Sjimharris * Redistribution and use in source and binary forms, with or without
6293675Sjimharris * modification, are permitted provided that the following conditions
7293675Sjimharris * are met:
8293675Sjimharris * 1. Redistributions of source code must retain the above copyright
9293675Sjimharris *    notice, this list of conditions and the following disclaimer.
10293675Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
11293675Sjimharris *    notice, this list of conditions and the following disclaimer in the
12293675Sjimharris *    documentation and/or other materials provided with the distribution.
13293675Sjimharris * 3. Neither the name of Intel Corporation nor the names of its
14293675Sjimharris *    contributors may be used to endorse or promote products derived from
15293675Sjimharris *    this software without specific prior written permission.
16293675Sjimharris *
17293675Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18293675Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19293675Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20293675Sjimharris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21293675Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22293675Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23293675Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24293675Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25293675Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26293675Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27293675Sjimharris * SUCH DAMAGE.
28293675Sjimharris */
29293675Sjimharris
30293675Sjimharris#include <sys/cdefs.h>
31293675Sjimharris__FBSDID("$FreeBSD$");
32293675Sjimharris
33293675Sjimharris#include <sys/param.h>
34293675Sjimharris#include <sys/systm.h>
35293675Sjimharris#include <sys/bus.h>
36293675Sjimharris#include <sys/errno.h>
37293675Sjimharris#include <sys/kernel.h>
38293675Sjimharris#include <sys/lock.h>
39293675Sjimharris#include <sys/module.h>
40293675Sjimharris#include <sys/priority.h>
41293675Sjimharris#include <sys/proc.h>
42293675Sjimharris#include <sys/syslog.h>
43293675Sjimharris
44293675Sjimharris#include <machine/bus.h>
45293675Sjimharris#include <sys/rman.h>
46293675Sjimharris#include <machine/resource.h>
47293675Sjimharris
48293675Sjimharris#include <dev/pci/pcireg.h>
49293675Sjimharris#include <dev/pci/pcivar.h>
50293675Sjimharris#include <dev/smbus/smbconf.h>
51293675Sjimharris
52293675Sjimharris#include "smbus_if.h"
53293675Sjimharris
54293675Sjimharris#define ISMT_DESC_ENTRIES	32
55293675Sjimharris
56293675Sjimharris/* Hardware Descriptor Constants - Control Field */
57293675Sjimharris#define ISMT_DESC_CWRL	0x01	/* Command/Write Length */
58293675Sjimharris#define ISMT_DESC_BLK	0X04	/* Perform Block Transaction */
59293675Sjimharris#define ISMT_DESC_FAIR	0x08	/* Set fairness flag upon successful arbit. */
60293675Sjimharris#define ISMT_DESC_PEC	0x10	/* Packet Error Code */
61293675Sjimharris#define ISMT_DESC_I2C	0x20	/* I2C Enable */
62293675Sjimharris#define ISMT_DESC_INT	0x40	/* Interrupt */
63293675Sjimharris#define ISMT_DESC_SOE	0x80	/* Stop On Error */
64293675Sjimharris
65293675Sjimharris/* Hardware Descriptor Constants - Status Field */
66293675Sjimharris#define ISMT_DESC_SCS	0x01	/* Success */
67293675Sjimharris#define ISMT_DESC_DLTO	0x04	/* Data Low Time Out */
68293675Sjimharris#define ISMT_DESC_NAK	0x08	/* NAK Received */
69293675Sjimharris#define ISMT_DESC_CRC	0x10	/* CRC Error */
70293675Sjimharris#define ISMT_DESC_CLTO	0x20	/* Clock Low Time Out */
71293675Sjimharris#define ISMT_DESC_COL	0x40	/* Collisions */
72293675Sjimharris#define ISMT_DESC_LPR	0x80	/* Large Packet Received */
73293675Sjimharris
74293675Sjimharris/* Macros */
75293676Sjimharris#define ISMT_DESC_ADDR_RW(addr, is_read) ((addr << 1) | (is_read))
76293675Sjimharris
77293675Sjimharris/* iSMT General Register address offsets (SMBBAR + <addr>) */
78293675Sjimharris#define ISMT_GR_GCTRL		0x000	/* General Control */
79293675Sjimharris#define ISMT_GR_SMTICL		0x008	/* SMT Interrupt Cause Location */
80293675Sjimharris#define ISMT_GR_ERRINTMSK	0x010	/* Error Interrupt Mask */
81293675Sjimharris#define ISMT_GR_ERRAERMSK	0x014	/* Error AER Mask */
82293675Sjimharris#define ISMT_GR_ERRSTS		0x018	/* Error Status */
83293675Sjimharris#define ISMT_GR_ERRINFO		0x01c	/* Error Information */
84293675Sjimharris
85293675Sjimharris/* iSMT Master Registers */
86293675Sjimharris#define ISMT_MSTR_MDBA		0x100	/* Master Descriptor Base Address */
87293675Sjimharris#define ISMT_MSTR_MCTRL		0x108	/* Master Control */
88293675Sjimharris#define ISMT_MSTR_MSTS		0x10c	/* Master Status */
89293675Sjimharris#define ISMT_MSTR_MDS		0x110	/* Master Descriptor Size */
90293675Sjimharris#define ISMT_MSTR_RPOLICY	0x114	/* Retry Policy */
91293675Sjimharris
92293675Sjimharris/* iSMT Miscellaneous Registers */
93293675Sjimharris#define ISMT_SPGT	0x300	/* SMBus PHY Global Timing */
94293675Sjimharris
95293675Sjimharris/* General Control Register (GCTRL) bit definitions */
96293675Sjimharris#define ISMT_GCTRL_TRST	0x04	/* Target Reset */
97293675Sjimharris#define ISMT_GCTRL_KILL	0x08	/* Kill */
98293675Sjimharris#define ISMT_GCTRL_SRST	0x40	/* Soft Reset */
99293675Sjimharris
100293675Sjimharris/* Master Control Register (MCTRL) bit definitions */
101293675Sjimharris#define ISMT_MCTRL_SS	0x01		/* Start/Stop */
102293675Sjimharris#define ISMT_MCTRL_MEIE	0x10		/* Master Error Interrupt Enable */
103293675Sjimharris#define ISMT_MCTRL_FMHP	0x00ff0000	/* Firmware Master Head Ptr (FMHP) */
104293675Sjimharris
105293675Sjimharris/* Master Status Register (MSTS) bit definitions */
106293675Sjimharris#define ISMT_MSTS_HMTP	0xff0000	/* HW Master Tail Pointer (HMTP) */
107293675Sjimharris#define ISMT_MSTS_MIS	0x20		/* Master Interrupt Status (MIS) */
108293675Sjimharris#define ISMT_MSTS_MEIS	0x10		/* Master Error Int Status (MEIS) */
109293675Sjimharris#define ISMT_MSTS_IP	0x01		/* In Progress */
110293675Sjimharris
111293675Sjimharris/* Master Descriptor Size (MDS) bit definitions */
112293675Sjimharris#define ISMT_MDS_MASK	0xff	/* Master Descriptor Size mask (MDS) */
113293675Sjimharris
114293675Sjimharris/* SMBus PHY Global Timing Register (SPGT) bit definitions */
115293675Sjimharris#define ISMT_SPGT_SPD_MASK	0xc0000000	/* SMBus Speed mask */
116293675Sjimharris#define ISMT_SPGT_SPD_80K	0x00		/* 80 kHz */
117293675Sjimharris#define ISMT_SPGT_SPD_100K	(0x1 << 30)	/* 100 kHz */
118293675Sjimharris#define ISMT_SPGT_SPD_400K	(0x2 << 30)	/* 400 kHz */
119293675Sjimharris#define ISMT_SPGT_SPD_1M	(0x3 << 30)	/* 1 MHz */
120293675Sjimharris
121293675Sjimharris/* MSI Control Register (MSICTL) bit definitions */
122293675Sjimharris#define ISMT_MSICTL_MSIE	0x01	/* MSI Enable */
123293675Sjimharris
124293675Sjimharris#define ISMT_MAX_BLOCK_SIZE	32 /* per SMBus spec */
125293675Sjimharris
126293675Sjimharris//#define ISMT_DEBUG	device_printf
127293675Sjimharris#ifndef ISMT_DEBUG
128293675Sjimharris#define ISMT_DEBUG(...)
129293675Sjimharris#endif
130293675Sjimharris
131293675Sjimharris/* iSMT Hardware Descriptor */
132293675Sjimharrisstruct ismt_desc {
133293675Sjimharris	uint8_t tgtaddr_rw;	/* target address & r/w bit */
134293675Sjimharris	uint8_t wr_len_cmd;	/* write length in bytes or a command */
135293675Sjimharris	uint8_t rd_len;		/* read length */
136293675Sjimharris	uint8_t control;	/* control bits */
137293675Sjimharris	uint8_t status;		/* status bits */
138293675Sjimharris	uint8_t retry;		/* collision retry and retry count */
139293675Sjimharris	uint8_t rxbytes;	/* received bytes */
140293675Sjimharris	uint8_t txbytes;	/* transmitted bytes */
141293675Sjimharris	uint32_t dptr_low;	/* lower 32 bit of the data pointer */
142293675Sjimharris	uint32_t dptr_high;	/* upper 32 bit of the data pointer */
143293675Sjimharris} __packed;
144293675Sjimharris
145293675Sjimharris#define DESC_SIZE	(ISMT_DESC_ENTRIES * sizeof(struct ismt_desc))
146293675Sjimharris
147293675Sjimharris#define DMA_BUFFER_SIZE	64
148293675Sjimharris
149293675Sjimharrisstruct ismt_softc {
150293675Sjimharris	device_t		pcidev;
151293675Sjimharris	device_t		smbdev;
152293675Sjimharris
153293675Sjimharris	struct thread		*bus_reserved;
154293675Sjimharris
155293675Sjimharris	int			intr_rid;
156293675Sjimharris	struct resource		*intr_res;
157293675Sjimharris	void			*intr_handle;
158293675Sjimharris
159293675Sjimharris	bus_space_tag_t		mmio_tag;
160293675Sjimharris	bus_space_handle_t	mmio_handle;
161293675Sjimharris	int			mmio_rid;
162293675Sjimharris	struct resource		*mmio_res;
163293675Sjimharris
164293675Sjimharris	uint8_t			head;
165293675Sjimharris
166293675Sjimharris	struct ismt_desc	*desc;
167293675Sjimharris	bus_dma_tag_t		desc_dma_tag;
168293675Sjimharris	bus_dmamap_t		desc_dma_map;
169293675Sjimharris	uint64_t		desc_bus_addr;
170293675Sjimharris
171293675Sjimharris	uint8_t			*dma_buffer;
172293675Sjimharris	bus_dma_tag_t		dma_buffer_dma_tag;
173293675Sjimharris	bus_dmamap_t		dma_buffer_dma_map;
174293675Sjimharris	uint64_t		dma_buffer_bus_addr;
175293675Sjimharris
176293675Sjimharris	uint8_t			using_msi;
177293675Sjimharris};
178293675Sjimharris
179293675Sjimharrisstatic void
180293675Sjimharrisismt_intr(void *arg)
181293675Sjimharris{
182293675Sjimharris	struct ismt_softc *sc = arg;
183293675Sjimharris	uint32_t val;
184293675Sjimharris
185293675Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MSTS);
186293675Sjimharris	ISMT_DEBUG(sc->pcidev, "%s MSTS=0x%x\n", __func__, val);
187293675Sjimharris
188293675Sjimharris	val |= (ISMT_MSTS_MIS | ISMT_MSTS_MEIS);
189293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MSTS, val);
190293675Sjimharris
191293675Sjimharris	wakeup(sc);
192293675Sjimharris}
193293675Sjimharris
194293675Sjimharrisstatic int
195293675Sjimharrisismt_callback(device_t dev, int index, void *data)
196293675Sjimharris{
197293675Sjimharris	struct ismt_softc	*sc;
198293675Sjimharris	int			acquired, err;
199293675Sjimharris
200293675Sjimharris	sc = device_get_softc(dev);
201293675Sjimharris
202293675Sjimharris	switch (index) {
203293675Sjimharris	case SMB_REQUEST_BUS:
204293675Sjimharris		acquired = atomic_cmpset_ptr(
205293675Sjimharris		    (uintptr_t *)&sc->bus_reserved,
206293675Sjimharris		    (uintptr_t)NULL, (uintptr_t)curthread);
207293675Sjimharris		ISMT_DEBUG(dev, "SMB_REQUEST_BUS acquired=%d\n", acquired);
208293675Sjimharris		if (acquired)
209293675Sjimharris			err = 0;
210293675Sjimharris		else
211293675Sjimharris			err = EWOULDBLOCK;
212293675Sjimharris		break;
213293675Sjimharris	case SMB_RELEASE_BUS:
214293675Sjimharris		KASSERT(sc->bus_reserved == curthread,
215293675Sjimharris		    ("SMB_RELEASE_BUS called by wrong thread\n"));
216293675Sjimharris		ISMT_DEBUG(dev, "SMB_RELEASE_BUS\n");
217293675Sjimharris		atomic_store_rel_ptr((uintptr_t *)&sc->bus_reserved,
218293675Sjimharris		    (uintptr_t)NULL);
219293675Sjimharris		err = 0;
220293675Sjimharris		break;
221293675Sjimharris	default:
222293675Sjimharris		err = SMB_EABORT;
223293675Sjimharris		break;
224293675Sjimharris	}
225293675Sjimharris
226293675Sjimharris	return (err);
227293675Sjimharris}
228293675Sjimharris
229293675Sjimharrisstatic struct ismt_desc *
230293675Sjimharrisismt_alloc_desc(struct ismt_softc *sc)
231293675Sjimharris{
232293675Sjimharris	struct ismt_desc *desc;
233293675Sjimharris
234293675Sjimharris	KASSERT(sc->bus_reserved == curthread,
235293675Sjimharris	    ("curthread %p did not request bus (%p has reserved)\n",
236293675Sjimharris	    curthread, sc->bus_reserved));
237293675Sjimharris
238293675Sjimharris	desc = &sc->desc[sc->head++];
239293675Sjimharris	if (sc->head == ISMT_DESC_ENTRIES)
240293675Sjimharris		sc->head = 0;
241293675Sjimharris
242293675Sjimharris	memset(desc, 0, sizeof(*desc));
243293675Sjimharris
244293675Sjimharris	return (desc);
245293675Sjimharris}
246293675Sjimharris
247293675Sjimharrisstatic int
248293675Sjimharrisismt_submit(struct ismt_softc *sc, struct ismt_desc *desc, uint8_t slave,
249293675Sjimharris    uint8_t is_read)
250293675Sjimharris{
251293675Sjimharris	uint32_t err, fmhp, val;
252293675Sjimharris
253293675Sjimharris	desc->control |= ISMT_DESC_FAIR;
254293675Sjimharris	if (sc->using_msi)
255293675Sjimharris		desc->control |= ISMT_DESC_INT;
256293675Sjimharris
257293675Sjimharris	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(slave, is_read);
258293675Sjimharris	desc->dptr_low = (sc->dma_buffer_bus_addr & 0xFFFFFFFFLL);
259293675Sjimharris	desc->dptr_high = (sc->dma_buffer_bus_addr >> 32);
260293675Sjimharris
261293675Sjimharris	wmb();
262293675Sjimharris
263293675Sjimharris	fmhp = sc->head << 16;
264293675Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MCTRL);
265293675Sjimharris	val &= ~ISMT_MCTRL_FMHP;
266293675Sjimharris	val |= fmhp;
267293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MCTRL, val);
268293675Sjimharris
269293675Sjimharris	/* set the start bit */
270293675Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MCTRL);
271293675Sjimharris	val |= ISMT_MCTRL_SS;
272293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MCTRL, val);
273293675Sjimharris
274293675Sjimharris	err = tsleep(sc, PWAIT, "ismt_wait", 5 * hz);
275293675Sjimharris
276293675Sjimharris	if (err != 0) {
277293675Sjimharris		ISMT_DEBUG(sc->pcidev, "%s timeout\n", __func__);
278293675Sjimharris		return (SMB_ETIMEOUT);
279293675Sjimharris	}
280293675Sjimharris
281293675Sjimharris	ISMT_DEBUG(sc->pcidev, "%s status=0x%x\n", __func__, desc->status);
282293675Sjimharris
283293675Sjimharris	if (desc->status & ISMT_DESC_SCS)
284293675Sjimharris		return (SMB_ENOERR);
285293675Sjimharris
286293675Sjimharris	if (desc->status & ISMT_DESC_NAK)
287293675Sjimharris		return (SMB_ENOACK);
288293675Sjimharris
289293675Sjimharris	if (desc->status & ISMT_DESC_CRC)
290293675Sjimharris		return (SMB_EBUSERR);
291293675Sjimharris
292293675Sjimharris	if (desc->status & ISMT_DESC_COL)
293293675Sjimharris		return (SMB_ECOLLI);
294293675Sjimharris
295293675Sjimharris	if (desc->status & ISMT_DESC_LPR)
296293675Sjimharris		return (SMB_EINVAL);
297293675Sjimharris
298293675Sjimharris	if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO))
299293675Sjimharris		return (SMB_ETIMEOUT);
300293675Sjimharris
301293675Sjimharris	return (SMB_EBUSERR);
302293675Sjimharris}
303293675Sjimharris
304293675Sjimharris
305293675Sjimharrisstatic int
306293675Sjimharrisismt_quick(device_t dev, u_char slave, int how)
307293675Sjimharris{
308293675Sjimharris	struct ismt_desc	*desc;
309293675Sjimharris	struct ismt_softc	*sc;
310293675Sjimharris	int			is_read;
311293675Sjimharris
312293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
313293675Sjimharris
314293675Sjimharris	if (how != SMB_QREAD && how != SMB_QWRITE) {
315293675Sjimharris		return (SMB_ENOTSUPP);
316293675Sjimharris	}
317293675Sjimharris
318293675Sjimharris	sc = device_get_softc(dev);
319293675Sjimharris	desc = ismt_alloc_desc(sc);
320293675Sjimharris	is_read = (how == SMB_QREAD ? 1 : 0);
321293675Sjimharris	return (ismt_submit(sc, desc, slave, is_read));
322293675Sjimharris}
323293675Sjimharris
324293675Sjimharrisstatic int
325293675Sjimharrisismt_sendb(device_t dev, u_char slave, char byte)
326293675Sjimharris{
327293675Sjimharris	struct ismt_desc	*desc;
328293675Sjimharris	struct ismt_softc	*sc;
329293675Sjimharris
330293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
331293675Sjimharris
332293675Sjimharris	sc = device_get_softc(dev);
333293675Sjimharris	desc = ismt_alloc_desc(sc);
334293675Sjimharris	desc->control = ISMT_DESC_CWRL;
335293675Sjimharris	desc->wr_len_cmd = byte;
336293675Sjimharris
337293675Sjimharris	return (ismt_submit(sc, desc, slave, 0));
338293675Sjimharris}
339293675Sjimharris
340293675Sjimharrisstatic int
341293675Sjimharrisismt_recvb(device_t dev, u_char slave, char *byte)
342293675Sjimharris{
343293675Sjimharris	struct ismt_desc	*desc;
344293675Sjimharris	struct ismt_softc	*sc;
345293675Sjimharris	int			err;
346293675Sjimharris
347293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
348293675Sjimharris
349293675Sjimharris	sc = device_get_softc(dev);
350293675Sjimharris	desc = ismt_alloc_desc(sc);
351293675Sjimharris	desc->rd_len = 1;
352293675Sjimharris
353293675Sjimharris	err = ismt_submit(sc, desc, slave, 1);
354293675Sjimharris
355293675Sjimharris	if (err != SMB_ENOERR)
356293675Sjimharris		return (err);
357293675Sjimharris
358293675Sjimharris	*byte = sc->dma_buffer[0];
359293675Sjimharris
360293675Sjimharris	return (err);
361293675Sjimharris}
362293675Sjimharris
363293675Sjimharrisstatic int
364293675Sjimharrisismt_writeb(device_t dev, u_char slave, char cmd, char byte)
365293675Sjimharris{
366293675Sjimharris	struct ismt_desc	*desc;
367293675Sjimharris	struct ismt_softc	*sc;
368293675Sjimharris
369293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
370293675Sjimharris
371293675Sjimharris	sc = device_get_softc(dev);
372293675Sjimharris	desc = ismt_alloc_desc(sc);
373293675Sjimharris	desc->wr_len_cmd = 2;
374293675Sjimharris	sc->dma_buffer[0] = cmd;
375293675Sjimharris	sc->dma_buffer[1] = byte;
376293675Sjimharris
377293675Sjimharris	return (ismt_submit(sc, desc, slave, 0));
378293675Sjimharris}
379293675Sjimharris
380293675Sjimharrisstatic int
381293675Sjimharrisismt_writew(device_t dev, u_char slave, char cmd, short word)
382293675Sjimharris{
383293675Sjimharris	struct ismt_desc	*desc;
384293675Sjimharris	struct ismt_softc	*sc;
385293675Sjimharris
386293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
387293675Sjimharris
388293675Sjimharris	sc = device_get_softc(dev);
389293675Sjimharris	desc = ismt_alloc_desc(sc);
390293675Sjimharris	desc->wr_len_cmd = 3;
391293675Sjimharris	sc->dma_buffer[0] = cmd;
392293675Sjimharris	sc->dma_buffer[1] = word & 0xFF;
393293675Sjimharris	sc->dma_buffer[2] = word >> 8;
394293675Sjimharris
395293675Sjimharris	return (ismt_submit(sc, desc, slave, 0));
396293675Sjimharris}
397293675Sjimharris
398293675Sjimharrisstatic int
399293675Sjimharrisismt_readb(device_t dev, u_char slave, char cmd, char *byte)
400293675Sjimharris{
401293675Sjimharris	struct ismt_desc	*desc;
402293675Sjimharris	struct ismt_softc	*sc;
403293675Sjimharris	int			err;
404293675Sjimharris
405293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
406293675Sjimharris
407293675Sjimharris	sc = device_get_softc(dev);
408293675Sjimharris	desc = ismt_alloc_desc(sc);
409293675Sjimharris	desc->control = ISMT_DESC_CWRL;
410293675Sjimharris	desc->wr_len_cmd = cmd;
411293675Sjimharris	desc->rd_len = 1;
412293675Sjimharris
413293675Sjimharris	err = ismt_submit(sc, desc, slave, 1);
414293675Sjimharris
415293675Sjimharris	if (err != SMB_ENOERR)
416293675Sjimharris		return (err);
417293675Sjimharris
418293675Sjimharris	*byte = sc->dma_buffer[0];
419293675Sjimharris
420293675Sjimharris	return (err);
421293675Sjimharris}
422293675Sjimharris
423293675Sjimharrisstatic int
424293675Sjimharrisismt_readw(device_t dev, u_char slave, char cmd, short *word)
425293675Sjimharris{
426293675Sjimharris	struct ismt_desc	*desc;
427293675Sjimharris	struct ismt_softc	*sc;
428293675Sjimharris	int			err;
429293675Sjimharris
430293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
431293675Sjimharris
432293675Sjimharris	sc = device_get_softc(dev);
433293675Sjimharris	desc = ismt_alloc_desc(sc);
434293675Sjimharris	desc->control = ISMT_DESC_CWRL;
435293675Sjimharris	desc->wr_len_cmd = cmd;
436293675Sjimharris	desc->rd_len = 2;
437293675Sjimharris
438293675Sjimharris	err = ismt_submit(sc, desc, slave, 1);
439293675Sjimharris
440293675Sjimharris	if (err != SMB_ENOERR)
441293675Sjimharris		return (err);
442293675Sjimharris
443293675Sjimharris	*word = sc->dma_buffer[0] | (sc->dma_buffer[1] << 8);
444293675Sjimharris
445293675Sjimharris	return (err);
446293675Sjimharris}
447293675Sjimharris
448293675Sjimharrisstatic int
449293675Sjimharrisismt_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
450293675Sjimharris{
451293675Sjimharris	struct ismt_desc	*desc;
452293675Sjimharris	struct ismt_softc	*sc;
453293675Sjimharris	int			err;
454293675Sjimharris
455293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
456293675Sjimharris
457293675Sjimharris	sc = device_get_softc(dev);
458293675Sjimharris	desc = ismt_alloc_desc(sc);
459293675Sjimharris	desc->wr_len_cmd = 3;
460293675Sjimharris	desc->rd_len = 2;
461293675Sjimharris	sc->dma_buffer[0] = cmd;
462293675Sjimharris	sc->dma_buffer[1] = sdata & 0xff;
463293675Sjimharris	sc->dma_buffer[2] = sdata >> 8;
464293675Sjimharris
465293675Sjimharris	err = ismt_submit(sc, desc, slave, 0);
466293675Sjimharris
467293675Sjimharris	if (err != SMB_ENOERR)
468293675Sjimharris		return (err);
469293675Sjimharris
470293675Sjimharris	*rdata = sc->dma_buffer[0] | (sc->dma_buffer[1] << 8);
471293675Sjimharris
472293675Sjimharris	return (err);
473293675Sjimharris}
474293675Sjimharris
475293675Sjimharrisstatic int
476293675Sjimharrisismt_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
477293675Sjimharris{
478293675Sjimharris	struct ismt_desc	*desc;
479293675Sjimharris	struct ismt_softc	*sc;
480293675Sjimharris
481293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
482293675Sjimharris
483293675Sjimharris	if (count == 0 || count > ISMT_MAX_BLOCK_SIZE)
484293675Sjimharris		return (SMB_EINVAL);
485293675Sjimharris
486293675Sjimharris	sc = device_get_softc(dev);
487293675Sjimharris	desc = ismt_alloc_desc(sc);
488293675Sjimharris	desc->control = ISMT_DESC_I2C;
489293675Sjimharris	desc->wr_len_cmd = count + 1;
490293675Sjimharris	sc->dma_buffer[0] = cmd;
491293675Sjimharris	memcpy(&sc->dma_buffer[1], buf, count);
492293675Sjimharris
493293675Sjimharris	return (ismt_submit(sc, desc, slave, 0));
494293675Sjimharris}
495293675Sjimharris
496293675Sjimharrisstatic int
497293675Sjimharrisismt_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
498293675Sjimharris{
499293675Sjimharris	struct ismt_desc	*desc;
500293675Sjimharris	struct ismt_softc	*sc;
501293675Sjimharris	int			err;
502293675Sjimharris
503293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
504293675Sjimharris
505293675Sjimharris	if (*count == 0 || *count > ISMT_MAX_BLOCK_SIZE)
506293675Sjimharris		return (SMB_EINVAL);
507293675Sjimharris
508293675Sjimharris	sc = device_get_softc(dev);
509293675Sjimharris	desc = ismt_alloc_desc(sc);
510293675Sjimharris	desc->control = ISMT_DESC_I2C | ISMT_DESC_CWRL;
511293675Sjimharris	desc->wr_len_cmd = cmd;
512293675Sjimharris	desc->rd_len = *count;
513293675Sjimharris
514293675Sjimharris	err = ismt_submit(sc, desc, slave, 0);
515293675Sjimharris
516293675Sjimharris	if (err != SMB_ENOERR)
517293675Sjimharris		return (err);
518293675Sjimharris
519293675Sjimharris	memcpy(buf, sc->dma_buffer, desc->rxbytes);
520293675Sjimharris	*count = desc->rxbytes;
521293675Sjimharris
522293675Sjimharris	return (err);
523293675Sjimharris}
524293675Sjimharris
525293675Sjimharrisstatic int
526293675Sjimharrisismt_detach(device_t dev)
527293675Sjimharris{
528293675Sjimharris	struct ismt_softc	*sc;
529293675Sjimharris	int			error;
530293675Sjimharris
531293675Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
532293675Sjimharris	sc = device_get_softc(dev);
533293675Sjimharris
534293675Sjimharris	error = bus_generic_detach(dev);
535293675Sjimharris	if (error)
536293675Sjimharris		return (error);
537293675Sjimharris
538293675Sjimharris	device_delete_child(dev, sc->smbdev);
539293675Sjimharris
540293675Sjimharris	if (sc->intr_handle != NULL) {
541293675Sjimharris		bus_teardown_intr(dev, sc->intr_res, sc->intr_handle);
542293675Sjimharris		sc->intr_handle = NULL;
543293675Sjimharris	}
544293675Sjimharris	if (sc->intr_res != NULL) {
545293675Sjimharris		bus_release_resource(dev,
546293675Sjimharris		    SYS_RES_IRQ, sc->intr_rid, sc->intr_res);
547293675Sjimharris		sc->intr_res = NULL;
548293675Sjimharris	}
549293675Sjimharris	if (sc->using_msi == 1)
550293675Sjimharris		pci_release_msi(dev);
551293675Sjimharris
552293675Sjimharris	if (sc->mmio_res != NULL) {
553293675Sjimharris		bus_release_resource(dev,
554293675Sjimharris		    SYS_RES_MEMORY, sc->mmio_rid, sc->mmio_res);
555293675Sjimharris		sc->mmio_res = NULL;
556293675Sjimharris	}
557293675Sjimharris
558293675Sjimharris	bus_dmamap_unload(sc->desc_dma_tag, sc->desc_dma_map);
559293675Sjimharris	bus_dmamap_unload(sc->dma_buffer_dma_tag, sc->dma_buffer_dma_map);
560293675Sjimharris
561293675Sjimharris	bus_dmamem_free(sc->desc_dma_tag, sc->desc,
562293675Sjimharris	    sc->desc_dma_map);
563293675Sjimharris	bus_dmamem_free(sc->dma_buffer_dma_tag, sc->dma_buffer,
564293675Sjimharris	    sc->dma_buffer_dma_map);
565293675Sjimharris
566293675Sjimharris	bus_dma_tag_destroy(sc->desc_dma_tag);
567293675Sjimharris	bus_dma_tag_destroy(sc->dma_buffer_dma_tag);
568293675Sjimharris
569293675Sjimharris	pci_disable_busmaster(dev);
570293675Sjimharris
571293675Sjimharris	return 0;
572293675Sjimharris}
573293675Sjimharris
574293675Sjimharrisstatic void
575293675Sjimharrisismt_single_map(void *arg, bus_dma_segment_t *seg, int nseg, int error)
576293675Sjimharris{
577293675Sjimharris	uint64_t *bus_addr = (uint64_t *)arg;
578293675Sjimharris
579293675Sjimharris	KASSERT(error == 0, ("%s: error=%d\n", __func__, error));
580293675Sjimharris	KASSERT(nseg == 1, ("%s: nseg=%d\n", __func__, nseg));
581293675Sjimharris
582293675Sjimharris	*bus_addr = seg[0].ds_addr;
583293675Sjimharris}
584293675Sjimharris
585293675Sjimharrisstatic int
586293675Sjimharrisismt_attach(device_t dev)
587293675Sjimharris{
588293675Sjimharris	struct ismt_softc *sc = device_get_softc(dev);
589293675Sjimharris	int err, num_vectors, val;
590293675Sjimharris
591293675Sjimharris	sc->pcidev = dev;
592293675Sjimharris	pci_enable_busmaster(dev);
593293675Sjimharris
594293675Sjimharris	if ((sc->smbdev = device_add_child(dev, "smbus", -1)) == NULL) {
595293675Sjimharris		device_printf(dev, "no smbus child found\n");
596293675Sjimharris		err = ENXIO;
597293675Sjimharris		goto fail;
598293675Sjimharris	}
599293675Sjimharris
600293675Sjimharris	sc->mmio_rid = PCIR_BAR(0);
601293675Sjimharris	sc->mmio_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
602293675Sjimharris	    &sc->mmio_rid, RF_ACTIVE);
603293675Sjimharris	if (sc->mmio_res == NULL) {
604293675Sjimharris		device_printf(dev, "cannot allocate mmio region\n");
605293675Sjimharris		err = ENOMEM;
606293675Sjimharris		goto fail;
607293675Sjimharris	}
608293675Sjimharris
609293675Sjimharris	sc->mmio_tag = rman_get_bustag(sc->mmio_res);
610293675Sjimharris	sc->mmio_handle = rman_get_bushandle(sc->mmio_res);
611293675Sjimharris
612293675Sjimharris	/* Attach "smbus" child */
613293675Sjimharris	if ((err = bus_generic_attach(dev)) != 0) {
614293675Sjimharris		device_printf(dev, "failed to attach child: %d\n", err);
615293675Sjimharris		err = ENXIO;
616293675Sjimharris		goto fail;
617293675Sjimharris	}
618293675Sjimharris
619293675Sjimharris	bus_dma_tag_create(bus_get_dma_tag(dev), 4, PAGE_SIZE,
620293675Sjimharris	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
621293675Sjimharris	    DESC_SIZE, 1, DESC_SIZE,
622293675Sjimharris	    0, NULL, NULL, &sc->desc_dma_tag);
623293675Sjimharris
624293675Sjimharris	bus_dma_tag_create(bus_get_dma_tag(dev), 4, PAGE_SIZE,
625293675Sjimharris	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
626293675Sjimharris	    DMA_BUFFER_SIZE, 1, DMA_BUFFER_SIZE,
627293675Sjimharris	    0, NULL, NULL, &sc->dma_buffer_dma_tag);
628293675Sjimharris
629293675Sjimharris	bus_dmamap_create(sc->desc_dma_tag, 0,
630293675Sjimharris	    &sc->desc_dma_map);
631293675Sjimharris	bus_dmamap_create(sc->dma_buffer_dma_tag, 0,
632293675Sjimharris	    &sc->dma_buffer_dma_map);
633293675Sjimharris
634293675Sjimharris	bus_dmamem_alloc(sc->desc_dma_tag,
635293675Sjimharris	    (void **)&sc->desc, BUS_DMA_WAITOK,
636293675Sjimharris	    &sc->desc_dma_map);
637293675Sjimharris	bus_dmamem_alloc(sc->dma_buffer_dma_tag,
638293675Sjimharris	    (void **)&sc->dma_buffer, BUS_DMA_WAITOK,
639293675Sjimharris	    &sc->dma_buffer_dma_map);
640293675Sjimharris
641293675Sjimharris	bus_dmamap_load(sc->desc_dma_tag,
642293675Sjimharris	    sc->desc_dma_map, sc->desc, DESC_SIZE,
643293675Sjimharris	    ismt_single_map, &sc->desc_bus_addr, 0);
644293675Sjimharris	bus_dmamap_load(sc->dma_buffer_dma_tag,
645293675Sjimharris	    sc->dma_buffer_dma_map, sc->dma_buffer, DMA_BUFFER_SIZE,
646293675Sjimharris	    ismt_single_map, &sc->dma_buffer_bus_addr, 0);
647293675Sjimharris
648293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MDBA,
649293675Sjimharris	    (sc->desc_bus_addr & 0xFFFFFFFFLL));
650293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MDBA + 4,
651293675Sjimharris	    (sc->desc_bus_addr >> 32));
652293675Sjimharris
653293675Sjimharris	/* initialize the Master Control Register (MCTRL) */
654293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MCTRL, ISMT_MCTRL_MEIE);
655293675Sjimharris
656293675Sjimharris	/* initialize the Master Status Register (MSTS) */
657293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MSTS, 0);
658293675Sjimharris
659293675Sjimharris	/* initialize the Master Descriptor Size (MDS) */
660293675Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MDS);
661293675Sjimharris	val &= ~ISMT_MDS_MASK;
662293675Sjimharris	val |= (ISMT_DESC_ENTRIES - 1);
663293675Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MDS, val);
664293675Sjimharris
665293675Sjimharris	sc->using_msi = 1;
666293675Sjimharris
667293675Sjimharris	if (pci_msi_count(dev) == 0) {
668293675Sjimharris		sc->using_msi = 0;
669293675Sjimharris		goto intx;
670293675Sjimharris	}
671293675Sjimharris
672293675Sjimharris	num_vectors = 1;
673293675Sjimharris	if (pci_alloc_msi(dev, &num_vectors) != 0) {
674293675Sjimharris		sc->using_msi = 0;
675293675Sjimharris		goto intx;
676293675Sjimharris	}
677293675Sjimharris
678293675Sjimharris	sc->intr_rid = 1;
679293675Sjimharris	sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
680293675Sjimharris	    &sc->intr_rid, RF_ACTIVE);
681293675Sjimharris
682293675Sjimharris	if (sc->intr_res == NULL) {
683293675Sjimharris		sc->using_msi = 0;
684293675Sjimharris		pci_release_msi(dev);
685293675Sjimharris	}
686293675Sjimharris
687293675Sjimharrisintx:
688293675Sjimharris	if (sc->using_msi == 0) {
689293675Sjimharris		sc->intr_rid = 0;
690293675Sjimharris		sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
691293675Sjimharris		    &sc->intr_rid, RF_SHAREABLE | RF_ACTIVE);
692293675Sjimharris		if (sc->intr_res == NULL) {
693293675Sjimharris			device_printf(dev, "cannot allocate irq\n");
694293675Sjimharris			err = ENXIO;
695293675Sjimharris			goto fail;
696293675Sjimharris		}
697293675Sjimharris	}
698293675Sjimharris
699293675Sjimharris	ISMT_DEBUG(dev, "using_msi = %d\n", sc->using_msi);
700293675Sjimharris
701293675Sjimharris	err = bus_setup_intr(dev, sc->intr_res,
702293675Sjimharris	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, ismt_intr, sc,
703293675Sjimharris	    &sc->intr_handle);
704293675Sjimharris	if (err != 0) {
705293675Sjimharris		device_printf(dev, "cannot setup interrupt\n");
706293675Sjimharris		err = ENXIO;
707293675Sjimharris		goto fail;
708293675Sjimharris	}
709293675Sjimharris
710293675Sjimharris	return (0);
711293675Sjimharris
712293675Sjimharrisfail:
713293675Sjimharris	ismt_detach(dev);
714293675Sjimharris	return (err);
715293675Sjimharris}
716293675Sjimharris
717293675Sjimharris#define ID_INTEL_S1200_SMT0		0x0c598086
718293675Sjimharris#define ID_INTEL_S1200_SMT1		0x0c5a8086
719293675Sjimharris#define ID_INTEL_C2000_SMT		0x1f158086
720293675Sjimharris
721293675Sjimharrisstatic int
722293675Sjimharrisismt_probe(device_t dev)
723293675Sjimharris{
724293675Sjimharris	const char *desc;
725293675Sjimharris
726293675Sjimharris	switch (pci_get_devid(dev)) {
727293675Sjimharris	case ID_INTEL_S1200_SMT0:
728293675Sjimharris		desc = "Atom Processor S1200 SMBus 2.0 Controller 0";
729293675Sjimharris		break;
730293675Sjimharris	case ID_INTEL_S1200_SMT1:
731293675Sjimharris		desc = "Atom Processor S1200 SMBus 2.0 Controller 1";
732293675Sjimharris		break;
733293675Sjimharris	case ID_INTEL_C2000_SMT:
734293675Sjimharris		desc = "Atom Processor C2000 SMBus 2.0";
735293675Sjimharris		break;
736293675Sjimharris	default:
737293675Sjimharris		return (ENXIO);
738293675Sjimharris	}
739293675Sjimharris
740293675Sjimharris	device_set_desc(dev, desc);
741293675Sjimharris	return (BUS_PROBE_DEFAULT);
742293675Sjimharris}
743293675Sjimharris
744293675Sjimharris/* Device methods */
745293675Sjimharrisstatic device_method_t ismt_pci_methods[] = {
746293675Sjimharris        DEVMETHOD(device_probe,		ismt_probe),
747293675Sjimharris        DEVMETHOD(device_attach,	ismt_attach),
748293675Sjimharris        DEVMETHOD(device_detach,	ismt_detach),
749293675Sjimharris
750293675Sjimharris        DEVMETHOD(smbus_callback,	ismt_callback),
751293675Sjimharris        DEVMETHOD(smbus_quick,		ismt_quick),
752293675Sjimharris        DEVMETHOD(smbus_sendb,		ismt_sendb),
753293675Sjimharris        DEVMETHOD(smbus_recvb,		ismt_recvb),
754293675Sjimharris        DEVMETHOD(smbus_writeb,		ismt_writeb),
755293675Sjimharris        DEVMETHOD(smbus_writew,		ismt_writew),
756293675Sjimharris        DEVMETHOD(smbus_readb,		ismt_readb),
757293675Sjimharris        DEVMETHOD(smbus_readw,		ismt_readw),
758293675Sjimharris        DEVMETHOD(smbus_pcall,		ismt_pcall),
759293675Sjimharris        DEVMETHOD(smbus_bwrite,		ismt_bwrite),
760293675Sjimharris        DEVMETHOD(smbus_bread,		ismt_bread),
761293675Sjimharris
762293675Sjimharris	DEVMETHOD_END
763293675Sjimharris};
764293675Sjimharris
765293675Sjimharrisstatic driver_t ismt_pci_driver = {
766293675Sjimharris	"ismt",
767293675Sjimharris	ismt_pci_methods,
768293675Sjimharris	sizeof(struct ismt_softc)
769293675Sjimharris};
770293675Sjimharris
771293675Sjimharrisstatic devclass_t ismt_pci_devclass;
772293675Sjimharris
773293675SjimharrisDRIVER_MODULE(ismt, pci, ismt_pci_driver, ismt_pci_devclass, 0, 0);
774293675SjimharrisDRIVER_MODULE(smbus, ismt, smbus_driver, smbus_devclass, 0, 0);
775293675Sjimharris
776293675SjimharrisMODULE_DEPEND(ismt, pci, 1, 1, 1);
777293675SjimharrisMODULE_DEPEND(ismt, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
778293675SjimharrisMODULE_VERSION(ismt, 1);
779