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 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 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 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 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 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 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 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 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