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