1266474Sjimharris/*-
2266474Sjimharris * Copyright (C) 2014 Intel Corporation
3266474Sjimharris * All rights reserved.
4266474Sjimharris *
5266474Sjimharris * Redistribution and use in source and binary forms, with or without
6266474Sjimharris * modification, are permitted provided that the following conditions
7266474Sjimharris * are met:
8266474Sjimharris * 1. Redistributions of source code must retain the above copyright
9266474Sjimharris *    notice, this list of conditions and the following disclaimer.
10266474Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
11266474Sjimharris *    notice, this list of conditions and the following disclaimer in the
12266474Sjimharris *    documentation and/or other materials provided with the distribution.
13266474Sjimharris * 3. Neither the name of Intel Corporation nor the names of its
14266474Sjimharris *    contributors may be used to endorse or promote products derived from
15266474Sjimharris *    this software without specific prior written permission.
16266474Sjimharris *
17266474Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18266474Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19266474Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20266474Sjimharris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21266474Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22266474Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23266474Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24266474Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25266474Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26266474Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27266474Sjimharris * SUCH DAMAGE.
28266474Sjimharris */
29266474Sjimharris
30266474Sjimharris#include <sys/cdefs.h>
31266474Sjimharris__FBSDID("$FreeBSD: stable/11/sys/dev/ismt/ismt.c 359360 2020-03-27 15:20:19Z jhibbits $");
32266474Sjimharris
33266474Sjimharris#include <sys/param.h>
34266474Sjimharris#include <sys/systm.h>
35266474Sjimharris#include <sys/bus.h>
36266474Sjimharris#include <sys/errno.h>
37266474Sjimharris#include <sys/kernel.h>
38266474Sjimharris#include <sys/lock.h>
39266474Sjimharris#include <sys/module.h>
40266474Sjimharris#include <sys/priority.h>
41266474Sjimharris#include <sys/proc.h>
42266474Sjimharris#include <sys/syslog.h>
43266474Sjimharris
44266474Sjimharris#include <machine/bus.h>
45266474Sjimharris#include <sys/rman.h>
46266474Sjimharris#include <machine/resource.h>
47266474Sjimharris
48266474Sjimharris#include <dev/pci/pcireg.h>
49266474Sjimharris#include <dev/pci/pcivar.h>
50266474Sjimharris#include <dev/smbus/smbconf.h>
51266474Sjimharris
52266474Sjimharris#include "smbus_if.h"
53266474Sjimharris
54266474Sjimharris#define ISMT_DESC_ENTRIES	32
55266474Sjimharris
56266474Sjimharris/* Hardware Descriptor Constants - Control Field */
57266474Sjimharris#define ISMT_DESC_CWRL	0x01	/* Command/Write Length */
58266474Sjimharris#define ISMT_DESC_BLK	0X04	/* Perform Block Transaction */
59266474Sjimharris#define ISMT_DESC_FAIR	0x08	/* Set fairness flag upon successful arbit. */
60266474Sjimharris#define ISMT_DESC_PEC	0x10	/* Packet Error Code */
61266474Sjimharris#define ISMT_DESC_I2C	0x20	/* I2C Enable */
62266474Sjimharris#define ISMT_DESC_INT	0x40	/* Interrupt */
63266474Sjimharris#define ISMT_DESC_SOE	0x80	/* Stop On Error */
64266474Sjimharris
65266474Sjimharris/* Hardware Descriptor Constants - Status Field */
66266474Sjimharris#define ISMT_DESC_SCS	0x01	/* Success */
67266474Sjimharris#define ISMT_DESC_DLTO	0x04	/* Data Low Time Out */
68266474Sjimharris#define ISMT_DESC_NAK	0x08	/* NAK Received */
69266474Sjimharris#define ISMT_DESC_CRC	0x10	/* CRC Error */
70266474Sjimharris#define ISMT_DESC_CLTO	0x20	/* Clock Low Time Out */
71266474Sjimharris#define ISMT_DESC_COL	0x40	/* Collisions */
72266474Sjimharris#define ISMT_DESC_LPR	0x80	/* Large Packet Received */
73266474Sjimharris
74266474Sjimharris/* Macros */
75359360Sjhibbits#define ISMT_DESC_ADDR_RW(addr, is_read) ((addr) | (is_read))
76266474Sjimharris
77266474Sjimharris/* iSMT General Register address offsets (SMBBAR + <addr>) */
78266474Sjimharris#define ISMT_GR_GCTRL		0x000	/* General Control */
79266474Sjimharris#define ISMT_GR_SMTICL		0x008	/* SMT Interrupt Cause Location */
80266474Sjimharris#define ISMT_GR_ERRINTMSK	0x010	/* Error Interrupt Mask */
81266474Sjimharris#define ISMT_GR_ERRAERMSK	0x014	/* Error AER Mask */
82266474Sjimharris#define ISMT_GR_ERRSTS		0x018	/* Error Status */
83266474Sjimharris#define ISMT_GR_ERRINFO		0x01c	/* Error Information */
84266474Sjimharris
85266474Sjimharris/* iSMT Master Registers */
86266474Sjimharris#define ISMT_MSTR_MDBA		0x100	/* Master Descriptor Base Address */
87266474Sjimharris#define ISMT_MSTR_MCTRL		0x108	/* Master Control */
88266474Sjimharris#define ISMT_MSTR_MSTS		0x10c	/* Master Status */
89266474Sjimharris#define ISMT_MSTR_MDS		0x110	/* Master Descriptor Size */
90266474Sjimharris#define ISMT_MSTR_RPOLICY	0x114	/* Retry Policy */
91266474Sjimharris
92266474Sjimharris/* iSMT Miscellaneous Registers */
93266474Sjimharris#define ISMT_SPGT	0x300	/* SMBus PHY Global Timing */
94266474Sjimharris
95266474Sjimharris/* General Control Register (GCTRL) bit definitions */
96266474Sjimharris#define ISMT_GCTRL_TRST	0x04	/* Target Reset */
97266474Sjimharris#define ISMT_GCTRL_KILL	0x08	/* Kill */
98266474Sjimharris#define ISMT_GCTRL_SRST	0x40	/* Soft Reset */
99266474Sjimharris
100266474Sjimharris/* Master Control Register (MCTRL) bit definitions */
101266474Sjimharris#define ISMT_MCTRL_SS	0x01		/* Start/Stop */
102266474Sjimharris#define ISMT_MCTRL_MEIE	0x10		/* Master Error Interrupt Enable */
103266474Sjimharris#define ISMT_MCTRL_FMHP	0x00ff0000	/* Firmware Master Head Ptr (FMHP) */
104266474Sjimharris
105266474Sjimharris/* Master Status Register (MSTS) bit definitions */
106266474Sjimharris#define ISMT_MSTS_HMTP	0xff0000	/* HW Master Tail Pointer (HMTP) */
107266474Sjimharris#define ISMT_MSTS_MIS	0x20		/* Master Interrupt Status (MIS) */
108266474Sjimharris#define ISMT_MSTS_MEIS	0x10		/* Master Error Int Status (MEIS) */
109266474Sjimharris#define ISMT_MSTS_IP	0x01		/* In Progress */
110266474Sjimharris
111266474Sjimharris/* Master Descriptor Size (MDS) bit definitions */
112266474Sjimharris#define ISMT_MDS_MASK	0xff	/* Master Descriptor Size mask (MDS) */
113266474Sjimharris
114266474Sjimharris/* SMBus PHY Global Timing Register (SPGT) bit definitions */
115266474Sjimharris#define ISMT_SPGT_SPD_MASK	0xc0000000	/* SMBus Speed mask */
116266474Sjimharris#define ISMT_SPGT_SPD_80K	0x00		/* 80 kHz */
117266474Sjimharris#define ISMT_SPGT_SPD_100K	(0x1 << 30)	/* 100 kHz */
118266474Sjimharris#define ISMT_SPGT_SPD_400K	(0x2 << 30)	/* 400 kHz */
119266474Sjimharris#define ISMT_SPGT_SPD_1M	(0x3 << 30)	/* 1 MHz */
120266474Sjimharris
121266474Sjimharris/* MSI Control Register (MSICTL) bit definitions */
122266474Sjimharris#define ISMT_MSICTL_MSIE	0x01	/* MSI Enable */
123266474Sjimharris
124266474Sjimharris#define ISMT_MAX_BLOCK_SIZE	32 /* per SMBus spec */
125266474Sjimharris
126266474Sjimharris//#define ISMT_DEBUG	device_printf
127266474Sjimharris#ifndef ISMT_DEBUG
128266474Sjimharris#define ISMT_DEBUG(...)
129266474Sjimharris#endif
130266474Sjimharris
131266474Sjimharris/* iSMT Hardware Descriptor */
132266474Sjimharrisstruct ismt_desc {
133266474Sjimharris	uint8_t tgtaddr_rw;	/* target address & r/w bit */
134266474Sjimharris	uint8_t wr_len_cmd;	/* write length in bytes or a command */
135266474Sjimharris	uint8_t rd_len;		/* read length */
136266474Sjimharris	uint8_t control;	/* control bits */
137266474Sjimharris	uint8_t status;		/* status bits */
138266474Sjimharris	uint8_t retry;		/* collision retry and retry count */
139266474Sjimharris	uint8_t rxbytes;	/* received bytes */
140266474Sjimharris	uint8_t txbytes;	/* transmitted bytes */
141266474Sjimharris	uint32_t dptr_low;	/* lower 32 bit of the data pointer */
142266474Sjimharris	uint32_t dptr_high;	/* upper 32 bit of the data pointer */
143266474Sjimharris} __packed;
144266474Sjimharris
145266474Sjimharris#define DESC_SIZE	(ISMT_DESC_ENTRIES * sizeof(struct ismt_desc))
146266474Sjimharris
147266474Sjimharris#define DMA_BUFFER_SIZE	64
148266474Sjimharris
149266474Sjimharrisstruct ismt_softc {
150266474Sjimharris	device_t		pcidev;
151266474Sjimharris	device_t		smbdev;
152266474Sjimharris
153266474Sjimharris	struct thread		*bus_reserved;
154266474Sjimharris
155266474Sjimharris	int			intr_rid;
156266474Sjimharris	struct resource		*intr_res;
157266474Sjimharris	void			*intr_handle;
158266474Sjimharris
159266474Sjimharris	bus_space_tag_t		mmio_tag;
160266474Sjimharris	bus_space_handle_t	mmio_handle;
161266474Sjimharris	int			mmio_rid;
162266474Sjimharris	struct resource		*mmio_res;
163266474Sjimharris
164266474Sjimharris	uint8_t			head;
165266474Sjimharris
166266474Sjimharris	struct ismt_desc	*desc;
167266474Sjimharris	bus_dma_tag_t		desc_dma_tag;
168266474Sjimharris	bus_dmamap_t		desc_dma_map;
169266474Sjimharris	uint64_t		desc_bus_addr;
170266474Sjimharris
171266474Sjimharris	uint8_t			*dma_buffer;
172266474Sjimharris	bus_dma_tag_t		dma_buffer_dma_tag;
173266474Sjimharris	bus_dmamap_t		dma_buffer_dma_map;
174266474Sjimharris	uint64_t		dma_buffer_bus_addr;
175266474Sjimharris
176266474Sjimharris	uint8_t			using_msi;
177266474Sjimharris};
178266474Sjimharris
179266474Sjimharrisstatic void
180266474Sjimharrisismt_intr(void *arg)
181266474Sjimharris{
182266474Sjimharris	struct ismt_softc *sc = arg;
183266474Sjimharris	uint32_t val;
184266474Sjimharris
185266474Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MSTS);
186266474Sjimharris	ISMT_DEBUG(sc->pcidev, "%s MSTS=0x%x\n", __func__, val);
187266474Sjimharris
188266474Sjimharris	val |= (ISMT_MSTS_MIS | ISMT_MSTS_MEIS);
189266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MSTS, val);
190266474Sjimharris
191266474Sjimharris	wakeup(sc);
192266474Sjimharris}
193266474Sjimharris
194266474Sjimharrisstatic int
195266474Sjimharrisismt_callback(device_t dev, int index, void *data)
196266474Sjimharris{
197266474Sjimharris	struct ismt_softc	*sc;
198266474Sjimharris	int			acquired, err;
199266474Sjimharris
200266474Sjimharris	sc = device_get_softc(dev);
201266474Sjimharris
202266474Sjimharris	switch (index) {
203266474Sjimharris	case SMB_REQUEST_BUS:
204266474Sjimharris		acquired = atomic_cmpset_ptr(
205266474Sjimharris		    (uintptr_t *)&sc->bus_reserved,
206266474Sjimharris		    (uintptr_t)NULL, (uintptr_t)curthread);
207266474Sjimharris		ISMT_DEBUG(dev, "SMB_REQUEST_BUS acquired=%d\n", acquired);
208266474Sjimharris		if (acquired)
209266474Sjimharris			err = 0;
210266474Sjimharris		else
211266474Sjimharris			err = EWOULDBLOCK;
212266474Sjimharris		break;
213266474Sjimharris	case SMB_RELEASE_BUS:
214266474Sjimharris		KASSERT(sc->bus_reserved == curthread,
215266474Sjimharris		    ("SMB_RELEASE_BUS called by wrong thread\n"));
216266474Sjimharris		ISMT_DEBUG(dev, "SMB_RELEASE_BUS\n");
217266474Sjimharris		atomic_store_rel_ptr((uintptr_t *)&sc->bus_reserved,
218266474Sjimharris		    (uintptr_t)NULL);
219266474Sjimharris		err = 0;
220266474Sjimharris		break;
221266474Sjimharris	default:
222266474Sjimharris		err = SMB_EABORT;
223266474Sjimharris		break;
224266474Sjimharris	}
225266474Sjimharris
226266474Sjimharris	return (err);
227266474Sjimharris}
228266474Sjimharris
229266474Sjimharrisstatic struct ismt_desc *
230266474Sjimharrisismt_alloc_desc(struct ismt_softc *sc)
231266474Sjimharris{
232266474Sjimharris	struct ismt_desc *desc;
233266474Sjimharris
234266474Sjimharris	KASSERT(sc->bus_reserved == curthread,
235266474Sjimharris	    ("curthread %p did not request bus (%p has reserved)\n",
236266474Sjimharris	    curthread, sc->bus_reserved));
237266474Sjimharris
238266474Sjimharris	desc = &sc->desc[sc->head++];
239266474Sjimharris	if (sc->head == ISMT_DESC_ENTRIES)
240266474Sjimharris		sc->head = 0;
241266474Sjimharris
242266474Sjimharris	memset(desc, 0, sizeof(*desc));
243266474Sjimharris
244266474Sjimharris	return (desc);
245266474Sjimharris}
246266474Sjimharris
247266474Sjimharrisstatic int
248266474Sjimharrisismt_submit(struct ismt_softc *sc, struct ismt_desc *desc, uint8_t slave,
249266474Sjimharris    uint8_t is_read)
250266474Sjimharris{
251266474Sjimharris	uint32_t err, fmhp, val;
252266474Sjimharris
253266474Sjimharris	desc->control |= ISMT_DESC_FAIR;
254266474Sjimharris	if (sc->using_msi)
255266474Sjimharris		desc->control |= ISMT_DESC_INT;
256266474Sjimharris
257266474Sjimharris	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(slave, is_read);
258266474Sjimharris	desc->dptr_low = (sc->dma_buffer_bus_addr & 0xFFFFFFFFLL);
259266474Sjimharris	desc->dptr_high = (sc->dma_buffer_bus_addr >> 32);
260266474Sjimharris
261266474Sjimharris	wmb();
262266474Sjimharris
263266474Sjimharris	fmhp = sc->head << 16;
264266474Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MCTRL);
265266474Sjimharris	val &= ~ISMT_MCTRL_FMHP;
266266474Sjimharris	val |= fmhp;
267266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MCTRL, val);
268266474Sjimharris
269266474Sjimharris	/* set the start bit */
270266474Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MCTRL);
271266474Sjimharris	val |= ISMT_MCTRL_SS;
272266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MCTRL, val);
273266474Sjimharris
274266474Sjimharris	err = tsleep(sc, PWAIT, "ismt_wait", 5 * hz);
275266474Sjimharris
276266474Sjimharris	if (err != 0) {
277266474Sjimharris		ISMT_DEBUG(sc->pcidev, "%s timeout\n", __func__);
278266474Sjimharris		return (SMB_ETIMEOUT);
279266474Sjimharris	}
280266474Sjimharris
281266474Sjimharris	ISMT_DEBUG(sc->pcidev, "%s status=0x%x\n", __func__, desc->status);
282266474Sjimharris
283266474Sjimharris	if (desc->status & ISMT_DESC_SCS)
284266474Sjimharris		return (SMB_ENOERR);
285266474Sjimharris
286266474Sjimharris	if (desc->status & ISMT_DESC_NAK)
287266474Sjimharris		return (SMB_ENOACK);
288266474Sjimharris
289266474Sjimharris	if (desc->status & ISMT_DESC_CRC)
290266474Sjimharris		return (SMB_EBUSERR);
291266474Sjimharris
292266474Sjimharris	if (desc->status & ISMT_DESC_COL)
293266474Sjimharris		return (SMB_ECOLLI);
294266474Sjimharris
295266474Sjimharris	if (desc->status & ISMT_DESC_LPR)
296266474Sjimharris		return (SMB_EINVAL);
297266474Sjimharris
298266474Sjimharris	if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO))
299266474Sjimharris		return (SMB_ETIMEOUT);
300266474Sjimharris
301266474Sjimharris	return (SMB_EBUSERR);
302266474Sjimharris}
303266474Sjimharris
304266474Sjimharris
305266474Sjimharrisstatic int
306266474Sjimharrisismt_quick(device_t dev, u_char slave, int how)
307266474Sjimharris{
308266474Sjimharris	struct ismt_desc	*desc;
309266474Sjimharris	struct ismt_softc	*sc;
310266474Sjimharris	int			is_read;
311266474Sjimharris
312266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
313266474Sjimharris
314266474Sjimharris	if (how != SMB_QREAD && how != SMB_QWRITE) {
315266474Sjimharris		return (SMB_ENOTSUPP);
316266474Sjimharris	}
317266474Sjimharris
318266474Sjimharris	sc = device_get_softc(dev);
319266474Sjimharris	desc = ismt_alloc_desc(sc);
320266474Sjimharris	is_read = (how == SMB_QREAD ? 1 : 0);
321266474Sjimharris	return (ismt_submit(sc, desc, slave, is_read));
322266474Sjimharris}
323266474Sjimharris
324266474Sjimharrisstatic int
325266474Sjimharrisismt_sendb(device_t dev, u_char slave, char byte)
326266474Sjimharris{
327266474Sjimharris	struct ismt_desc	*desc;
328266474Sjimharris	struct ismt_softc	*sc;
329266474Sjimharris
330266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
331266474Sjimharris
332266474Sjimharris	sc = device_get_softc(dev);
333266474Sjimharris	desc = ismt_alloc_desc(sc);
334266474Sjimharris	desc->control = ISMT_DESC_CWRL;
335266474Sjimharris	desc->wr_len_cmd = byte;
336266474Sjimharris
337266474Sjimharris	return (ismt_submit(sc, desc, slave, 0));
338266474Sjimharris}
339266474Sjimharris
340266474Sjimharrisstatic int
341266474Sjimharrisismt_recvb(device_t dev, u_char slave, char *byte)
342266474Sjimharris{
343266474Sjimharris	struct ismt_desc	*desc;
344266474Sjimharris	struct ismt_softc	*sc;
345266474Sjimharris	int			err;
346266474Sjimharris
347266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
348266474Sjimharris
349266474Sjimharris	sc = device_get_softc(dev);
350266474Sjimharris	desc = ismt_alloc_desc(sc);
351266474Sjimharris	desc->rd_len = 1;
352266474Sjimharris
353266474Sjimharris	err = ismt_submit(sc, desc, slave, 1);
354266474Sjimharris
355266474Sjimharris	if (err != SMB_ENOERR)
356266474Sjimharris		return (err);
357266474Sjimharris
358266474Sjimharris	*byte = sc->dma_buffer[0];
359266474Sjimharris
360266474Sjimharris	return (err);
361266474Sjimharris}
362266474Sjimharris
363266474Sjimharrisstatic int
364266474Sjimharrisismt_writeb(device_t dev, u_char slave, char cmd, char byte)
365266474Sjimharris{
366266474Sjimharris	struct ismt_desc	*desc;
367266474Sjimharris	struct ismt_softc	*sc;
368266474Sjimharris
369266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
370266474Sjimharris
371266474Sjimharris	sc = device_get_softc(dev);
372266474Sjimharris	desc = ismt_alloc_desc(sc);
373266474Sjimharris	desc->wr_len_cmd = 2;
374266474Sjimharris	sc->dma_buffer[0] = cmd;
375266474Sjimharris	sc->dma_buffer[1] = byte;
376266474Sjimharris
377266474Sjimharris	return (ismt_submit(sc, desc, slave, 0));
378266474Sjimharris}
379266474Sjimharris
380266474Sjimharrisstatic int
381266474Sjimharrisismt_writew(device_t dev, u_char slave, char cmd, short word)
382266474Sjimharris{
383266474Sjimharris	struct ismt_desc	*desc;
384266474Sjimharris	struct ismt_softc	*sc;
385266474Sjimharris
386266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
387266474Sjimharris
388266474Sjimharris	sc = device_get_softc(dev);
389266474Sjimharris	desc = ismt_alloc_desc(sc);
390266474Sjimharris	desc->wr_len_cmd = 3;
391266474Sjimharris	sc->dma_buffer[0] = cmd;
392266474Sjimharris	sc->dma_buffer[1] = word & 0xFF;
393266474Sjimharris	sc->dma_buffer[2] = word >> 8;
394266474Sjimharris
395266474Sjimharris	return (ismt_submit(sc, desc, slave, 0));
396266474Sjimharris}
397266474Sjimharris
398266474Sjimharrisstatic int
399266474Sjimharrisismt_readb(device_t dev, u_char slave, char cmd, char *byte)
400266474Sjimharris{
401266474Sjimharris	struct ismt_desc	*desc;
402266474Sjimharris	struct ismt_softc	*sc;
403266474Sjimharris	int			err;
404266474Sjimharris
405266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
406266474Sjimharris
407266474Sjimharris	sc = device_get_softc(dev);
408266474Sjimharris	desc = ismt_alloc_desc(sc);
409266474Sjimharris	desc->control = ISMT_DESC_CWRL;
410266474Sjimharris	desc->wr_len_cmd = cmd;
411266474Sjimharris	desc->rd_len = 1;
412266474Sjimharris
413266474Sjimharris	err = ismt_submit(sc, desc, slave, 1);
414266474Sjimharris
415266474Sjimharris	if (err != SMB_ENOERR)
416266474Sjimharris		return (err);
417266474Sjimharris
418266474Sjimharris	*byte = sc->dma_buffer[0];
419266474Sjimharris
420266474Sjimharris	return (err);
421266474Sjimharris}
422266474Sjimharris
423266474Sjimharrisstatic int
424266474Sjimharrisismt_readw(device_t dev, u_char slave, char cmd, short *word)
425266474Sjimharris{
426266474Sjimharris	struct ismt_desc	*desc;
427266474Sjimharris	struct ismt_softc	*sc;
428266474Sjimharris	int			err;
429266474Sjimharris
430266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
431266474Sjimharris
432266474Sjimharris	sc = device_get_softc(dev);
433266474Sjimharris	desc = ismt_alloc_desc(sc);
434266474Sjimharris	desc->control = ISMT_DESC_CWRL;
435266474Sjimharris	desc->wr_len_cmd = cmd;
436266474Sjimharris	desc->rd_len = 2;
437266474Sjimharris
438266474Sjimharris	err = ismt_submit(sc, desc, slave, 1);
439266474Sjimharris
440266474Sjimharris	if (err != SMB_ENOERR)
441266474Sjimharris		return (err);
442266474Sjimharris
443266474Sjimharris	*word = sc->dma_buffer[0] | (sc->dma_buffer[1] << 8);
444266474Sjimharris
445266474Sjimharris	return (err);
446266474Sjimharris}
447266474Sjimharris
448266474Sjimharrisstatic int
449266474Sjimharrisismt_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
450266474Sjimharris{
451266474Sjimharris	struct ismt_desc	*desc;
452266474Sjimharris	struct ismt_softc	*sc;
453266474Sjimharris	int			err;
454266474Sjimharris
455266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
456266474Sjimharris
457266474Sjimharris	sc = device_get_softc(dev);
458266474Sjimharris	desc = ismt_alloc_desc(sc);
459266474Sjimharris	desc->wr_len_cmd = 3;
460266474Sjimharris	desc->rd_len = 2;
461266474Sjimharris	sc->dma_buffer[0] = cmd;
462266474Sjimharris	sc->dma_buffer[1] = sdata & 0xff;
463266474Sjimharris	sc->dma_buffer[2] = sdata >> 8;
464266474Sjimharris
465266474Sjimharris	err = ismt_submit(sc, desc, slave, 0);
466266474Sjimharris
467266474Sjimharris	if (err != SMB_ENOERR)
468266474Sjimharris		return (err);
469266474Sjimharris
470266474Sjimharris	*rdata = sc->dma_buffer[0] | (sc->dma_buffer[1] << 8);
471266474Sjimharris
472266474Sjimharris	return (err);
473266474Sjimharris}
474266474Sjimharris
475266474Sjimharrisstatic int
476266474Sjimharrisismt_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
477266474Sjimharris{
478266474Sjimharris	struct ismt_desc	*desc;
479266474Sjimharris	struct ismt_softc	*sc;
480266474Sjimharris
481266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
482266474Sjimharris
483266474Sjimharris	if (count == 0 || count > ISMT_MAX_BLOCK_SIZE)
484266474Sjimharris		return (SMB_EINVAL);
485266474Sjimharris
486266474Sjimharris	sc = device_get_softc(dev);
487266474Sjimharris	desc = ismt_alloc_desc(sc);
488266474Sjimharris	desc->control = ISMT_DESC_I2C;
489266474Sjimharris	desc->wr_len_cmd = count + 1;
490266474Sjimharris	sc->dma_buffer[0] = cmd;
491266474Sjimharris	memcpy(&sc->dma_buffer[1], buf, count);
492266474Sjimharris
493266474Sjimharris	return (ismt_submit(sc, desc, slave, 0));
494266474Sjimharris}
495266474Sjimharris
496266474Sjimharrisstatic int
497266474Sjimharrisismt_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
498266474Sjimharris{
499266474Sjimharris	struct ismt_desc	*desc;
500266474Sjimharris	struct ismt_softc	*sc;
501266474Sjimharris	int			err;
502266474Sjimharris
503266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
504266474Sjimharris
505266474Sjimharris	if (*count == 0 || *count > ISMT_MAX_BLOCK_SIZE)
506266474Sjimharris		return (SMB_EINVAL);
507266474Sjimharris
508266474Sjimharris	sc = device_get_softc(dev);
509266474Sjimharris	desc = ismt_alloc_desc(sc);
510266474Sjimharris	desc->control = ISMT_DESC_I2C | ISMT_DESC_CWRL;
511266474Sjimharris	desc->wr_len_cmd = cmd;
512266474Sjimharris	desc->rd_len = *count;
513266474Sjimharris
514266474Sjimharris	err = ismt_submit(sc, desc, slave, 0);
515266474Sjimharris
516266474Sjimharris	if (err != SMB_ENOERR)
517266474Sjimharris		return (err);
518266474Sjimharris
519266474Sjimharris	memcpy(buf, sc->dma_buffer, desc->rxbytes);
520266474Sjimharris	*count = desc->rxbytes;
521266474Sjimharris
522266474Sjimharris	return (err);
523266474Sjimharris}
524266474Sjimharris
525266474Sjimharrisstatic int
526266474Sjimharrisismt_detach(device_t dev)
527266474Sjimharris{
528266474Sjimharris	struct ismt_softc	*sc;
529266474Sjimharris	int			error;
530266474Sjimharris
531266474Sjimharris	ISMT_DEBUG(dev, "%s\n", __func__);
532266474Sjimharris	sc = device_get_softc(dev);
533266474Sjimharris
534266474Sjimharris	error = bus_generic_detach(dev);
535266474Sjimharris	if (error)
536266474Sjimharris		return (error);
537266474Sjimharris
538266474Sjimharris	device_delete_child(dev, sc->smbdev);
539266474Sjimharris
540266474Sjimharris	if (sc->intr_handle != NULL) {
541266474Sjimharris		bus_teardown_intr(dev, sc->intr_res, sc->intr_handle);
542266474Sjimharris		sc->intr_handle = NULL;
543266474Sjimharris	}
544266474Sjimharris	if (sc->intr_res != NULL) {
545266474Sjimharris		bus_release_resource(dev,
546266474Sjimharris		    SYS_RES_IRQ, sc->intr_rid, sc->intr_res);
547266474Sjimharris		sc->intr_res = NULL;
548266474Sjimharris	}
549266474Sjimharris	if (sc->using_msi == 1)
550266474Sjimharris		pci_release_msi(dev);
551266474Sjimharris
552266474Sjimharris	if (sc->mmio_res != NULL) {
553266474Sjimharris		bus_release_resource(dev,
554266474Sjimharris		    SYS_RES_MEMORY, sc->mmio_rid, sc->mmio_res);
555266474Sjimharris		sc->mmio_res = NULL;
556266474Sjimharris	}
557266474Sjimharris
558266474Sjimharris	bus_dmamap_unload(sc->desc_dma_tag, sc->desc_dma_map);
559266474Sjimharris	bus_dmamap_unload(sc->dma_buffer_dma_tag, sc->dma_buffer_dma_map);
560266474Sjimharris
561266474Sjimharris	bus_dmamem_free(sc->desc_dma_tag, sc->desc,
562266474Sjimharris	    sc->desc_dma_map);
563266474Sjimharris	bus_dmamem_free(sc->dma_buffer_dma_tag, sc->dma_buffer,
564266474Sjimharris	    sc->dma_buffer_dma_map);
565266474Sjimharris
566266474Sjimharris	bus_dma_tag_destroy(sc->desc_dma_tag);
567266474Sjimharris	bus_dma_tag_destroy(sc->dma_buffer_dma_tag);
568266474Sjimharris
569266474Sjimharris	pci_disable_busmaster(dev);
570266474Sjimharris
571266474Sjimharris	return 0;
572266474Sjimharris}
573266474Sjimharris
574266474Sjimharrisstatic void
575266474Sjimharrisismt_single_map(void *arg, bus_dma_segment_t *seg, int nseg, int error)
576266474Sjimharris{
577266474Sjimharris	uint64_t *bus_addr = (uint64_t *)arg;
578266474Sjimharris
579266474Sjimharris	KASSERT(error == 0, ("%s: error=%d\n", __func__, error));
580266474Sjimharris	KASSERT(nseg == 1, ("%s: nseg=%d\n", __func__, nseg));
581266474Sjimharris
582266474Sjimharris	*bus_addr = seg[0].ds_addr;
583266474Sjimharris}
584266474Sjimharris
585266474Sjimharrisstatic int
586266474Sjimharrisismt_attach(device_t dev)
587266474Sjimharris{
588266474Sjimharris	struct ismt_softc *sc = device_get_softc(dev);
589266474Sjimharris	int err, num_vectors, val;
590266474Sjimharris
591266474Sjimharris	sc->pcidev = dev;
592266474Sjimharris	pci_enable_busmaster(dev);
593266474Sjimharris
594266474Sjimharris	if ((sc->smbdev = device_add_child(dev, "smbus", -1)) == NULL) {
595266474Sjimharris		device_printf(dev, "no smbus child found\n");
596266474Sjimharris		err = ENXIO;
597266474Sjimharris		goto fail;
598266474Sjimharris	}
599266474Sjimharris
600266474Sjimharris	sc->mmio_rid = PCIR_BAR(0);
601266474Sjimharris	sc->mmio_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
602266474Sjimharris	    &sc->mmio_rid, RF_ACTIVE);
603266474Sjimharris	if (sc->mmio_res == NULL) {
604266474Sjimharris		device_printf(dev, "cannot allocate mmio region\n");
605266474Sjimharris		err = ENOMEM;
606266474Sjimharris		goto fail;
607266474Sjimharris	}
608266474Sjimharris
609266474Sjimharris	sc->mmio_tag = rman_get_bustag(sc->mmio_res);
610266474Sjimharris	sc->mmio_handle = rman_get_bushandle(sc->mmio_res);
611266474Sjimharris
612266474Sjimharris	/* Attach "smbus" child */
613266474Sjimharris	if ((err = bus_generic_attach(dev)) != 0) {
614266474Sjimharris		device_printf(dev, "failed to attach child: %d\n", err);
615266474Sjimharris		err = ENXIO;
616266474Sjimharris		goto fail;
617266474Sjimharris	}
618266474Sjimharris
619266474Sjimharris	bus_dma_tag_create(bus_get_dma_tag(dev), 4, PAGE_SIZE,
620266474Sjimharris	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
621266474Sjimharris	    DESC_SIZE, 1, DESC_SIZE,
622266474Sjimharris	    0, NULL, NULL, &sc->desc_dma_tag);
623266474Sjimharris
624266474Sjimharris	bus_dma_tag_create(bus_get_dma_tag(dev), 4, PAGE_SIZE,
625266474Sjimharris	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
626266474Sjimharris	    DMA_BUFFER_SIZE, 1, DMA_BUFFER_SIZE,
627266474Sjimharris	    0, NULL, NULL, &sc->dma_buffer_dma_tag);
628266474Sjimharris
629266474Sjimharris	bus_dmamap_create(sc->desc_dma_tag, 0,
630266474Sjimharris	    &sc->desc_dma_map);
631266474Sjimharris	bus_dmamap_create(sc->dma_buffer_dma_tag, 0,
632266474Sjimharris	    &sc->dma_buffer_dma_map);
633266474Sjimharris
634266474Sjimharris	bus_dmamem_alloc(sc->desc_dma_tag,
635266474Sjimharris	    (void **)&sc->desc, BUS_DMA_WAITOK,
636266474Sjimharris	    &sc->desc_dma_map);
637266474Sjimharris	bus_dmamem_alloc(sc->dma_buffer_dma_tag,
638266474Sjimharris	    (void **)&sc->dma_buffer, BUS_DMA_WAITOK,
639266474Sjimharris	    &sc->dma_buffer_dma_map);
640266474Sjimharris
641266474Sjimharris	bus_dmamap_load(sc->desc_dma_tag,
642266474Sjimharris	    sc->desc_dma_map, sc->desc, DESC_SIZE,
643266474Sjimharris	    ismt_single_map, &sc->desc_bus_addr, 0);
644266474Sjimharris	bus_dmamap_load(sc->dma_buffer_dma_tag,
645266474Sjimharris	    sc->dma_buffer_dma_map, sc->dma_buffer, DMA_BUFFER_SIZE,
646266474Sjimharris	    ismt_single_map, &sc->dma_buffer_bus_addr, 0);
647266474Sjimharris
648266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MDBA,
649266474Sjimharris	    (sc->desc_bus_addr & 0xFFFFFFFFLL));
650266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MDBA + 4,
651266474Sjimharris	    (sc->desc_bus_addr >> 32));
652266474Sjimharris
653266474Sjimharris	/* initialize the Master Control Register (MCTRL) */
654266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MCTRL, ISMT_MCTRL_MEIE);
655266474Sjimharris
656266474Sjimharris	/* initialize the Master Status Register (MSTS) */
657266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MSTS, 0);
658266474Sjimharris
659266474Sjimharris	/* initialize the Master Descriptor Size (MDS) */
660266474Sjimharris	val = bus_read_4(sc->mmio_res, ISMT_MSTR_MDS);
661266474Sjimharris	val &= ~ISMT_MDS_MASK;
662266474Sjimharris	val |= (ISMT_DESC_ENTRIES - 1);
663266474Sjimharris	bus_write_4(sc->mmio_res, ISMT_MSTR_MDS, val);
664266474Sjimharris
665266474Sjimharris	sc->using_msi = 1;
666266474Sjimharris
667266474Sjimharris	if (pci_msi_count(dev) == 0) {
668266474Sjimharris		sc->using_msi = 0;
669266474Sjimharris		goto intx;
670266474Sjimharris	}
671266474Sjimharris
672266474Sjimharris	num_vectors = 1;
673266474Sjimharris	if (pci_alloc_msi(dev, &num_vectors) != 0) {
674266474Sjimharris		sc->using_msi = 0;
675266474Sjimharris		goto intx;
676266474Sjimharris	}
677266474Sjimharris
678266474Sjimharris	sc->intr_rid = 1;
679266474Sjimharris	sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
680266474Sjimharris	    &sc->intr_rid, RF_ACTIVE);
681266474Sjimharris
682266474Sjimharris	if (sc->intr_res == NULL) {
683266474Sjimharris		sc->using_msi = 0;
684266474Sjimharris		pci_release_msi(dev);
685266474Sjimharris	}
686266474Sjimharris
687266474Sjimharrisintx:
688266474Sjimharris	if (sc->using_msi == 0) {
689266474Sjimharris		sc->intr_rid = 0;
690266474Sjimharris		sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
691266474Sjimharris		    &sc->intr_rid, RF_SHAREABLE | RF_ACTIVE);
692266474Sjimharris		if (sc->intr_res == NULL) {
693266474Sjimharris			device_printf(dev, "cannot allocate irq\n");
694266474Sjimharris			err = ENXIO;
695266474Sjimharris			goto fail;
696266474Sjimharris		}
697266474Sjimharris	}
698266474Sjimharris
699266474Sjimharris	ISMT_DEBUG(dev, "using_msi = %d\n", sc->using_msi);
700266474Sjimharris
701266474Sjimharris	err = bus_setup_intr(dev, sc->intr_res,
702266474Sjimharris	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, ismt_intr, sc,
703266474Sjimharris	    &sc->intr_handle);
704266474Sjimharris	if (err != 0) {
705266474Sjimharris		device_printf(dev, "cannot setup interrupt\n");
706266474Sjimharris		err = ENXIO;
707266474Sjimharris		goto fail;
708266474Sjimharris	}
709266474Sjimharris
710266474Sjimharris	return (0);
711266474Sjimharris
712266474Sjimharrisfail:
713266474Sjimharris	ismt_detach(dev);
714266474Sjimharris	return (err);
715266474Sjimharris}
716266474Sjimharris
717266474Sjimharris#define ID_INTEL_S1200_SMT0		0x0c598086
718266474Sjimharris#define ID_INTEL_S1200_SMT1		0x0c5a8086
719266474Sjimharris#define ID_INTEL_C2000_SMT		0x1f158086
720358710Sjhibbits#define ID_INTEL_C3000_SMT		0x19ac8086
721266474Sjimharris
722266474Sjimharrisstatic int
723266474Sjimharrisismt_probe(device_t dev)
724266474Sjimharris{
725266474Sjimharris	const char *desc;
726266474Sjimharris
727266474Sjimharris	switch (pci_get_devid(dev)) {
728266474Sjimharris	case ID_INTEL_S1200_SMT0:
729266474Sjimharris		desc = "Atom Processor S1200 SMBus 2.0 Controller 0";
730266474Sjimharris		break;
731266474Sjimharris	case ID_INTEL_S1200_SMT1:
732266474Sjimharris		desc = "Atom Processor S1200 SMBus 2.0 Controller 1";
733266474Sjimharris		break;
734266474Sjimharris	case ID_INTEL_C2000_SMT:
735266474Sjimharris		desc = "Atom Processor C2000 SMBus 2.0";
736266474Sjimharris		break;
737358710Sjhibbits	case ID_INTEL_C3000_SMT:
738358710Sjhibbits		desc = "Atom Processor C3000 SMBus 2.0";
739358710Sjhibbits		break;
740266474Sjimharris	default:
741266474Sjimharris		return (ENXIO);
742266474Sjimharris	}
743266474Sjimharris
744266474Sjimharris	device_set_desc(dev, desc);
745266474Sjimharris	return (BUS_PROBE_DEFAULT);
746266474Sjimharris}
747266474Sjimharris
748266474Sjimharris/* Device methods */
749266474Sjimharrisstatic device_method_t ismt_pci_methods[] = {
750266474Sjimharris        DEVMETHOD(device_probe,		ismt_probe),
751266474Sjimharris        DEVMETHOD(device_attach,	ismt_attach),
752266474Sjimharris        DEVMETHOD(device_detach,	ismt_detach),
753266474Sjimharris
754266474Sjimharris        DEVMETHOD(smbus_callback,	ismt_callback),
755266474Sjimharris        DEVMETHOD(smbus_quick,		ismt_quick),
756266474Sjimharris        DEVMETHOD(smbus_sendb,		ismt_sendb),
757266474Sjimharris        DEVMETHOD(smbus_recvb,		ismt_recvb),
758266474Sjimharris        DEVMETHOD(smbus_writeb,		ismt_writeb),
759266474Sjimharris        DEVMETHOD(smbus_writew,		ismt_writew),
760266474Sjimharris        DEVMETHOD(smbus_readb,		ismt_readb),
761266474Sjimharris        DEVMETHOD(smbus_readw,		ismt_readw),
762266474Sjimharris        DEVMETHOD(smbus_pcall,		ismt_pcall),
763266474Sjimharris        DEVMETHOD(smbus_bwrite,		ismt_bwrite),
764266474Sjimharris        DEVMETHOD(smbus_bread,		ismt_bread),
765266474Sjimharris
766266474Sjimharris	DEVMETHOD_END
767266474Sjimharris};
768266474Sjimharris
769266474Sjimharrisstatic driver_t ismt_pci_driver = {
770266474Sjimharris	"ismt",
771266474Sjimharris	ismt_pci_methods,
772266474Sjimharris	sizeof(struct ismt_softc)
773266474Sjimharris};
774266474Sjimharris
775266474Sjimharrisstatic devclass_t ismt_pci_devclass;
776266474Sjimharris
777266474SjimharrisDRIVER_MODULE(ismt, pci, ismt_pci_driver, ismt_pci_devclass, 0, 0);
778266474SjimharrisDRIVER_MODULE(smbus, ismt, smbus_driver, smbus_devclass, 0, 0);
779266474Sjimharris
780266474SjimharrisMODULE_DEPEND(ismt, pci, 1, 1, 1);
781266474SjimharrisMODULE_DEPEND(ismt, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
782266474SjimharrisMODULE_VERSION(ismt, 1);
783