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