qemu/hw/i2c/pm_smbus.c
<<
>>
Prefs
   1/*
   2 * PC SMBus implementation
   3 * splitted from acpi.c
   4 *
   5 * Copyright (c) 2006 Fabrice Bellard
   6 *
   7 * This library is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU Lesser General Public
   9 * License version 2 as published by the Free Software Foundation.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see
  18 * <http://www.gnu.org/licenses/>.
  19 */
  20#include "qemu/osdep.h"
  21#include "hw/hw.h"
  22#include "hw/i2c/pm_smbus.h"
  23#include "hw/i2c/smbus.h"
  24
  25#define SMBHSTSTS       0x00
  26#define SMBHSTCNT       0x02
  27#define SMBHSTCMD       0x03
  28#define SMBHSTADD       0x04
  29#define SMBHSTDAT0      0x05
  30#define SMBHSTDAT1      0x06
  31#define SMBBLKDAT       0x07
  32#define SMBAUXCTL       0x0d
  33
  34#define STS_HOST_BUSY   (1 << 0)
  35#define STS_INTR        (1 << 1)
  36#define STS_DEV_ERR     (1 << 2)
  37#define STS_BUS_ERR     (1 << 3)
  38#define STS_FAILED      (1 << 4)
  39#define STS_SMBALERT    (1 << 5)
  40#define STS_INUSE_STS   (1 << 6)
  41#define STS_BYTE_DONE   (1 << 7)
  42/* Signs of successfully transaction end :
  43*  ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR )
  44*/
  45
  46#define CTL_INTREN      (1 << 0)
  47#define CTL_KILL        (1 << 1)
  48#define CTL_LAST_BYTE   (1 << 5)
  49#define CTL_START       (1 << 6)
  50#define CTL_PEC_EN      (1 << 7)
  51#define CTL_RETURN_MASK 0x1f
  52
  53#define PROT_QUICK          0
  54#define PROT_BYTE           1
  55#define PROT_BYTE_DATA      2
  56#define PROT_WORD_DATA      3
  57#define PROT_PROC_CALL      4
  58#define PROT_BLOCK_DATA     5
  59#define PROT_I2C_BLOCK_READ 6
  60
  61#define AUX_PEC       (1 << 0)
  62#define AUX_BLK       (1 << 1)
  63#define AUX_MASK      0x3
  64
  65/*#define DEBUG*/
  66
  67#ifdef DEBUG
  68# define SMBUS_DPRINTF(format, ...)     printf(format, ## __VA_ARGS__)
  69#else
  70# define SMBUS_DPRINTF(format, ...)     do { } while (0)
  71#endif
  72
  73
  74static void smb_transaction(PMSMBus *s)
  75{
  76    uint8_t prot = (s->smb_ctl >> 2) & 0x07;
  77    uint8_t read = s->smb_addr & 0x01;
  78    uint8_t cmd = s->smb_cmd;
  79    uint8_t addr = s->smb_addr >> 1;
  80    I2CBus *bus = s->smbus;
  81    int ret;
  82
  83    SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
  84    /* Transaction isn't exec if STS_DEV_ERR bit set */
  85    if ((s->smb_stat & STS_DEV_ERR) != 0)  {
  86        goto error;
  87    }
  88
  89    switch(prot) {
  90    case PROT_QUICK:
  91        ret = smbus_quick_command(bus, addr, read);
  92        goto done;
  93    case PROT_BYTE:
  94        if (read) {
  95            ret = smbus_receive_byte(bus, addr);
  96            goto data8;
  97        } else {
  98            ret = smbus_send_byte(bus, addr, cmd);
  99            goto done;
 100        }
 101    case PROT_BYTE_DATA:
 102        if (read) {
 103            ret = smbus_read_byte(bus, addr, cmd);
 104            goto data8;
 105        } else {
 106            ret = smbus_write_byte(bus, addr, cmd, s->smb_data0);
 107            goto done;
 108        }
 109        break;
 110    case PROT_WORD_DATA:
 111        if (read) {
 112            ret = smbus_read_word(bus, addr, cmd);
 113            goto data16;
 114        } else {
 115            ret = smbus_write_word(bus, addr, cmd,
 116                                   (s->smb_data1 << 8) | s->smb_data0);
 117            goto done;
 118        }
 119        break;
 120    case PROT_I2C_BLOCK_READ:
 121        if (read) {
 122            int xfersize = s->smb_data0;
 123            if (xfersize > sizeof(s->smb_data)) {
 124                xfersize = sizeof(s->smb_data);
 125            }
 126            ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data,
 127                                   xfersize, false, true);
 128            goto data8;
 129        } else {
 130            /* The manual says the behavior is undefined, just set DEV_ERR. */
 131            goto error;
 132        }
 133        break;
 134    case PROT_BLOCK_DATA:
 135        if (read) {
 136            ret = smbus_read_block(bus, addr, cmd, s->smb_data,
 137                                   sizeof(s->smb_data), !s->i2c_enable,
 138                                   !s->i2c_enable);
 139            if (ret < 0) {
 140                goto error;
 141            }
 142            s->smb_index = 0;
 143            s->op_done = false;
 144            if (s->smb_auxctl & AUX_BLK) {
 145                s->smb_stat |= STS_INTR;
 146            } else {
 147                s->smb_blkdata = s->smb_data[0];
 148                s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
 149            }
 150            s->smb_data0 = ret;
 151            goto out;
 152        } else {
 153            if (s->smb_auxctl & AUX_BLK) {
 154                if (s->smb_index != s->smb_data0) {
 155                    s->smb_index = 0;
 156                    goto error;
 157                }
 158                /* Data is already all written to the queue, just do
 159                   the operation. */
 160                s->smb_index = 0;
 161                ret = smbus_write_block(bus, addr, cmd, s->smb_data,
 162                                        s->smb_data0, !s->i2c_enable);
 163                if (ret < 0) {
 164                    goto error;
 165                }
 166                s->op_done = true;
 167                s->smb_stat |= STS_INTR;
 168                s->smb_stat &= ~STS_HOST_BUSY;
 169            } else {
 170                s->op_done = false;
 171                s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
 172                s->smb_data[0] = s->smb_blkdata;
 173                s->smb_index = 0;
 174                ret = 0;
 175            }
 176            goto out;
 177        }
 178        break;
 179    default:
 180        goto error;
 181    }
 182    abort();
 183
 184data16:
 185    if (ret < 0) {
 186        goto error;
 187    }
 188    s->smb_data1 = ret >> 8;
 189data8:
 190    if (ret < 0) {
 191        goto error;
 192    }
 193    s->smb_data0 = ret;
 194done:
 195    if (ret < 0) {
 196        goto error;
 197    }
 198    s->smb_stat |= STS_INTR;
 199out:
 200    return;
 201
 202error:
 203    s->smb_stat |= STS_DEV_ERR;
 204    return;
 205}
 206
 207static void smb_transaction_start(PMSMBus *s)
 208{
 209    if (s->smb_ctl & CTL_INTREN) {
 210        smb_transaction(s);
 211    } else {
 212        /* Do not execute immediately the command; it will be
 213         * executed when guest will read SMB_STAT register.  This
 214         * is to work around a bug in AMIBIOS (that is working
 215         * around another bug in some specific hardware) where
 216         * it waits for STS_HOST_BUSY to be set before waiting
 217         * checking for status.  If STS_HOST_BUSY doesn't get
 218         * set, it gets stuck. */
 219        s->smb_stat |= STS_HOST_BUSY;
 220    }
 221}
 222
 223static bool
 224smb_irq_value(PMSMBus *s)
 225{
 226    return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN);
 227}
 228
 229static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
 230                              unsigned width)
 231{
 232    PMSMBus *s = opaque;
 233
 234    SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx
 235                  " val=0x%02" PRIx64 "\n", addr, val);
 236    switch(addr) {
 237    case SMBHSTSTS:
 238        s->smb_stat &= ~(val & ~STS_HOST_BUSY);
 239        if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
 240            uint8_t read = s->smb_addr & 0x01;
 241
 242            s->smb_index++;
 243            if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
 244                s->smb_index = 0;
 245            }
 246            if (!read && s->smb_index == s->smb_data0) {
 247                uint8_t prot = (s->smb_ctl >> 2) & 0x07;
 248                uint8_t cmd = s->smb_cmd;
 249                uint8_t addr = s->smb_addr >> 1;
 250                int ret;
 251
 252                if (prot == PROT_I2C_BLOCK_READ) {
 253                    s->smb_stat |= STS_DEV_ERR;
 254                    goto out;
 255                }
 256
 257                ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data,
 258                                        s->smb_data0, !s->i2c_enable);
 259                if (ret < 0) {
 260                    s->smb_stat |= STS_DEV_ERR;
 261                    goto out;
 262                }
 263                s->op_done = true;
 264                s->smb_stat |= STS_INTR;
 265                s->smb_stat &= ~STS_HOST_BUSY;
 266            } else if (!read) {
 267                s->smb_data[s->smb_index] = s->smb_blkdata;
 268                s->smb_stat |= STS_BYTE_DONE;
 269            } else if (s->smb_ctl & CTL_LAST_BYTE) {
 270                s->op_done = true;
 271                s->smb_blkdata = s->smb_data[s->smb_index];
 272                s->smb_index = 0;
 273                s->smb_stat |= STS_INTR;
 274                s->smb_stat &= ~STS_HOST_BUSY;
 275            } else {
 276                s->smb_blkdata = s->smb_data[s->smb_index];
 277                s->smb_stat |= STS_BYTE_DONE;
 278            }
 279        }
 280        break;
 281    case SMBHSTCNT:
 282        s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */
 283        if (val & CTL_START) {
 284            if (!s->op_done) {
 285                s->smb_index = 0;
 286                s->op_done = true;
 287            }
 288            smb_transaction_start(s);
 289        }
 290        if (s->smb_ctl & CTL_KILL) {
 291            s->op_done = true;
 292            s->smb_index = 0;
 293            s->smb_stat |= STS_FAILED;
 294            s->smb_stat &= ~STS_HOST_BUSY;
 295        }
 296        break;
 297    case SMBHSTCMD:
 298        s->smb_cmd = val;
 299        break;
 300    case SMBHSTADD:
 301        s->smb_addr = val;
 302        break;
 303    case SMBHSTDAT0:
 304        s->smb_data0 = val;
 305        break;
 306    case SMBHSTDAT1:
 307        s->smb_data1 = val;
 308        break;
 309    case SMBBLKDAT:
 310        if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
 311            s->smb_index = 0;
 312        }
 313        if (s->smb_auxctl & AUX_BLK) {
 314            s->smb_data[s->smb_index++] = val;
 315        } else {
 316            s->smb_blkdata = val;
 317        }
 318        break;
 319    case SMBAUXCTL:
 320        s->smb_auxctl = val & AUX_MASK;
 321        break;
 322    default:
 323        break;
 324    }
 325
 326 out:
 327    if (s->set_irq) {
 328        s->set_irq(s, smb_irq_value(s));
 329    }
 330}
 331
 332static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
 333{
 334    PMSMBus *s = opaque;
 335    uint32_t val;
 336
 337    switch(addr) {
 338    case SMBHSTSTS:
 339        val = s->smb_stat;
 340        if (s->smb_stat & STS_HOST_BUSY) {
 341            /* execute command now */
 342            s->smb_stat &= ~STS_HOST_BUSY;
 343            smb_transaction(s);
 344        }
 345        break;
 346    case SMBHSTCNT:
 347        val = s->smb_ctl & CTL_RETURN_MASK;
 348        break;
 349    case SMBHSTCMD:
 350        val = s->smb_cmd;
 351        break;
 352    case SMBHSTADD:
 353        val = s->smb_addr;
 354        break;
 355    case SMBHSTDAT0:
 356        val = s->smb_data0;
 357        break;
 358    case SMBHSTDAT1:
 359        val = s->smb_data1;
 360        break;
 361    case SMBBLKDAT:
 362        if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
 363            s->smb_index = 0;
 364        }
 365        if (s->smb_auxctl & AUX_BLK) {
 366            val = s->smb_data[s->smb_index++];
 367            if (!s->op_done && s->smb_index == s->smb_data0) {
 368                s->op_done = true;
 369                s->smb_index = 0;
 370                s->smb_stat &= ~STS_HOST_BUSY;
 371            }
 372        } else {
 373            val = s->smb_blkdata;
 374        }
 375        break;
 376    case SMBAUXCTL:
 377        val = s->smb_auxctl;
 378        break;
 379    default:
 380        val = 0;
 381        break;
 382    }
 383    SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n",
 384                  addr, val);
 385
 386    if (s->set_irq) {
 387        s->set_irq(s, smb_irq_value(s));
 388    }
 389
 390    return val;
 391}
 392
 393static void pm_smbus_reset(PMSMBus *s)
 394{
 395    s->op_done = true;
 396    s->smb_index = 0;
 397    s->smb_stat = 0;
 398}
 399
 400static const MemoryRegionOps pm_smbus_ops = {
 401    .read = smb_ioport_readb,
 402    .write = smb_ioport_writeb,
 403    .valid.min_access_size = 1,
 404    .valid.max_access_size = 1,
 405    .endianness = DEVICE_LITTLE_ENDIAN,
 406};
 407
 408void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
 409{
 410    smb->op_done = true;
 411    smb->reset = pm_smbus_reset;
 412    smb->smbus = i2c_init_bus(parent, "i2c");
 413    if (force_aux_blk) {
 414        smb->smb_auxctl |= AUX_BLK;
 415    }
 416    memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb,
 417                          "pm-smbus", 64);
 418}
 419