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 DEBUG 36 37 #ifdef DEBUG 38 # define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) 39 #else 40 # define SMBUS_DPRINTF(format, ...) do { } while (0) 41 #endif 42 43 44 static void smb_transaction(PMSMBus *s) 45 { 46 uint8_t prot = (s->smb_ctl >> 2) & 0x07; 47 uint8_t read = s->smb_addr & 0x01; 48 uint8_t cmd = s->smb_cmd; 49 uint8_t addr = s->smb_addr >> 1; 50 i2c_bus *bus = s->smbus; 51 52 SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); 53 switch(prot) { 54 case 0x0: 55 smbus_quick_command(bus, addr, read); 56 break; 57 case 0x1: 58 if (read) { 59 s->smb_data0 = smbus_receive_byte(bus, addr); 60 } else { 61 smbus_send_byte(bus, addr, cmd); 62 } 63 break; 64 case 0x2: 65 if (read) { 66 s->smb_data0 = smbus_read_byte(bus, addr, cmd); 67 } else { 68 smbus_write_byte(bus, addr, cmd, s->smb_data0); 69 } 70 break; 71 case 0x3: 72 if (read) { 73 uint16_t val; 74 val = smbus_read_word(bus, addr, cmd); 75 s->smb_data0 = val; 76 s->smb_data1 = val >> 8; 77 } else { 78 smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); 79 } 80 break; 81 case 0x5: 82 if (read) { 83 s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); 84 } else { 85 smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); 86 } 87 break; 88 default: 89 goto error; 90 } 91 return; 92 93 error: 94 s->smb_stat |= 0x04; 95 } 96 97 static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, 98 unsigned width) 99 { 100 PMSMBus *s = opaque; 101 102 SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val); 103 switch(addr) { 104 case SMBHSTSTS: 105 s->smb_stat = 0; 106 s->smb_index = 0; 107 break; 108 case SMBHSTCNT: 109 s->smb_ctl = val; 110 if (val & 0x40) 111 smb_transaction(s); 112 break; 113 case SMBHSTCMD: 114 s->smb_cmd = val; 115 break; 116 case SMBHSTADD: 117 s->smb_addr = val; 118 break; 119 case SMBHSTDAT0: 120 s->smb_data0 = val; 121 break; 122 case SMBHSTDAT1: 123 s->smb_data1 = val; 124 break; 125 case SMBBLKDAT: 126 s->smb_data[s->smb_index++] = val; 127 if (s->smb_index > 31) 128 s->smb_index = 0; 129 break; 130 default: 131 break; 132 } 133 } 134 135 static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) 136 { 137 PMSMBus *s = opaque; 138 uint32_t val; 139 140 switch(addr) { 141 case SMBHSTSTS: 142 val = s->smb_stat; 143 break; 144 case SMBHSTCNT: 145 s->smb_index = 0; 146 val = s->smb_ctl & 0x1f; 147 break; 148 case SMBHSTCMD: 149 val = s->smb_cmd; 150 break; 151 case SMBHSTADD: 152 val = s->smb_addr; 153 break; 154 case SMBHSTDAT0: 155 val = s->smb_data0; 156 break; 157 case SMBHSTDAT1: 158 val = s->smb_data1; 159 break; 160 case SMBBLKDAT: 161 val = s->smb_data[s->smb_index++]; 162 if (s->smb_index > 31) 163 s->smb_index = 0; 164 break; 165 default: 166 val = 0; 167 break; 168 } 169 SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val); 170 return val; 171 } 172 173 static const MemoryRegionOps pm_smbus_ops = { 174 .read = smb_ioport_readb, 175 .write = smb_ioport_writeb, 176 .valid.min_access_size = 1, 177 .valid.max_access_size = 1, 178 .endianness = DEVICE_LITTLE_ENDIAN, 179 }; 180 181 void pm_smbus_init(DeviceState *parent, PMSMBus *smb) 182 { 183 smb->smbus = i2c_init_bus(parent, "i2c"); 184 memory_region_init_io(&smb->io, &pm_smbus_ops, smb, "pm-smbus", 64); 185 } 186