xref: /freebsd/sys/dev/imcsmb/imcsmb.c (revision dfee3204c98718efbdadb2439fd95ba312920ece)
124f93aa0SRavi Pokala /*-
224f93aa0SRavi Pokala  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
324f93aa0SRavi Pokala  *
424f93aa0SRavi Pokala  * Authors: Joe Kloss; Ravi Pokala (rpokala@freebsd.org)
524f93aa0SRavi Pokala  *
624f93aa0SRavi Pokala  * Copyright (c) 2017-2018 Panasas
724f93aa0SRavi Pokala  *
824f93aa0SRavi Pokala  * Redistribution and use in source and binary forms, with or without
924f93aa0SRavi Pokala  * modification, are permitted provided that the following conditions
1024f93aa0SRavi Pokala  * are met:
1124f93aa0SRavi Pokala  * 1. Redistributions of source code must retain the above copyright
1224f93aa0SRavi Pokala  *    notice, this list of conditions and the following disclaimer.
1324f93aa0SRavi Pokala  * 2. Redistributions in binary form must reproduce the above copyright
1424f93aa0SRavi Pokala  *    notice, this list of conditions and the following disclaimer in the
1524f93aa0SRavi Pokala  *    documentation and/or other materials provided with the distribution.
1624f93aa0SRavi Pokala  *
1724f93aa0SRavi Pokala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1824f93aa0SRavi Pokala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1924f93aa0SRavi Pokala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2024f93aa0SRavi Pokala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2124f93aa0SRavi Pokala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2224f93aa0SRavi Pokala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2324f93aa0SRavi Pokala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2424f93aa0SRavi Pokala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2524f93aa0SRavi Pokala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2624f93aa0SRavi Pokala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2724f93aa0SRavi Pokala  * SUCH DAMAGE.
2824f93aa0SRavi Pokala  *
2924f93aa0SRavi Pokala  * $FreeBSD$
3024f93aa0SRavi Pokala  */
3124f93aa0SRavi Pokala 
3224f93aa0SRavi Pokala /* A detailed description of this device is present in imcsmb_pci.c */
3324f93aa0SRavi Pokala 
3424f93aa0SRavi Pokala #include <sys/param.h>
3524f93aa0SRavi Pokala #include <sys/systm.h>
3624f93aa0SRavi Pokala #include <sys/kernel.h>
3724f93aa0SRavi Pokala #include <sys/module.h>
3824f93aa0SRavi Pokala #include <sys/endian.h>
3924f93aa0SRavi Pokala #include <sys/errno.h>
4024f93aa0SRavi Pokala #include <sys/lock.h>
4124f93aa0SRavi Pokala #include <sys/mutex.h>
4224f93aa0SRavi Pokala #include <sys/syslog.h>
4324f93aa0SRavi Pokala #include <sys/bus.h>
4424f93aa0SRavi Pokala 
4524f93aa0SRavi Pokala #include <machine/bus.h>
4624f93aa0SRavi Pokala #include <machine/atomic.h>
4724f93aa0SRavi Pokala 
4824f93aa0SRavi Pokala #include <dev/pci/pcivar.h>
4924f93aa0SRavi Pokala #include <dev/pci/pcireg.h>
5024f93aa0SRavi Pokala 
5124f93aa0SRavi Pokala #include <dev/smbus/smbconf.h>
5224f93aa0SRavi Pokala 
5324f93aa0SRavi Pokala #include "imcsmb_reg.h"
5424f93aa0SRavi Pokala #include "imcsmb_var.h"
5524f93aa0SRavi Pokala 
5624f93aa0SRavi Pokala /* Device methods */
5724f93aa0SRavi Pokala static int imcsmb_attach(device_t dev);
5824f93aa0SRavi Pokala static int imcsmb_detach(device_t dev);
5924f93aa0SRavi Pokala static int imcsmb_probe(device_t dev);
6024f93aa0SRavi Pokala 
6124f93aa0SRavi Pokala /* SMBus methods */
6224f93aa0SRavi Pokala static int imcsmb_callback(device_t dev, int index, void *data);
6324f93aa0SRavi Pokala static int imcsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
6424f93aa0SRavi Pokala static int imcsmb_readw(device_t dev, u_char slave, char cmd, short *word);
6524f93aa0SRavi Pokala static int imcsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
6624f93aa0SRavi Pokala static int imcsmb_writew(device_t dev, u_char slave, char cmd, short word);
6724f93aa0SRavi Pokala 
6824f93aa0SRavi Pokala /* All the read/write methods wrap around this. */
6924f93aa0SRavi Pokala static int imcsmb_transfer(device_t dev, u_char slave, char cmd, void *data,
7024f93aa0SRavi Pokala     int word_op, int write_op);
7124f93aa0SRavi Pokala 
7224f93aa0SRavi Pokala /**
7324f93aa0SRavi Pokala  * device_attach() method. Set up the softc, including getting the set of the
7424f93aa0SRavi Pokala  * parent imcsmb_pci's registers that we will use. Create the smbus(4) device,
7524f93aa0SRavi Pokala  * which any SMBus slave device drivers will connect to.
7624f93aa0SRavi Pokala  *
7724f93aa0SRavi Pokala  * @author rpokala
7824f93aa0SRavi Pokala  *
7924f93aa0SRavi Pokala  * @param[in,out] dev
8024f93aa0SRavi Pokala  *      Device being attached.
8124f93aa0SRavi Pokala  */
8224f93aa0SRavi Pokala static int
8324f93aa0SRavi Pokala imcsmb_attach(device_t dev)
8424f93aa0SRavi Pokala {
8524f93aa0SRavi Pokala 	struct imcsmb_softc *sc;
8624f93aa0SRavi Pokala 	int rc;
8724f93aa0SRavi Pokala 
8824f93aa0SRavi Pokala 	/* Initialize private state */
8924f93aa0SRavi Pokala 	sc = device_get_softc(dev);
9024f93aa0SRavi Pokala 	sc->dev = dev;
9124f93aa0SRavi Pokala 	sc->imcsmb_pci = device_get_parent(dev);
9224f93aa0SRavi Pokala 	sc->regs = device_get_ivars(dev);
9324f93aa0SRavi Pokala 
9424f93aa0SRavi Pokala 	/* Create the smbus child */
9524f93aa0SRavi Pokala 	sc->smbus = device_add_child(dev, "smbus", -1);
9624f93aa0SRavi Pokala 	if (sc->smbus == NULL) {
9724f93aa0SRavi Pokala 		/* Nothing has been allocated, so there's no cleanup. */
9824f93aa0SRavi Pokala 		device_printf(dev, "Child smbus not added\n");
9924f93aa0SRavi Pokala 		rc = ENXIO;
10024f93aa0SRavi Pokala 		goto out;
10124f93aa0SRavi Pokala 	}
10224f93aa0SRavi Pokala 
10324f93aa0SRavi Pokala 	/* Attach the smbus child. */
10424f93aa0SRavi Pokala 	if ((rc = bus_generic_attach(dev)) != 0) {
10524f93aa0SRavi Pokala 		device_printf(dev, "Failed to attach smbus: %d\n", rc);
10624f93aa0SRavi Pokala 	}
10724f93aa0SRavi Pokala 
10824f93aa0SRavi Pokala out:
10924f93aa0SRavi Pokala 	return (rc);
11024f93aa0SRavi Pokala }
11124f93aa0SRavi Pokala 
11224f93aa0SRavi Pokala /**
11324f93aa0SRavi Pokala  * device_detach() method. attach() didn't do any allocations, so all that's
11424f93aa0SRavi Pokala  * needed here is to free up any downstream drivers and children.
11524f93aa0SRavi Pokala  *
11624f93aa0SRavi Pokala  * @author Joe Kloss
11724f93aa0SRavi Pokala  *
11824f93aa0SRavi Pokala  * @param[in] dev
11924f93aa0SRavi Pokala  *      Device being detached.
12024f93aa0SRavi Pokala  */
12124f93aa0SRavi Pokala static int
12224f93aa0SRavi Pokala imcsmb_detach(device_t dev)
12324f93aa0SRavi Pokala {
12424f93aa0SRavi Pokala 	int rc;
12524f93aa0SRavi Pokala 
12624f93aa0SRavi Pokala 	/* Detach any attached drivers */
12724f93aa0SRavi Pokala 	rc = bus_generic_detach(dev);
12824f93aa0SRavi Pokala 	if (rc == 0) {
12924f93aa0SRavi Pokala 		/* Remove all children */
13024f93aa0SRavi Pokala 		rc = device_delete_children(dev);
13124f93aa0SRavi Pokala 	}
13224f93aa0SRavi Pokala 
13324f93aa0SRavi Pokala 	return (rc);
13424f93aa0SRavi Pokala }
13524f93aa0SRavi Pokala 
13624f93aa0SRavi Pokala /**
13724f93aa0SRavi Pokala  * device_probe() method. All the actual probing was done by the imcsmb_pci
13824f93aa0SRavi Pokala  * parent, so just report success.
13924f93aa0SRavi Pokala  *
14024f93aa0SRavi Pokala  * @author Joe Kloss
14124f93aa0SRavi Pokala  *
14224f93aa0SRavi Pokala  * @param[in,out] dev
14324f93aa0SRavi Pokala  *      Device being probed.
14424f93aa0SRavi Pokala  */
14524f93aa0SRavi Pokala static int
14624f93aa0SRavi Pokala imcsmb_probe(device_t dev)
14724f93aa0SRavi Pokala {
14824f93aa0SRavi Pokala 
14924f93aa0SRavi Pokala 	device_set_desc(dev, "iMC SMBus controller");
15024f93aa0SRavi Pokala 	return (BUS_PROBE_DEFAULT);
15124f93aa0SRavi Pokala }
15224f93aa0SRavi Pokala 
15324f93aa0SRavi Pokala /**
15424f93aa0SRavi Pokala  * smbus_callback() method. Call the parent imcsmb_pci's request or release
15524f93aa0SRavi Pokala  * function to quiesce / restart firmware tasks which might use the SMBus.
15624f93aa0SRavi Pokala  *
15724f93aa0SRavi Pokala  * @author rpokala
15824f93aa0SRavi Pokala  *
15924f93aa0SRavi Pokala  * @param[in] dev
16024f93aa0SRavi Pokala  *      Device being requested or released.
16124f93aa0SRavi Pokala  *
16224f93aa0SRavi Pokala  * @param[in] index
16324f93aa0SRavi Pokala  *      Either SMB_REQUEST_BUS or SMB_RELEASE_BUS.
16424f93aa0SRavi Pokala  *
16524f93aa0SRavi Pokala  * @param[in] data
16624f93aa0SRavi Pokala  *      Tell's the rest of the SMBus subsystem to allow or disallow waiting;
16724f93aa0SRavi Pokala  *      this driver only works with SMB_DONTWAIT.
16824f93aa0SRavi Pokala  */
16924f93aa0SRavi Pokala static int
17024f93aa0SRavi Pokala imcsmb_callback(device_t dev, int index, void *data)
17124f93aa0SRavi Pokala {
17224f93aa0SRavi Pokala 	struct imcsmb_softc *sc;
17324f93aa0SRavi Pokala 	int *how;
17424f93aa0SRavi Pokala 	int rc;
17524f93aa0SRavi Pokala 
17624f93aa0SRavi Pokala 	sc = device_get_softc(dev);
17724f93aa0SRavi Pokala 	how = (int *) data;
17824f93aa0SRavi Pokala 
17924f93aa0SRavi Pokala 	switch (index) {
18024f93aa0SRavi Pokala 	case SMB_REQUEST_BUS: {
18124f93aa0SRavi Pokala 		if (*how != SMB_DONTWAIT) {
18224f93aa0SRavi Pokala 			rc = EINVAL;
18324f93aa0SRavi Pokala 			goto out;
18424f93aa0SRavi Pokala 		}
18524f93aa0SRavi Pokala 		rc = imcsmb_pci_request_bus(sc->imcsmb_pci);
18624f93aa0SRavi Pokala 		break;
18724f93aa0SRavi Pokala 	}
18824f93aa0SRavi Pokala 	case SMB_RELEASE_BUS:
18924f93aa0SRavi Pokala 		imcsmb_pci_release_bus(sc->imcsmb_pci);
19024f93aa0SRavi Pokala 		rc = 0;
19124f93aa0SRavi Pokala 		break;
19224f93aa0SRavi Pokala 	default:
19324f93aa0SRavi Pokala 		rc = EINVAL;
19424f93aa0SRavi Pokala 		break;
19524f93aa0SRavi Pokala 	}
19624f93aa0SRavi Pokala 
19724f93aa0SRavi Pokala out:
19824f93aa0SRavi Pokala 	return (rc);
19924f93aa0SRavi Pokala }
20024f93aa0SRavi Pokala 
20124f93aa0SRavi Pokala /**
20224f93aa0SRavi Pokala  * smbus_readb() method. Thin wrapper around imcsmb_transfer().
20324f93aa0SRavi Pokala  *
20424f93aa0SRavi Pokala  * @author Joe Kloss
20524f93aa0SRavi Pokala  *
20624f93aa0SRavi Pokala  * @param[in] dev
20724f93aa0SRavi Pokala  *
20824f93aa0SRavi Pokala  * @param[in] slave
20924f93aa0SRavi Pokala  *      The SMBus address of the target device.
21024f93aa0SRavi Pokala  *
21124f93aa0SRavi Pokala  * @param[in] cmd
21224f93aa0SRavi Pokala  *      The SMBus command for the target device; this is the offset for SPDs,
21324f93aa0SRavi Pokala  *      or the register number for TSODs.
21424f93aa0SRavi Pokala  *
21524f93aa0SRavi Pokala  * @param[out] byte
21624f93aa0SRavi Pokala  *      The byte which was read.
21724f93aa0SRavi Pokala  */
21824f93aa0SRavi Pokala static int
21924f93aa0SRavi Pokala imcsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
22024f93aa0SRavi Pokala {
22124f93aa0SRavi Pokala 
22224f93aa0SRavi Pokala 	return (imcsmb_transfer(dev, slave, cmd, byte, FALSE, FALSE));
22324f93aa0SRavi Pokala }
22424f93aa0SRavi Pokala 
22524f93aa0SRavi Pokala /**
22624f93aa0SRavi Pokala  * smbus_readw() method. Thin wrapper around imcsmb_transfer().
22724f93aa0SRavi Pokala  *
22824f93aa0SRavi Pokala  * @author Joe Kloss
22924f93aa0SRavi Pokala  *
23024f93aa0SRavi Pokala  * @param[in] dev
23124f93aa0SRavi Pokala  *
23224f93aa0SRavi Pokala  * @param[in] slave
23324f93aa0SRavi Pokala  *      The SMBus address of the target device.
23424f93aa0SRavi Pokala  *
23524f93aa0SRavi Pokala  * @param[in] cmd
23624f93aa0SRavi Pokala  *      The SMBus command for the target device; this is the offset for SPDs,
23724f93aa0SRavi Pokala  *      or the register number for TSODs.
23824f93aa0SRavi Pokala  *
23924f93aa0SRavi Pokala  * @param[out] word
24024f93aa0SRavi Pokala  *      The word which was read.
24124f93aa0SRavi Pokala  */
24224f93aa0SRavi Pokala static int
24324f93aa0SRavi Pokala imcsmb_readw(device_t dev, u_char slave, char cmd, short *word)
24424f93aa0SRavi Pokala {
24524f93aa0SRavi Pokala 
24624f93aa0SRavi Pokala 	return (imcsmb_transfer(dev, slave, cmd, word, TRUE, FALSE));
24724f93aa0SRavi Pokala }
24824f93aa0SRavi Pokala 
24924f93aa0SRavi Pokala /**
25024f93aa0SRavi Pokala  * smbus_writeb() method. Thin wrapper around imcsmb_transfer().
25124f93aa0SRavi Pokala  *
25224f93aa0SRavi Pokala  * @author Joe Kloss
25324f93aa0SRavi Pokala  *
25424f93aa0SRavi Pokala  * @param[in] dev
25524f93aa0SRavi Pokala  *
25624f93aa0SRavi Pokala  * @param[in] slave
25724f93aa0SRavi Pokala  *      The SMBus address of the target device.
25824f93aa0SRavi Pokala  *
25924f93aa0SRavi Pokala  * @param[in] cmd
26024f93aa0SRavi Pokala  *      The SMBus command for the target device; this is the offset for SPDs,
26124f93aa0SRavi Pokala  *      or the register number for TSODs.
26224f93aa0SRavi Pokala  *
26324f93aa0SRavi Pokala  * @param[in] byte
26424f93aa0SRavi Pokala  *      The byte to write.
26524f93aa0SRavi Pokala  */
26624f93aa0SRavi Pokala static int
26724f93aa0SRavi Pokala imcsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
26824f93aa0SRavi Pokala {
26924f93aa0SRavi Pokala 
27024f93aa0SRavi Pokala 	return (imcsmb_transfer(dev, slave, cmd, &byte, FALSE, TRUE));
27124f93aa0SRavi Pokala }
27224f93aa0SRavi Pokala 
27324f93aa0SRavi Pokala /**
27424f93aa0SRavi Pokala  * smbus_writew() method. Thin wrapper around imcsmb_transfer().
27524f93aa0SRavi Pokala  *
27624f93aa0SRavi Pokala  * @author Joe Kloss
27724f93aa0SRavi Pokala  *
27824f93aa0SRavi Pokala  * @param[in] dev
27924f93aa0SRavi Pokala  *
28024f93aa0SRavi Pokala  * @param[in] slave
28124f93aa0SRavi Pokala  *      The SMBus address of the target device.
28224f93aa0SRavi Pokala  *
28324f93aa0SRavi Pokala  * @param[in] cmd
28424f93aa0SRavi Pokala  *      The SMBus command for the target device; this is the offset for SPDs,
28524f93aa0SRavi Pokala  *      or the register number for TSODs.
28624f93aa0SRavi Pokala  *
28724f93aa0SRavi Pokala  * @param[in] word
28824f93aa0SRavi Pokala  *      The word to write.
28924f93aa0SRavi Pokala  */
29024f93aa0SRavi Pokala static int
29124f93aa0SRavi Pokala imcsmb_writew(device_t dev, u_char slave, char cmd, short word)
29224f93aa0SRavi Pokala {
29324f93aa0SRavi Pokala 
29424f93aa0SRavi Pokala 	return (imcsmb_transfer(dev, slave, cmd, &word, TRUE, TRUE));
29524f93aa0SRavi Pokala }
29624f93aa0SRavi Pokala 
29724f93aa0SRavi Pokala /**
29824f93aa0SRavi Pokala  * Manipulate the PCI control registers to read data from or write data to the
29924f93aa0SRavi Pokala  * SMBus controller.
30024f93aa0SRavi Pokala  *
30124f93aa0SRavi Pokala  * @author Joe Kloss, rpokala
30224f93aa0SRavi Pokala  *
30324f93aa0SRavi Pokala  * @param[in] dev
30424f93aa0SRavi Pokala  *
30524f93aa0SRavi Pokala  * @param[in] slave
30624f93aa0SRavi Pokala  *      The SMBus address of the target device.
30724f93aa0SRavi Pokala  *
30824f93aa0SRavi Pokala  * @param[in] cmd
30924f93aa0SRavi Pokala  *      The SMBus command for the target device; this is the offset for SPDs,
31024f93aa0SRavi Pokala  *      or the register number for TSODs.
31124f93aa0SRavi Pokala  *
31224f93aa0SRavi Pokala  * @param[in,out] data
31324f93aa0SRavi Pokala  *      Pointer to either the value to be written, or where to place the value
31424f93aa0SRavi Pokala  *      which was read.
31524f93aa0SRavi Pokala  *
31624f93aa0SRavi Pokala  * @param[in] word_op
31724f93aa0SRavi Pokala  *      Bool: is this a word operation?
31824f93aa0SRavi Pokala  *
31924f93aa0SRavi Pokala  * @param[in] write_op
32024f93aa0SRavi Pokala  *      Bool: is this a write operation?
32124f93aa0SRavi Pokala  */
32224f93aa0SRavi Pokala static int
32324f93aa0SRavi Pokala imcsmb_transfer(device_t dev, u_char slave, char cmd, void *data, int word_op,
32424f93aa0SRavi Pokala     int write_op)
32524f93aa0SRavi Pokala {
32624f93aa0SRavi Pokala 	struct imcsmb_softc *sc;
32724f93aa0SRavi Pokala 	int i;
32824f93aa0SRavi Pokala 	int rc;
32924f93aa0SRavi Pokala 	uint32_t cmd_val;
33024f93aa0SRavi Pokala 	uint32_t cntl_val;
33124f93aa0SRavi Pokala 	uint32_t orig_cntl_val;
33224f93aa0SRavi Pokala 	uint32_t stat_val;
33324f93aa0SRavi Pokala 	uint16_t *word;
33424f93aa0SRavi Pokala 	uint16_t lword;
33524f93aa0SRavi Pokala 	uint8_t *byte;
33624f93aa0SRavi Pokala 	uint8_t lbyte;
33724f93aa0SRavi Pokala 
33824f93aa0SRavi Pokala 	sc = device_get_softc(dev);
33924f93aa0SRavi Pokala 	byte = data;
34024f93aa0SRavi Pokala 	word = data;
34124f93aa0SRavi Pokala 	lbyte = *byte;
34224f93aa0SRavi Pokala 	lword = *word;
34324f93aa0SRavi Pokala 
34424f93aa0SRavi Pokala 	/* We modify the value of the control register; save the original, so
34524f93aa0SRavi Pokala 	 * we can restore it later
34624f93aa0SRavi Pokala 	 */
34724f93aa0SRavi Pokala 	orig_cntl_val = pci_read_config(sc->imcsmb_pci,
34824f93aa0SRavi Pokala 	    sc->regs->smb_cntl, 4);
34924f93aa0SRavi Pokala 	cntl_val = orig_cntl_val;
35024f93aa0SRavi Pokala 
35124f93aa0SRavi Pokala 	/*
35224f93aa0SRavi Pokala 	 * Set up the SMBCNTL register
35324f93aa0SRavi Pokala 	 */
35424f93aa0SRavi Pokala 
35524f93aa0SRavi Pokala 	/* [31:28] Clear the existing value of the DTI bits, then set them to
35624f93aa0SRavi Pokala 	 * the four high bits of the slave address.
35724f93aa0SRavi Pokala 	 */
35824f93aa0SRavi Pokala 	cntl_val &= ~IMCSMB_CNTL_DTI_MASK;
35924f93aa0SRavi Pokala 	cntl_val |= ((uint32_t) slave & 0xf0) << 24;
36024f93aa0SRavi Pokala 
36124f93aa0SRavi Pokala 	/* [27:27] Set the CLK_OVERRIDE bit, to enable normal operation */
36224f93aa0SRavi Pokala 	cntl_val |= IMCSMB_CNTL_CLK_OVERRIDE;
36324f93aa0SRavi Pokala 
36424f93aa0SRavi Pokala 	/* [26:26] Clear the WRITE_DISABLE bit; the datasheet says this isn't
36524f93aa0SRavi Pokala 	 * necessary, but empirically, it is.
36624f93aa0SRavi Pokala 	 */
36724f93aa0SRavi Pokala 	cntl_val &= ~IMCSMB_CNTL_WRITE_DISABLE_BIT;
36824f93aa0SRavi Pokala 
36924f93aa0SRavi Pokala 	/* [9:9] Clear the POLL_EN bit, to stop the hardware TSOD polling. */
37024f93aa0SRavi Pokala 	cntl_val &= ~IMCSMB_CNTL_POLL_EN;
37124f93aa0SRavi Pokala 
37224f93aa0SRavi Pokala 	/*
37324f93aa0SRavi Pokala 	 * Set up the SMBCMD register
37424f93aa0SRavi Pokala 	 */
37524f93aa0SRavi Pokala 
37624f93aa0SRavi Pokala 	/* [31:31] Set the TRIGGER bit; when this gets written, the controller
37724f93aa0SRavi Pokala 	 * will issue the command.
37824f93aa0SRavi Pokala 	 */
37924f93aa0SRavi Pokala 	cmd_val = IMCSMB_CMD_TRIGGER_BIT;
38024f93aa0SRavi Pokala 
38124f93aa0SRavi Pokala 	/* [29:29] For word operations, set the WORD_ACCESS bit. */
38224f93aa0SRavi Pokala 	if (word_op) {
38324f93aa0SRavi Pokala 		cmd_val |= IMCSMB_CMD_WORD_ACCESS;
38424f93aa0SRavi Pokala 	}
38524f93aa0SRavi Pokala 
38624f93aa0SRavi Pokala 	/* [27:27] For write operations, set the WRITE bit. */
38724f93aa0SRavi Pokala 	if (write_op) {
38824f93aa0SRavi Pokala 		cmd_val |= IMCSMB_CMD_WRITE_BIT;
38924f93aa0SRavi Pokala 	}
39024f93aa0SRavi Pokala 
39124f93aa0SRavi Pokala 	/* [26:24] The three non-DTI, non-R/W bits of the slave address. */
39224f93aa0SRavi Pokala 	cmd_val |= (uint32_t) ((slave & 0xe) << 23);
39324f93aa0SRavi Pokala 
39424f93aa0SRavi Pokala 	/* [23:16] The command (offset in the case of an EEPROM, or register in
39524f93aa0SRavi Pokala 	 * the case of TSOD or NVDIMM controller).
39624f93aa0SRavi Pokala 	 */
39724f93aa0SRavi Pokala 	cmd_val |= (uint32_t) ((uint8_t) cmd << 16);
39824f93aa0SRavi Pokala 
39924f93aa0SRavi Pokala 	/* [15:0] The data to be written for a write operation. */
40024f93aa0SRavi Pokala 	if (write_op) {
40124f93aa0SRavi Pokala 		if (word_op) {
40224f93aa0SRavi Pokala 			/* The datasheet says the controller uses different
40324f93aa0SRavi Pokala 			 * endianness for word operations on I2C vs SMBus!
40424f93aa0SRavi Pokala 			 *      I2C: [15:8] = MSB; [7:0] = LSB
40524f93aa0SRavi Pokala 			 *      SMB: [15:8] = LSB; [7:0] = MSB
40624f93aa0SRavi Pokala 			 * As a practical matter, this controller is very
40724f93aa0SRavi Pokala 			 * specifically for use with DIMMs, the SPD (and
40824f93aa0SRavi Pokala 			 * NVDIMM controllers) are only accessed as bytes,
40924f93aa0SRavi Pokala 			 * the temperature sensor is only accessed as words, and
41024f93aa0SRavi Pokala 			 * the temperature sensors are I2C. Thus, byte-swap the
41124f93aa0SRavi Pokala 			 * word.
41224f93aa0SRavi Pokala 			 */
41324f93aa0SRavi Pokala 			lword = htobe16(lword);
41424f93aa0SRavi Pokala 		} else {
41524f93aa0SRavi Pokala 			/* For byte operations, the data goes in the LSB, and
41624f93aa0SRavi Pokala 			 * the MSB is a don't care.
41724f93aa0SRavi Pokala 			 */
41824f93aa0SRavi Pokala 			lword = (uint16_t) (lbyte & 0xff);
41924f93aa0SRavi Pokala 		}
42024f93aa0SRavi Pokala 		cmd_val |= lword;
42124f93aa0SRavi Pokala 	}
42224f93aa0SRavi Pokala 
42324f93aa0SRavi Pokala 	/* Write the updated value to the control register first, to disable
42424f93aa0SRavi Pokala 	 * the hardware TSOD polling.
42524f93aa0SRavi Pokala 	 */
42624f93aa0SRavi Pokala 	pci_write_config(sc->imcsmb_pci, sc->regs->smb_cntl, cntl_val, 4);
42724f93aa0SRavi Pokala 
42824f93aa0SRavi Pokala 	/* Poll on the BUSY bit in the status register until clear, or timeout.
42924f93aa0SRavi Pokala 	 * We just cleared the auto-poll bit, so we need to make sure the device
43024f93aa0SRavi Pokala 	 * is idle before issuing a command. We can safely timeout after 35 ms,
43124f93aa0SRavi Pokala 	 * as this is the maximum time the SMBus spec allows for a transaction.
43224f93aa0SRavi Pokala 	 */
43324f93aa0SRavi Pokala 	for (i = 4; i != 0; i--) {
43424f93aa0SRavi Pokala 		stat_val = pci_read_config(sc->imcsmb_pci, sc->regs->smb_stat,
43524f93aa0SRavi Pokala 		    4);
43624f93aa0SRavi Pokala 		if ((stat_val & IMCSMB_STATUS_BUSY_BIT) == 0) {
43724f93aa0SRavi Pokala 			break;
43824f93aa0SRavi Pokala 		}
43924f93aa0SRavi Pokala 		pause("imcsmb", 10 * hz / 1000);
44024f93aa0SRavi Pokala 	}
44124f93aa0SRavi Pokala 
44224f93aa0SRavi Pokala 	if (i == 0) {
44324f93aa0SRavi Pokala 		device_printf(sc->dev,
44424f93aa0SRavi Pokala 		    "transfer: timeout waiting for device to settle\n");
44524f93aa0SRavi Pokala 	}
44624f93aa0SRavi Pokala 
44724f93aa0SRavi Pokala 	/* Now that polling has stopped, we can write the command register. This
44824f93aa0SRavi Pokala 	 * starts the SMBus command.
44924f93aa0SRavi Pokala 	 */
45024f93aa0SRavi Pokala 	pci_write_config(sc->imcsmb_pci, sc->regs->smb_cmd, cmd_val, 4);
45124f93aa0SRavi Pokala 
45224f93aa0SRavi Pokala 	/* Wait for WRITE_DATA_DONE/READ_DATA_VALID to be set, or timeout and
45324f93aa0SRavi Pokala 	 * fail. We wait up to 35ms.
45424f93aa0SRavi Pokala 	 */
45524f93aa0SRavi Pokala 	for (i = 35000; i != 0; i -= 10)
45624f93aa0SRavi Pokala 	{
45724f93aa0SRavi Pokala 		DELAY(10);
45824f93aa0SRavi Pokala 		stat_val = pci_read_config(sc->imcsmb_pci, sc->regs->smb_stat,
45924f93aa0SRavi Pokala 		    4);
46024f93aa0SRavi Pokala 		/* For a write, the bits holding the data contain the data being
46124f93aa0SRavi Pokala 		 * written. You'd think that would cause the READ_DATA_VALID bit
46224f93aa0SRavi Pokala 		 * to be cleared, because the data bits no longer contain valid
46324f93aa0SRavi Pokala 		 * data from the most recent read operation. While that would be
46424f93aa0SRavi Pokala 		 * logical, that's not the case here: READ_DATA_VALID is only
46524f93aa0SRavi Pokala 		 * cleared when starting a read operation, and WRITE_DATA_DONE
46624f93aa0SRavi Pokala 		 * is only cleared when starting a write operation.
46724f93aa0SRavi Pokala 		 */
46824f93aa0SRavi Pokala 		if (write_op) {
46924f93aa0SRavi Pokala 			if ((stat_val & IMCSMB_STATUS_WRITE_DATA_DONE) != 0) {
47024f93aa0SRavi Pokala 				break;
47124f93aa0SRavi Pokala 			}
47224f93aa0SRavi Pokala 		} else {
47324f93aa0SRavi Pokala 			if ((stat_val & IMCSMB_STATUS_READ_DATA_VALID) != 0) {
47424f93aa0SRavi Pokala 				break;
47524f93aa0SRavi Pokala 			}
47624f93aa0SRavi Pokala 		}
47724f93aa0SRavi Pokala 	}
47824f93aa0SRavi Pokala 	if (i == 0) {
47924f93aa0SRavi Pokala 		rc = SMB_ETIMEOUT;
48024f93aa0SRavi Pokala 		device_printf(dev, "transfer timeout\n");
48124f93aa0SRavi Pokala 		goto out;
48224f93aa0SRavi Pokala 	}
48324f93aa0SRavi Pokala 
48424f93aa0SRavi Pokala 	/* It is generally the case that this bit indicates non-ACK, but it
48524f93aa0SRavi Pokala 	 * could also indicate other bus errors. There's no way to tell the
48624f93aa0SRavi Pokala 	 * difference.
48724f93aa0SRavi Pokala 	 */
48824f93aa0SRavi Pokala 	if ((stat_val & IMCSMB_STATUS_BUS_ERROR_BIT) != 0) {
48924f93aa0SRavi Pokala 		/* While it is not documented, empirically, SPD page-change
49024f93aa0SRavi Pokala 		 * commands (writes with DTI = 0x60) always complete with the
49124f93aa0SRavi Pokala 		 * error bit set. So, ignore it in those cases.
49224f93aa0SRavi Pokala 		 */
49324f93aa0SRavi Pokala 		if ((slave & 0xf0) != 0x60) {
49424f93aa0SRavi Pokala 			rc = SMB_ENOACK;
49524f93aa0SRavi Pokala 			goto out;
49624f93aa0SRavi Pokala 		}
49724f93aa0SRavi Pokala 	}
49824f93aa0SRavi Pokala 
49924f93aa0SRavi Pokala 	/* For a read operation, copy the data out */
50024f93aa0SRavi Pokala 	if (write_op == 0) {
50124f93aa0SRavi Pokala 		if (word_op) {
50224f93aa0SRavi Pokala 			/* The data is returned in bits [15:0]; as discussed
50324f93aa0SRavi Pokala 			 * above, byte-swap.
50424f93aa0SRavi Pokala 			 */
50524f93aa0SRavi Pokala 			lword = (uint16_t) (stat_val & 0xffff);
50624f93aa0SRavi Pokala 			lword = htobe16(lword);
50724f93aa0SRavi Pokala 			*word = lword;
50824f93aa0SRavi Pokala 		} else {
50924f93aa0SRavi Pokala 			/* The data is returned in bits [7:0] */
51024f93aa0SRavi Pokala 			lbyte = (uint8_t) (stat_val & 0xff);
51124f93aa0SRavi Pokala 			*byte = lbyte;
51224f93aa0SRavi Pokala 		}
51324f93aa0SRavi Pokala 	}
51424f93aa0SRavi Pokala 
51524f93aa0SRavi Pokala 	/* A lack of an error is, de facto, success. */
51624f93aa0SRavi Pokala 	rc = SMB_ENOERR;
51724f93aa0SRavi Pokala 
51824f93aa0SRavi Pokala out:
51924f93aa0SRavi Pokala 	/* Restore the original value of the control register. */
52024f93aa0SRavi Pokala 	pci_write_config(sc->imcsmb_pci, sc->regs->smb_cntl, orig_cntl_val, 4);
52124f93aa0SRavi Pokala 	return (rc);
52224f93aa0SRavi Pokala }
52324f93aa0SRavi Pokala 
52424f93aa0SRavi Pokala /* Device methods */
52524f93aa0SRavi Pokala static device_method_t imcsmb_methods[] = {
52624f93aa0SRavi Pokala 	/* Device interface */
52724f93aa0SRavi Pokala 	DEVMETHOD(device_attach,	imcsmb_attach),
52824f93aa0SRavi Pokala 	DEVMETHOD(device_detach,	imcsmb_detach),
52924f93aa0SRavi Pokala 	DEVMETHOD(device_probe,		imcsmb_probe),
53024f93aa0SRavi Pokala 
53124f93aa0SRavi Pokala 	/* smbus methods */
53224f93aa0SRavi Pokala 	DEVMETHOD(smbus_callback,	imcsmb_callback),
53324f93aa0SRavi Pokala 	DEVMETHOD(smbus_readb,		imcsmb_readb),
53424f93aa0SRavi Pokala 	DEVMETHOD(smbus_readw,		imcsmb_readw),
53524f93aa0SRavi Pokala 	DEVMETHOD(smbus_writeb,		imcsmb_writeb),
53624f93aa0SRavi Pokala 	DEVMETHOD(smbus_writew,		imcsmb_writew),
53724f93aa0SRavi Pokala 
53824f93aa0SRavi Pokala 	DEVMETHOD_END
53924f93aa0SRavi Pokala };
54024f93aa0SRavi Pokala 
54124f93aa0SRavi Pokala static driver_t imcsmb_driver = {
54224f93aa0SRavi Pokala 	.name = "imcsmb",
54324f93aa0SRavi Pokala 	.methods = imcsmb_methods,
54424f93aa0SRavi Pokala 	.size = sizeof(struct imcsmb_softc),
54524f93aa0SRavi Pokala };
54624f93aa0SRavi Pokala 
547*dfee3204SJohn Baldwin DRIVER_MODULE(imcsmb, imcsmb_pci, imcsmb_driver, 0, 0);
54824f93aa0SRavi Pokala MODULE_DEPEND(imcsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
54924f93aa0SRavi Pokala MODULE_VERSION(imcsmb, 1);
55024f93aa0SRavi Pokala 
551c6d39765SJohn Baldwin DRIVER_MODULE(smbus, imcsmb, smbus_driver, 0, 0);
55224f93aa0SRavi Pokala 
55324f93aa0SRavi Pokala /* vi: set ts=8 sw=4 sts=8 noet: */
554