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 "hw/hw.h" 21 #include "hw/i386/pc.h" 22 #include "hw/i2c/pm_smbus.h" 23 #include "hw/i2c/smbus.h" 24 25 /* no save/load? */ 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 35 #define STS_HOST_BUSY (1) 36 #define STS_INTR (1<<1) 37 #define STS_DEV_ERR (1<<2) 38 #define STS_BUS_ERR (1<<3) 39 #define STS_FAILED (1<<4) 40 #define STS_SMBALERT (1<<5) 41 #define STS_INUSE_STS (1<<6) 42 #define STS_BYTE_DONE (1<<7) 43 /* Signs of successfully transaction end : 44 * ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) 45 */ 46 47 //#define DEBUG 48 49 #ifdef DEBUG 50 # define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) 51 #else 52 # define SMBUS_DPRINTF(format, ...) do { } while (0) 53 #endif 54 55 56 static void smb_transaction(PMSMBus *s) 57 { 58 uint8_t prot = (s->smb_ctl >> 2) & 0x07; 59 uint8_t read = s->smb_addr & 0x01; 60 uint8_t cmd = s->smb_cmd; 61 uint8_t addr = s->smb_addr >> 1; 62 I2CBus *bus = s->smbus; 63 64 SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); 65 /* Transaction isn't exec if STS_DEV_ERR bit set */ 66 if ((s->smb_stat & STS_DEV_ERR) != 0) { 67 goto error; 68 } 69 switch(prot) { 70 case 0x0: 71 smbus_quick_command(bus, addr, read); 72 s->smb_stat |= STS_BYTE_DONE | STS_INTR; 73 break; 74 case 0x1: 75 if (read) { 76 s->smb_data0 = smbus_receive_byte(bus, addr); 77 } else { 78 smbus_send_byte(bus, addr, cmd); 79 } 80 s->smb_stat |= STS_BYTE_DONE | STS_INTR; 81 break; 82 case 0x2: 83 if (read) { 84 s->smb_data0 = smbus_read_byte(bus, addr, cmd); 85 } else { 86 smbus_write_byte(bus, addr, cmd, s->smb_data0); 87 } 88 s->smb_stat |= STS_BYTE_DONE | STS_INTR; 89 break; 90 case 0x3: 91 if (read) { 92 uint16_t val; 93 val = smbus_read_word(bus, addr, cmd); 94 s->smb_data0 = val; 95 s->smb_data1 = val >> 8; 96 } else { 97 smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); 98 } 99 s->smb_stat |= STS_BYTE_DONE | STS_INTR; 100 break; 101 case 0x5: 102 if (read) { 103 s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); 104 } else { 105 smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); 106 } 107 s->smb_stat |= STS_BYTE_DONE | STS_INTR; 108 break; 109 default: 110 goto error; 111 } 112 return; 113 114 error: 115 s->smb_stat |= STS_DEV_ERR; 116 } 117 118 static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, 119 unsigned width) 120 { 121 PMSMBus *s = opaque; 122 123 SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val); 124 switch(addr) { 125 case SMBHSTSTS: 126 s->smb_stat = (~(val & 0xff)) & s->smb_stat; 127 s->smb_index = 0; 128 break; 129 case SMBHSTCNT: 130 s->smb_ctl = val; 131 if (val & 0x40) 132 smb_transaction(s); 133 break; 134 case SMBHSTCMD: 135 s->smb_cmd = val; 136 break; 137 case SMBHSTADD: 138 s->smb_addr = val; 139 break; 140 case SMBHSTDAT0: 141 s->smb_data0 = val; 142 break; 143 case SMBHSTDAT1: 144 s->smb_data1 = val; 145 break; 146 case SMBBLKDAT: 147 s->smb_data[s->smb_index++] = val; 148 if (s->smb_index > 31) 149 s->smb_index = 0; 150 break; 151 default: 152 break; 153 } 154 } 155 156 static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) 157 { 158 PMSMBus *s = opaque; 159 uint32_t val; 160 161 switch(addr) { 162 case SMBHSTSTS: 163 val = s->smb_stat; 164 break; 165 case SMBHSTCNT: 166 s->smb_index = 0; 167 val = s->smb_ctl & 0x1f; 168 break; 169 case SMBHSTCMD: 170 val = s->smb_cmd; 171 break; 172 case SMBHSTADD: 173 val = s->smb_addr; 174 break; 175 case SMBHSTDAT0: 176 val = s->smb_data0; 177 break; 178 case SMBHSTDAT1: 179 val = s->smb_data1; 180 break; 181 case SMBBLKDAT: 182 val = s->smb_data[s->smb_index++]; 183 if (s->smb_index > 31) 184 s->smb_index = 0; 185 break; 186 default: 187 val = 0; 188 break; 189 } 190 SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val); 191 return val; 192 } 193 194 static const MemoryRegionOps pm_smbus_ops = { 195 .read = smb_ioport_readb, 196 .write = smb_ioport_writeb, 197 .valid.min_access_size = 1, 198 .valid.max_access_size = 1, 199 .endianness = DEVICE_LITTLE_ENDIAN, 200 }; 201 202 void pm_smbus_init(DeviceState *parent, PMSMBus *smb) 203 { 204 smb->smbus = i2c_init_bus(parent, "i2c"); 205 memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, 206 "pm-smbus", 64); 207 } 208