qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

pm_smbus.c (14527B)


      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.1 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 
     21 #include "qemu/osdep.h"
     22 #include "hw/boards.h"
     23 #include "hw/i2c/pm_smbus.h"
     24 #include "hw/i2c/smbus_master.h"
     25 #include "migration/vmstate.h"
     26 
     27 #define SMBHSTSTS       0x00
     28 #define SMBHSTCNT       0x02
     29 #define SMBHSTCMD       0x03
     30 #define SMBHSTADD       0x04
     31 #define SMBHSTDAT0      0x05
     32 #define SMBHSTDAT1      0x06
     33 #define SMBBLKDAT       0x07
     34 #define SMBAUXCTL       0x0d
     35 
     36 #define STS_HOST_BUSY   (1 << 0)
     37 #define STS_INTR        (1 << 1)
     38 #define STS_DEV_ERR     (1 << 2)
     39 #define STS_BUS_ERR     (1 << 3)
     40 #define STS_FAILED      (1 << 4)
     41 #define STS_SMBALERT    (1 << 5)
     42 #define STS_INUSE_STS   (1 << 6)
     43 #define STS_BYTE_DONE   (1 << 7)
     44 /* Signs of successfully transaction end :
     45 *  ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR )
     46 */
     47 
     48 #define CTL_INTREN      (1 << 0)
     49 #define CTL_KILL        (1 << 1)
     50 #define CTL_LAST_BYTE   (1 << 5)
     51 #define CTL_START       (1 << 6)
     52 #define CTL_PEC_EN      (1 << 7)
     53 #define CTL_RETURN_MASK 0x1f
     54 
     55 #define PROT_QUICK          0
     56 #define PROT_BYTE           1
     57 #define PROT_BYTE_DATA      2
     58 #define PROT_WORD_DATA      3
     59 #define PROT_PROC_CALL      4
     60 #define PROT_BLOCK_DATA     5
     61 #define PROT_I2C_BLOCK_READ 6
     62 
     63 #define AUX_PEC       (1 << 0)
     64 #define AUX_BLK       (1 << 1)
     65 #define AUX_MASK      0x3
     66 
     67 /*#define DEBUG*/
     68 
     69 #ifdef DEBUG
     70 # define SMBUS_DPRINTF(format, ...)     printf(format, ## __VA_ARGS__)
     71 #else
     72 # define SMBUS_DPRINTF(format, ...)     do { } while (0)
     73 #endif
     74 
     75 
     76 static void smb_transaction(PMSMBus *s)
     77 {
     78     uint8_t prot = (s->smb_ctl >> 2) & 0x07;
     79     uint8_t read = s->smb_addr & 0x01;
     80     uint8_t cmd = s->smb_cmd;
     81     uint8_t addr = s->smb_addr >> 1;
     82     I2CBus *bus = s->smbus;
     83     int ret;
     84 
     85     SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
     86     /* Transaction isn't exec if STS_DEV_ERR bit set */
     87     if ((s->smb_stat & STS_DEV_ERR) != 0)  {
     88         goto error;
     89     }
     90 
     91     switch(prot) {
     92     case PROT_QUICK:
     93         ret = smbus_quick_command(bus, addr, read);
     94         goto done;
     95     case PROT_BYTE:
     96         if (read) {
     97             ret = smbus_receive_byte(bus, addr);
     98             goto data8;
     99         } else {
    100             ret = smbus_send_byte(bus, addr, cmd);
    101             goto done;
    102         }
    103     case PROT_BYTE_DATA:
    104         if (read) {
    105             ret = smbus_read_byte(bus, addr, cmd);
    106             goto data8;
    107         } else {
    108             ret = smbus_write_byte(bus, addr, cmd, s->smb_data0);
    109             goto done;
    110         }
    111         break;
    112     case PROT_WORD_DATA:
    113         if (read) {
    114             ret = smbus_read_word(bus, addr, cmd);
    115             goto data16;
    116         } else {
    117             ret = smbus_write_word(bus, addr, cmd,
    118                                    (s->smb_data1 << 8) | s->smb_data0);
    119             goto done;
    120         }
    121         break;
    122     case PROT_I2C_BLOCK_READ:
    123         /* According to the Linux i2c-i801 driver:
    124          *   NB: page 240 of ICH5 datasheet shows that the R/#W
    125          *   bit should be cleared here, even when reading.
    126          *   However if SPD Write Disable is set (Lynx Point and later),
    127          *   the read will fail if we don't set the R/#W bit.
    128          * So at least Linux may or may not set the read bit here.
    129          * So just ignore the read bit for this command.
    130          */
    131         if (i2c_start_send(bus, addr)) {
    132             goto error;
    133         }
    134         ret = i2c_send(bus, s->smb_data1);
    135         if (ret) {
    136             goto error;
    137         }
    138         if (i2c_start_recv(bus, addr)) {
    139             goto error;
    140         }
    141         s->in_i2c_block_read = true;
    142         s->smb_blkdata = i2c_recv(s->smbus);
    143         s->op_done = false;
    144         s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
    145         goto out;
    146 
    147     case PROT_BLOCK_DATA:
    148         if (read) {
    149             ret = smbus_read_block(bus, addr, cmd, s->smb_data,
    150                                    sizeof(s->smb_data), !s->i2c_enable,
    151                                    !s->i2c_enable);
    152             if (ret < 0) {
    153                 goto error;
    154             }
    155             s->smb_index = 0;
    156             s->op_done = false;
    157             if (s->smb_auxctl & AUX_BLK) {
    158                 s->smb_stat |= STS_INTR;
    159             } else {
    160                 s->smb_blkdata = s->smb_data[0];
    161                 s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
    162             }
    163             s->smb_data0 = ret;
    164             goto out;
    165         } else {
    166             if (s->smb_auxctl & AUX_BLK) {
    167                 if (s->smb_index != s->smb_data0) {
    168                     s->smb_index = 0;
    169                     goto error;
    170                 }
    171                 /* Data is already all written to the queue, just do
    172                    the operation. */
    173                 s->smb_index = 0;
    174                 ret = smbus_write_block(bus, addr, cmd, s->smb_data,
    175                                         s->smb_data0, !s->i2c_enable);
    176                 if (ret < 0) {
    177                     goto error;
    178                 }
    179                 s->op_done = true;
    180                 s->smb_stat |= STS_INTR;
    181                 s->smb_stat &= ~STS_HOST_BUSY;
    182             } else {
    183                 s->op_done = false;
    184                 s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
    185                 s->smb_data[0] = s->smb_blkdata;
    186                 s->smb_index = 0;
    187             }
    188             goto out;
    189         }
    190         break;
    191     default:
    192         goto error;
    193     }
    194     abort();
    195 
    196 data16:
    197     if (ret < 0) {
    198         goto error;
    199     }
    200     s->smb_data1 = ret >> 8;
    201 data8:
    202     if (ret < 0) {
    203         goto error;
    204     }
    205     s->smb_data0 = ret;
    206 done:
    207     if (ret < 0) {
    208         goto error;
    209     }
    210     s->smb_stat |= STS_INTR;
    211 out:
    212     return;
    213 
    214 error:
    215     s->smb_stat |= STS_DEV_ERR;
    216     return;
    217 }
    218 
    219 static void smb_transaction_start(PMSMBus *s)
    220 {
    221     if (s->smb_ctl & CTL_INTREN) {
    222         smb_transaction(s);
    223         s->start_transaction_on_status_read = false;
    224     } else {
    225         /* Do not execute immediately the command; it will be
    226          * executed when guest will read SMB_STAT register.  This
    227          * is to work around a bug in AMIBIOS (that is working
    228          * around another bug in some specific hardware) where
    229          * it waits for STS_HOST_BUSY to be set before waiting
    230          * checking for status.  If STS_HOST_BUSY doesn't get
    231          * set, it gets stuck. */
    232         s->smb_stat |= STS_HOST_BUSY;
    233         s->start_transaction_on_status_read = true;
    234     }
    235 }
    236 
    237 static bool
    238 smb_irq_value(PMSMBus *s)
    239 {
    240     return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN);
    241 }
    242 
    243 static bool
    244 smb_byte_by_byte(PMSMBus *s)
    245 {
    246     if (s->op_done) {
    247         return false;
    248     }
    249     if (s->in_i2c_block_read) {
    250         return true;
    251     }
    252     return !(s->smb_auxctl & AUX_BLK);
    253 }
    254 
    255 static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
    256                               unsigned width)
    257 {
    258     PMSMBus *s = opaque;
    259     uint8_t clear_byte_done;
    260 
    261     SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx
    262                   " val=0x%02" PRIx64 "\n", addr, val);
    263     switch(addr) {
    264     case SMBHSTSTS:
    265         clear_byte_done = s->smb_stat & val & STS_BYTE_DONE;
    266         s->smb_stat &= ~(val & ~STS_HOST_BUSY);
    267         if (clear_byte_done && smb_byte_by_byte(s)) {
    268             uint8_t read = s->smb_addr & 0x01;
    269 
    270             if (s->in_i2c_block_read) {
    271                 /* See comment below PROT_I2C_BLOCK_READ above. */
    272                 read = 1;
    273             }
    274 
    275             s->smb_index++;
    276             if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
    277                 s->smb_index = 0;
    278             }
    279             if (!read && s->smb_index == s->smb_data0) {
    280                 uint8_t prot = (s->smb_ctl >> 2) & 0x07;
    281                 uint8_t cmd = s->smb_cmd;
    282                 uint8_t addr = s->smb_addr >> 1;
    283                 int ret;
    284 
    285                 if (prot == PROT_I2C_BLOCK_READ) {
    286                     s->smb_stat |= STS_DEV_ERR;
    287                     goto out;
    288                 }
    289 
    290                 ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data,
    291                                         s->smb_data0, !s->i2c_enable);
    292                 if (ret < 0) {
    293                     s->smb_stat |= STS_DEV_ERR;
    294                     goto out;
    295                 }
    296                 s->op_done = true;
    297                 s->smb_stat |= STS_INTR;
    298                 s->smb_stat &= ~STS_HOST_BUSY;
    299             } else if (!read) {
    300                 s->smb_data[s->smb_index] = s->smb_blkdata;
    301                 s->smb_stat |= STS_BYTE_DONE;
    302             } else if (s->smb_ctl & CTL_LAST_BYTE) {
    303                 s->op_done = true;
    304                 if (s->in_i2c_block_read) {
    305                     s->in_i2c_block_read = false;
    306                     s->smb_blkdata = i2c_recv(s->smbus);
    307                     i2c_nack(s->smbus);
    308                     i2c_end_transfer(s->smbus);
    309                 } else {
    310                     s->smb_blkdata = s->smb_data[s->smb_index];
    311                 }
    312                 s->smb_index = 0;
    313                 s->smb_stat |= STS_INTR;
    314                 s->smb_stat &= ~STS_HOST_BUSY;
    315             } else {
    316                 if (s->in_i2c_block_read) {
    317                     s->smb_blkdata = i2c_recv(s->smbus);
    318                 } else {
    319                     s->smb_blkdata = s->smb_data[s->smb_index];
    320                 }
    321                 s->smb_stat |= STS_BYTE_DONE;
    322             }
    323         }
    324         break;
    325     case SMBHSTCNT:
    326         s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */
    327         if (val & CTL_START) {
    328             if (!s->op_done) {
    329                 s->smb_index = 0;
    330                 s->op_done = true;
    331                 if (s->in_i2c_block_read) {
    332                     s->in_i2c_block_read = false;
    333                     i2c_end_transfer(s->smbus);
    334                 }
    335             }
    336             smb_transaction_start(s);
    337         }
    338         if (s->smb_ctl & CTL_KILL) {
    339             s->op_done = true;
    340             s->smb_index = 0;
    341             s->smb_stat |= STS_FAILED;
    342             s->smb_stat &= ~STS_HOST_BUSY;
    343         }
    344         break;
    345     case SMBHSTCMD:
    346         s->smb_cmd = val;
    347         break;
    348     case SMBHSTADD:
    349         s->smb_addr = val;
    350         break;
    351     case SMBHSTDAT0:
    352         s->smb_data0 = val;
    353         break;
    354     case SMBHSTDAT1:
    355         s->smb_data1 = val;
    356         break;
    357     case SMBBLKDAT:
    358         if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
    359             s->smb_index = 0;
    360         }
    361         if (s->smb_auxctl & AUX_BLK) {
    362             s->smb_data[s->smb_index++] = val;
    363         } else {
    364             s->smb_blkdata = val;
    365         }
    366         break;
    367     case SMBAUXCTL:
    368         s->smb_auxctl = val & AUX_MASK;
    369         break;
    370     default:
    371         break;
    372     }
    373 
    374  out:
    375     if (s->set_irq) {
    376         s->set_irq(s, smb_irq_value(s));
    377     }
    378 }
    379 
    380 static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
    381 {
    382     PMSMBus *s = opaque;
    383     uint32_t val;
    384 
    385     switch(addr) {
    386     case SMBHSTSTS:
    387         val = s->smb_stat;
    388         if (s->start_transaction_on_status_read) {
    389             /* execute command now */
    390             s->start_transaction_on_status_read = false;
    391             s->smb_stat &= ~STS_HOST_BUSY;
    392             smb_transaction(s);
    393         }
    394         break;
    395     case SMBHSTCNT:
    396         val = s->smb_ctl & CTL_RETURN_MASK;
    397         break;
    398     case SMBHSTCMD:
    399         val = s->smb_cmd;
    400         break;
    401     case SMBHSTADD:
    402         val = s->smb_addr;
    403         break;
    404     case SMBHSTDAT0:
    405         val = s->smb_data0;
    406         break;
    407     case SMBHSTDAT1:
    408         val = s->smb_data1;
    409         break;
    410     case SMBBLKDAT:
    411         if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) {
    412             if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
    413                 s->smb_index = 0;
    414             }
    415             val = s->smb_data[s->smb_index++];
    416             if (!s->op_done && s->smb_index == s->smb_data0) {
    417                 s->op_done = true;
    418                 s->smb_index = 0;
    419                 s->smb_stat &= ~STS_HOST_BUSY;
    420             }
    421         } else {
    422             val = s->smb_blkdata;
    423         }
    424         break;
    425     case SMBAUXCTL:
    426         val = s->smb_auxctl;
    427         break;
    428     default:
    429         val = 0;
    430         break;
    431     }
    432     SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n",
    433                   addr, val);
    434 
    435     if (s->set_irq) {
    436         s->set_irq(s, smb_irq_value(s));
    437     }
    438 
    439     return val;
    440 }
    441 
    442 static void pm_smbus_reset(PMSMBus *s)
    443 {
    444     s->op_done = true;
    445     s->smb_index = 0;
    446     s->smb_stat = 0;
    447 }
    448 
    449 static const MemoryRegionOps pm_smbus_ops = {
    450     .read = smb_ioport_readb,
    451     .write = smb_ioport_writeb,
    452     .valid.min_access_size = 1,
    453     .valid.max_access_size = 1,
    454     .endianness = DEVICE_LITTLE_ENDIAN,
    455 };
    456 
    457 bool pm_smbus_vmstate_needed(void)
    458 {
    459     MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
    460 
    461     return !mc->smbus_no_migration_support;
    462 }
    463 
    464 const VMStateDescription pmsmb_vmstate = {
    465     .name = "pmsmb",
    466     .version_id = 1,
    467     .minimum_version_id = 1,
    468     .fields = (VMStateField[]) {
    469         VMSTATE_UINT8(smb_stat, PMSMBus),
    470         VMSTATE_UINT8(smb_ctl, PMSMBus),
    471         VMSTATE_UINT8(smb_cmd, PMSMBus),
    472         VMSTATE_UINT8(smb_addr, PMSMBus),
    473         VMSTATE_UINT8(smb_data0, PMSMBus),
    474         VMSTATE_UINT8(smb_data1, PMSMBus),
    475         VMSTATE_UINT32(smb_index, PMSMBus),
    476         VMSTATE_UINT8_ARRAY(smb_data, PMSMBus, PM_SMBUS_MAX_MSG_SIZE),
    477         VMSTATE_UINT8(smb_auxctl, PMSMBus),
    478         VMSTATE_UINT8(smb_blkdata, PMSMBus),
    479         VMSTATE_BOOL(i2c_enable, PMSMBus),
    480         VMSTATE_BOOL(op_done, PMSMBus),
    481         VMSTATE_BOOL(in_i2c_block_read, PMSMBus),
    482         VMSTATE_BOOL(start_transaction_on_status_read, PMSMBus),
    483         VMSTATE_END_OF_LIST()
    484     }
    485 };
    486 
    487 void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
    488 {
    489     smb->op_done = true;
    490     smb->reset = pm_smbus_reset;
    491     smb->smbus = i2c_init_bus(parent, "i2c");
    492     if (force_aux_blk) {
    493         smb->smb_auxctl |= AUX_BLK;
    494     }
    495     memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb,
    496                           "pm-smbus", 64);
    497 }