1 /* 2 * Broadcom Serial Controller (BSC) 3 * 4 * Copyright (c) 2024 Rayhan Faizel <rayhan.faizel@gmail.com> 5 * 6 * SPDX-License-Identifier: MIT 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in 16 * all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 * THE SOFTWARE. 25 */ 26 27 #include "qemu/osdep.h" 28 #include "qemu/log.h" 29 #include "hw/i2c/bcm2835_i2c.h" 30 #include "hw/irq.h" 31 #include "migration/vmstate.h" 32 33 static void bcm2835_i2c_update_interrupt(BCM2835I2CState *s) 34 { 35 int do_interrupt = 0; 36 /* Interrupt on RXR (Needs reading) */ 37 if (s->c & BCM2835_I2C_C_INTR && s->s & BCM2835_I2C_S_RXR) { 38 do_interrupt = 1; 39 } 40 41 /* Interrupt on TXW (Needs writing) */ 42 if (s->c & BCM2835_I2C_C_INTT && s->s & BCM2835_I2C_S_TXW) { 43 do_interrupt = 1; 44 } 45 46 /* Interrupt on DONE (Transfer complete) */ 47 if (s->c & BCM2835_I2C_C_INTD && s->s & BCM2835_I2C_S_DONE) { 48 do_interrupt = 1; 49 } 50 qemu_set_irq(s->irq, do_interrupt); 51 } 52 53 static void bcm2835_i2c_begin_transfer(BCM2835I2CState *s) 54 { 55 int direction = s->c & BCM2835_I2C_C_READ; 56 if (i2c_start_transfer(s->bus, s->a, direction)) { 57 s->s |= BCM2835_I2C_S_ERR; 58 } 59 s->s |= BCM2835_I2C_S_TA; 60 61 if (direction) { 62 s->s |= BCM2835_I2C_S_RXR | BCM2835_I2C_S_RXD; 63 } else { 64 s->s |= BCM2835_I2C_S_TXW; 65 } 66 } 67 68 static void bcm2835_i2c_finish_transfer(BCM2835I2CState *s) 69 { 70 /* 71 * STOP is sent when DLEN counts down to zero. 72 * 73 * https://github.com/torvalds/linux/blob/v6.7/drivers/i2c/busses/i2c-bcm2835.c#L223-L261 74 * It is possible to initiate repeated starts on real hardware. 75 * However, this requires sending another ST request before the bytes in 76 * TX FIFO are shifted out. 77 * 78 * This is not emulated currently. 79 */ 80 i2c_end_transfer(s->bus); 81 s->s |= BCM2835_I2C_S_DONE; 82 83 /* Ensure RXD is cleared, otherwise the driver registers an error */ 84 s->s &= ~(BCM2835_I2C_S_TA | BCM2835_I2C_S_RXR | 85 BCM2835_I2C_S_TXW | BCM2835_I2C_S_RXD); 86 } 87 88 static uint64_t bcm2835_i2c_read(void *opaque, hwaddr addr, unsigned size) 89 { 90 BCM2835I2CState *s = opaque; 91 uint32_t readval = 0; 92 93 switch (addr) { 94 case BCM2835_I2C_C: 95 readval = s->c; 96 break; 97 case BCM2835_I2C_S: 98 readval = s->s; 99 break; 100 case BCM2835_I2C_DLEN: 101 readval = s->dlen; 102 break; 103 case BCM2835_I2C_A: 104 readval = s->a; 105 break; 106 case BCM2835_I2C_FIFO: 107 /* We receive I2C messages directly instead of using FIFOs */ 108 if (s->s & BCM2835_I2C_S_TA) { 109 readval = i2c_recv(s->bus); 110 s->dlen -= 1; 111 112 if (s->dlen == 0) { 113 bcm2835_i2c_finish_transfer(s); 114 } 115 } 116 bcm2835_i2c_update_interrupt(s); 117 break; 118 case BCM2835_I2C_DIV: 119 readval = s->div; 120 break; 121 case BCM2835_I2C_DEL: 122 readval = s->del; 123 break; 124 case BCM2835_I2C_CLKT: 125 readval = s->clkt; 126 break; 127 default: 128 qemu_log_mask(LOG_GUEST_ERROR, 129 "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); 130 } 131 132 return readval; 133 } 134 135 static void bcm2835_i2c_write(void *opaque, hwaddr addr, 136 uint64_t value, unsigned int size) 137 { 138 BCM2835I2CState *s = opaque; 139 uint32_t writeval = value; 140 141 switch (addr) { 142 case BCM2835_I2C_C: 143 /* ST is a one-shot operation; it must read back as 0 */ 144 s->c = writeval & ~BCM2835_I2C_C_ST; 145 146 /* Start transfer */ 147 if (writeval & (BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN)) { 148 bcm2835_i2c_begin_transfer(s); 149 /* 150 * Handle special case where transfer starts with zero data length. 151 * Required for zero length i2c quick messages to work. 152 */ 153 if (s->dlen == 0) { 154 bcm2835_i2c_finish_transfer(s); 155 } 156 } 157 158 bcm2835_i2c_update_interrupt(s); 159 break; 160 case BCM2835_I2C_S: 161 if (writeval & BCM2835_I2C_S_DONE && s->s & BCM2835_I2C_S_DONE) { 162 /* When DONE is cleared, DLEN should read last written value. */ 163 s->dlen = s->last_dlen; 164 } 165 166 /* Clear DONE, CLKT and ERR by writing 1 */ 167 s->s &= ~(writeval & (BCM2835_I2C_S_DONE | 168 BCM2835_I2C_S_ERR | BCM2835_I2C_S_CLKT)); 169 break; 170 case BCM2835_I2C_DLEN: 171 s->dlen = writeval; 172 s->last_dlen = writeval; 173 break; 174 case BCM2835_I2C_A: 175 s->a = writeval; 176 break; 177 case BCM2835_I2C_FIFO: 178 /* We send I2C messages directly instead of using FIFOs */ 179 if (s->s & BCM2835_I2C_S_TA) { 180 if (s->s & BCM2835_I2C_S_TXD) { 181 if (!i2c_send(s->bus, writeval & 0xff)) { 182 s->dlen -= 1; 183 } else { 184 s->s |= BCM2835_I2C_S_ERR; 185 } 186 } 187 188 if (s->dlen == 0) { 189 bcm2835_i2c_finish_transfer(s); 190 } 191 } 192 bcm2835_i2c_update_interrupt(s); 193 break; 194 case BCM2835_I2C_DIV: 195 s->div = writeval; 196 break; 197 case BCM2835_I2C_DEL: 198 s->del = writeval; 199 break; 200 case BCM2835_I2C_CLKT: 201 s->clkt = writeval; 202 break; 203 default: 204 qemu_log_mask(LOG_GUEST_ERROR, 205 "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); 206 } 207 } 208 209 static const MemoryRegionOps bcm2835_i2c_ops = { 210 .read = bcm2835_i2c_read, 211 .write = bcm2835_i2c_write, 212 .endianness = DEVICE_NATIVE_ENDIAN, 213 .valid = { 214 .min_access_size = 4, 215 .max_access_size = 4, 216 }, 217 }; 218 219 static void bcm2835_i2c_realize(DeviceState *dev, Error **errp) 220 { 221 BCM2835I2CState *s = BCM2835_I2C(dev); 222 s->bus = i2c_init_bus(dev, NULL); 223 224 memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_i2c_ops, s, 225 TYPE_BCM2835_I2C, 0x24); 226 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); 227 sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); 228 } 229 230 static void bcm2835_i2c_reset(DeviceState *dev) 231 { 232 BCM2835I2CState *s = BCM2835_I2C(dev); 233 234 /* Reset values according to BCM2835 Peripheral Documentation */ 235 s->c = 0x0; 236 s->s = BCM2835_I2C_S_TXD | BCM2835_I2C_S_TXE; 237 s->dlen = 0x0; 238 s->a = 0x0; 239 s->div = 0x5dc; 240 s->del = 0x00300030; 241 s->clkt = 0x40; 242 } 243 244 static const VMStateDescription vmstate_bcm2835_i2c = { 245 .name = TYPE_BCM2835_I2C, 246 .version_id = 1, 247 .minimum_version_id = 1, 248 .fields = (const VMStateField[]) { 249 VMSTATE_UINT32(c, BCM2835I2CState), 250 VMSTATE_UINT32(s, BCM2835I2CState), 251 VMSTATE_UINT32(dlen, BCM2835I2CState), 252 VMSTATE_UINT32(a, BCM2835I2CState), 253 VMSTATE_UINT32(div, BCM2835I2CState), 254 VMSTATE_UINT32(del, BCM2835I2CState), 255 VMSTATE_UINT32(clkt, BCM2835I2CState), 256 VMSTATE_UINT32(last_dlen, BCM2835I2CState), 257 VMSTATE_END_OF_LIST() 258 } 259 }; 260 261 static void bcm2835_i2c_class_init(ObjectClass *klass, void *data) 262 { 263 DeviceClass *dc = DEVICE_CLASS(klass); 264 265 device_class_set_legacy_reset(dc, bcm2835_i2c_reset); 266 dc->realize = bcm2835_i2c_realize; 267 dc->vmsd = &vmstate_bcm2835_i2c; 268 } 269 270 static const TypeInfo bcm2835_i2c_info = { 271 .name = TYPE_BCM2835_I2C, 272 .parent = TYPE_SYS_BUS_DEVICE, 273 .instance_size = sizeof(BCM2835I2CState), 274 .class_init = bcm2835_i2c_class_init, 275 }; 276 277 static void bcm2835_i2c_register_types(void) 278 { 279 type_register_static(&bcm2835_i2c_info); 280 } 281 282 type_init(bcm2835_i2c_register_types) 283