1d1f711d4SAlistair Francis /* 2d1f711d4SAlistair Francis * STM32F2XX ADC 3d1f711d4SAlistair Francis * 4d1f711d4SAlistair Francis * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> 5d1f711d4SAlistair Francis * 6d1f711d4SAlistair Francis * Permission is hereby granted, free of charge, to any person obtaining a copy 7d1f711d4SAlistair Francis * of this software and associated documentation files (the "Software"), to deal 8d1f711d4SAlistair Francis * in the Software without restriction, including without limitation the rights 9d1f711d4SAlistair Francis * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10d1f711d4SAlistair Francis * copies of the Software, and to permit persons to whom the Software is 11d1f711d4SAlistair Francis * furnished to do so, subject to the following conditions: 12d1f711d4SAlistair Francis * 13d1f711d4SAlistair Francis * The above copyright notice and this permission notice shall be included in 14d1f711d4SAlistair Francis * all copies or substantial portions of the Software. 15d1f711d4SAlistair Francis * 16d1f711d4SAlistair Francis * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17d1f711d4SAlistair Francis * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18d1f711d4SAlistair Francis * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19d1f711d4SAlistair Francis * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20d1f711d4SAlistair Francis * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21d1f711d4SAlistair Francis * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22d1f711d4SAlistair Francis * THE SOFTWARE. 23d1f711d4SAlistair Francis */ 24d1f711d4SAlistair Francis 25d1f711d4SAlistair Francis #include "qemu/osdep.h" 26d1f711d4SAlistair Francis #include "hw/sysbus.h" 27d6454270SMarkus Armbruster #include "migration/vmstate.h" 28d1f711d4SAlistair Francis #include "qemu/log.h" 290b8fa32fSMarkus Armbruster #include "qemu/module.h" 30d1f711d4SAlistair Francis #include "hw/adc/stm32f2xx_adc.h" 31d1f711d4SAlistair Francis 32d1f711d4SAlistair Francis #ifndef STM_ADC_ERR_DEBUG 33d1f711d4SAlistair Francis #define STM_ADC_ERR_DEBUG 0 34d1f711d4SAlistair Francis #endif 35d1f711d4SAlistair Francis 36d1f711d4SAlistair Francis #define DB_PRINT_L(lvl, fmt, args...) do { \ 37d1f711d4SAlistair Francis if (STM_ADC_ERR_DEBUG >= lvl) { \ 38d1f711d4SAlistair Francis qemu_log("%s: " fmt, __func__, ## args); \ 39d1f711d4SAlistair Francis } \ 402562755eSEric Blake } while (0) 41d1f711d4SAlistair Francis 42d1f711d4SAlistair Francis #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) 43d1f711d4SAlistair Francis 44d1f711d4SAlistair Francis static void stm32f2xx_adc_reset(DeviceState *dev) 45d1f711d4SAlistair Francis { 46d1f711d4SAlistair Francis STM32F2XXADCState *s = STM32F2XX_ADC(dev); 47d1f711d4SAlistair Francis 48d1f711d4SAlistair Francis s->adc_sr = 0x00000000; 49d1f711d4SAlistair Francis s->adc_cr1 = 0x00000000; 50d1f711d4SAlistair Francis s->adc_cr2 = 0x00000000; 51d1f711d4SAlistair Francis s->adc_smpr1 = 0x00000000; 52d1f711d4SAlistair Francis s->adc_smpr2 = 0x00000000; 53d1f711d4SAlistair Francis s->adc_jofr[0] = 0x00000000; 54d1f711d4SAlistair Francis s->adc_jofr[1] = 0x00000000; 55d1f711d4SAlistair Francis s->adc_jofr[2] = 0x00000000; 56d1f711d4SAlistair Francis s->adc_jofr[3] = 0x00000000; 57d1f711d4SAlistair Francis s->adc_htr = 0x00000FFF; 58d1f711d4SAlistair Francis s->adc_ltr = 0x00000000; 59d1f711d4SAlistair Francis s->adc_sqr1 = 0x00000000; 60d1f711d4SAlistair Francis s->adc_sqr2 = 0x00000000; 61d1f711d4SAlistair Francis s->adc_sqr3 = 0x00000000; 62d1f711d4SAlistair Francis s->adc_jsqr = 0x00000000; 63d1f711d4SAlistair Francis s->adc_jdr[0] = 0x00000000; 64d1f711d4SAlistair Francis s->adc_jdr[1] = 0x00000000; 65d1f711d4SAlistair Francis s->adc_jdr[2] = 0x00000000; 66d1f711d4SAlistair Francis s->adc_jdr[3] = 0x00000000; 67d1f711d4SAlistair Francis s->adc_dr = 0x00000000; 68d1f711d4SAlistair Francis } 69d1f711d4SAlistair Francis 70d1f711d4SAlistair Francis static uint32_t stm32f2xx_adc_generate_value(STM32F2XXADCState *s) 71d1f711d4SAlistair Francis { 72d1f711d4SAlistair Francis /* Attempts to fake some ADC values */ 73d1f711d4SAlistair Francis s->adc_dr = s->adc_dr + 7; 74d1f711d4SAlistair Francis 75d1f711d4SAlistair Francis switch ((s->adc_cr1 & ADC_CR1_RES) >> 24) { 76d1f711d4SAlistair Francis case 0: 77d1f711d4SAlistair Francis /* 12-bit */ 78d1f711d4SAlistair Francis s->adc_dr &= 0xFFF; 79d1f711d4SAlistair Francis break; 80d1f711d4SAlistair Francis case 1: 81d1f711d4SAlistair Francis /* 10-bit */ 82d1f711d4SAlistair Francis s->adc_dr &= 0x3FF; 83d1f711d4SAlistair Francis break; 84d1f711d4SAlistair Francis case 2: 85d1f711d4SAlistair Francis /* 8-bit */ 86d1f711d4SAlistair Francis s->adc_dr &= 0xFF; 87d1f711d4SAlistair Francis break; 88d1f711d4SAlistair Francis default: 89d1f711d4SAlistair Francis /* 6-bit */ 90d1f711d4SAlistair Francis s->adc_dr &= 0x3F; 91d1f711d4SAlistair Francis } 92d1f711d4SAlistair Francis 93d1f711d4SAlistair Francis if (s->adc_cr2 & ADC_CR2_ALIGN) { 94d1f711d4SAlistair Francis return (s->adc_dr << 1) & 0xFFF0; 95d1f711d4SAlistair Francis } else { 96d1f711d4SAlistair Francis return s->adc_dr; 97d1f711d4SAlistair Francis } 98d1f711d4SAlistair Francis } 99d1f711d4SAlistair Francis 100d1f711d4SAlistair Francis static uint64_t stm32f2xx_adc_read(void *opaque, hwaddr addr, 101d1f711d4SAlistair Francis unsigned int size) 102d1f711d4SAlistair Francis { 103d1f711d4SAlistair Francis STM32F2XXADCState *s = opaque; 104d1f711d4SAlistair Francis 105d1f711d4SAlistair Francis DB_PRINT("Address: 0x%" HWADDR_PRIx "\n", addr); 106d1f711d4SAlistair Francis 107d1f711d4SAlistair Francis if (addr >= ADC_COMMON_ADDRESS) { 108d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, 109d1f711d4SAlistair Francis "%s: ADC Common Register Unsupported\n", __func__); 110d1f711d4SAlistair Francis } 111d1f711d4SAlistair Francis 112d1f711d4SAlistair Francis switch (addr) { 113d1f711d4SAlistair Francis case ADC_SR: 114d1f711d4SAlistair Francis return s->adc_sr; 115d1f711d4SAlistair Francis case ADC_CR1: 116d1f711d4SAlistair Francis return s->adc_cr1; 117d1f711d4SAlistair Francis case ADC_CR2: 118d1f711d4SAlistair Francis return s->adc_cr2 & 0xFFFFFFF; 119d1f711d4SAlistair Francis case ADC_SMPR1: 120d1f711d4SAlistair Francis return s->adc_smpr1; 121d1f711d4SAlistair Francis case ADC_SMPR2: 122d1f711d4SAlistair Francis return s->adc_smpr2; 123d1f711d4SAlistair Francis case ADC_JOFR1: 124d1f711d4SAlistair Francis case ADC_JOFR2: 125d1f711d4SAlistair Francis case ADC_JOFR3: 126d1f711d4SAlistair Francis case ADC_JOFR4: 127d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, "%s: " \ 128d1f711d4SAlistair Francis "Injection ADC is not implemented, the registers are " \ 129d1f711d4SAlistair Francis "included for compatibility\n", __func__); 130d1f711d4SAlistair Francis return s->adc_jofr[(addr - ADC_JOFR1) / 4]; 131d1f711d4SAlistair Francis case ADC_HTR: 132d1f711d4SAlistair Francis return s->adc_htr; 133d1f711d4SAlistair Francis case ADC_LTR: 134d1f711d4SAlistair Francis return s->adc_ltr; 135d1f711d4SAlistair Francis case ADC_SQR1: 136d1f711d4SAlistair Francis return s->adc_sqr1; 137d1f711d4SAlistair Francis case ADC_SQR2: 138d1f711d4SAlistair Francis return s->adc_sqr2; 139d1f711d4SAlistair Francis case ADC_SQR3: 140d1f711d4SAlistair Francis return s->adc_sqr3; 141d1f711d4SAlistair Francis case ADC_JSQR: 142d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, "%s: " \ 143d1f711d4SAlistair Francis "Injection ADC is not implemented, the registers are " \ 144d1f711d4SAlistair Francis "included for compatibility\n", __func__); 145d1f711d4SAlistair Francis return s->adc_jsqr; 146d1f711d4SAlistair Francis case ADC_JDR1: 147d1f711d4SAlistair Francis case ADC_JDR2: 148d1f711d4SAlistair Francis case ADC_JDR3: 149d1f711d4SAlistair Francis case ADC_JDR4: 150d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, "%s: " \ 151d1f711d4SAlistair Francis "Injection ADC is not implemented, the registers are " \ 152d1f711d4SAlistair Francis "included for compatibility\n", __func__); 153d1f711d4SAlistair Francis return s->adc_jdr[(addr - ADC_JDR1) / 4] - 154d1f711d4SAlistair Francis s->adc_jofr[(addr - ADC_JDR1) / 4]; 155d1f711d4SAlistair Francis case ADC_DR: 156d1f711d4SAlistair Francis if ((s->adc_cr2 & ADC_CR2_ADON) && (s->adc_cr2 & ADC_CR2_SWSTART)) { 157d1f711d4SAlistair Francis s->adc_cr2 ^= ADC_CR2_SWSTART; 158d1f711d4SAlistair Francis return stm32f2xx_adc_generate_value(s); 159d1f711d4SAlistair Francis } else { 160d1f711d4SAlistair Francis return 0; 161d1f711d4SAlistair Francis } 162d1f711d4SAlistair Francis default: 163d1f711d4SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 164d1f711d4SAlistair Francis "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); 165d1f711d4SAlistair Francis } 166d1f711d4SAlistair Francis 167d1f711d4SAlistair Francis return 0; 168d1f711d4SAlistair Francis } 169d1f711d4SAlistair Francis 170d1f711d4SAlistair Francis static void stm32f2xx_adc_write(void *opaque, hwaddr addr, 171d1f711d4SAlistair Francis uint64_t val64, unsigned int size) 172d1f711d4SAlistair Francis { 173d1f711d4SAlistair Francis STM32F2XXADCState *s = opaque; 174d1f711d4SAlistair Francis uint32_t value = (uint32_t) val64; 175d1f711d4SAlistair Francis 176d1f711d4SAlistair Francis DB_PRINT("Address: 0x%" HWADDR_PRIx ", Value: 0x%x\n", 177d1f711d4SAlistair Francis addr, value); 178d1f711d4SAlistair Francis 179d1f711d4SAlistair Francis if (addr >= 0x100) { 180d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, 181d1f711d4SAlistair Francis "%s: ADC Common Register Unsupported\n", __func__); 182d1f711d4SAlistair Francis } 183d1f711d4SAlistair Francis 184d1f711d4SAlistair Francis switch (addr) { 185d1f711d4SAlistair Francis case ADC_SR: 186d1f711d4SAlistair Francis s->adc_sr &= (value & 0x3F); 187d1f711d4SAlistair Francis break; 188d1f711d4SAlistair Francis case ADC_CR1: 189d1f711d4SAlistair Francis s->adc_cr1 = value; 190d1f711d4SAlistair Francis break; 191d1f711d4SAlistair Francis case ADC_CR2: 192d1f711d4SAlistair Francis s->adc_cr2 = value; 193d1f711d4SAlistair Francis break; 194d1f711d4SAlistair Francis case ADC_SMPR1: 195d1f711d4SAlistair Francis s->adc_smpr1 = value; 196d1f711d4SAlistair Francis break; 197d1f711d4SAlistair Francis case ADC_SMPR2: 198d1f711d4SAlistair Francis s->adc_smpr2 = value; 199d1f711d4SAlistair Francis break; 200d1f711d4SAlistair Francis case ADC_JOFR1: 201d1f711d4SAlistair Francis case ADC_JOFR2: 202d1f711d4SAlistair Francis case ADC_JOFR3: 203d1f711d4SAlistair Francis case ADC_JOFR4: 204d1f711d4SAlistair Francis s->adc_jofr[(addr - ADC_JOFR1) / 4] = (value & 0xFFF); 205d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, "%s: " \ 206d1f711d4SAlistair Francis "Injection ADC is not implemented, the registers are " \ 207d1f711d4SAlistair Francis "included for compatibility\n", __func__); 208d1f711d4SAlistair Francis break; 209d1f711d4SAlistair Francis case ADC_HTR: 210d1f711d4SAlistair Francis s->adc_htr = value; 211d1f711d4SAlistair Francis break; 212d1f711d4SAlistair Francis case ADC_LTR: 213d1f711d4SAlistair Francis s->adc_ltr = value; 214d1f711d4SAlistair Francis break; 215d1f711d4SAlistair Francis case ADC_SQR1: 216d1f711d4SAlistair Francis s->adc_sqr1 = value; 217d1f711d4SAlistair Francis break; 218d1f711d4SAlistair Francis case ADC_SQR2: 219d1f711d4SAlistair Francis s->adc_sqr2 = value; 220d1f711d4SAlistair Francis break; 221d1f711d4SAlistair Francis case ADC_SQR3: 222d1f711d4SAlistair Francis s->adc_sqr3 = value; 223d1f711d4SAlistair Francis break; 224d1f711d4SAlistair Francis case ADC_JSQR: 225d1f711d4SAlistair Francis s->adc_jsqr = value; 226d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, "%s: " \ 227d1f711d4SAlistair Francis "Injection ADC is not implemented, the registers are " \ 228d1f711d4SAlistair Francis "included for compatibility\n", __func__); 229d1f711d4SAlistair Francis break; 230d1f711d4SAlistair Francis case ADC_JDR1: 231d1f711d4SAlistair Francis case ADC_JDR2: 232d1f711d4SAlistair Francis case ADC_JDR3: 233d1f711d4SAlistair Francis case ADC_JDR4: 234d1f711d4SAlistair Francis s->adc_jdr[(addr - ADC_JDR1) / 4] = value; 235d1f711d4SAlistair Francis qemu_log_mask(LOG_UNIMP, "%s: " \ 236d1f711d4SAlistair Francis "Injection ADC is not implemented, the registers are " \ 237d1f711d4SAlistair Francis "included for compatibility\n", __func__); 238d1f711d4SAlistair Francis break; 239d1f711d4SAlistair Francis default: 240d1f711d4SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 241d1f711d4SAlistair Francis "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); 242d1f711d4SAlistair Francis } 243d1f711d4SAlistair Francis } 244d1f711d4SAlistair Francis 245d1f711d4SAlistair Francis static const MemoryRegionOps stm32f2xx_adc_ops = { 246d1f711d4SAlistair Francis .read = stm32f2xx_adc_read, 247d1f711d4SAlistair Francis .write = stm32f2xx_adc_write, 248d1f711d4SAlistair Francis .endianness = DEVICE_NATIVE_ENDIAN, 249d04bf49cSPhilippe Mathieu-Daudé .impl.min_access_size = 4, 250d04bf49cSPhilippe Mathieu-Daudé .impl.max_access_size = 4, 251d1f711d4SAlistair Francis }; 252d1f711d4SAlistair Francis 253d1f711d4SAlistair Francis static const VMStateDescription vmstate_stm32f2xx_adc = { 254d1f711d4SAlistair Francis .name = TYPE_STM32F2XX_ADC, 255d1f711d4SAlistair Francis .version_id = 1, 256d1f711d4SAlistair Francis .minimum_version_id = 1, 257*99367627SRichard Henderson .fields = (const VMStateField[]) { 258d1f711d4SAlistair Francis VMSTATE_UINT32(adc_sr, STM32F2XXADCState), 259d1f711d4SAlistair Francis VMSTATE_UINT32(adc_cr1, STM32F2XXADCState), 260d1f711d4SAlistair Francis VMSTATE_UINT32(adc_cr2, STM32F2XXADCState), 261d1f711d4SAlistair Francis VMSTATE_UINT32(adc_smpr1, STM32F2XXADCState), 262d1f711d4SAlistair Francis VMSTATE_UINT32(adc_smpr2, STM32F2XXADCState), 263d1f711d4SAlistair Francis VMSTATE_UINT32_ARRAY(adc_jofr, STM32F2XXADCState, 4), 264d1f711d4SAlistair Francis VMSTATE_UINT32(adc_htr, STM32F2XXADCState), 265d1f711d4SAlistair Francis VMSTATE_UINT32(adc_ltr, STM32F2XXADCState), 266d1f711d4SAlistair Francis VMSTATE_UINT32(adc_sqr1, STM32F2XXADCState), 267d1f711d4SAlistair Francis VMSTATE_UINT32(adc_sqr2, STM32F2XXADCState), 268d1f711d4SAlistair Francis VMSTATE_UINT32(adc_sqr3, STM32F2XXADCState), 269d1f711d4SAlistair Francis VMSTATE_UINT32(adc_jsqr, STM32F2XXADCState), 270d1f711d4SAlistair Francis VMSTATE_UINT32_ARRAY(adc_jdr, STM32F2XXADCState, 4), 271d1f711d4SAlistair Francis VMSTATE_UINT32(adc_dr, STM32F2XXADCState), 272d1f711d4SAlistair Francis VMSTATE_END_OF_LIST() 273d1f711d4SAlistair Francis } 274d1f711d4SAlistair Francis }; 275d1f711d4SAlistair Francis 276d1f711d4SAlistair Francis static void stm32f2xx_adc_init(Object *obj) 277d1f711d4SAlistair Francis { 278d1f711d4SAlistair Francis STM32F2XXADCState *s = STM32F2XX_ADC(obj); 279d1f711d4SAlistair Francis 280d1f711d4SAlistair Francis sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); 281d1f711d4SAlistair Francis 282d1f711d4SAlistair Francis memory_region_init_io(&s->mmio, obj, &stm32f2xx_adc_ops, s, 283d04bf49cSPhilippe Mathieu-Daudé TYPE_STM32F2XX_ADC, 0x100); 284d1f711d4SAlistair Francis sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 285d1f711d4SAlistair Francis } 286d1f711d4SAlistair Francis 287d1f711d4SAlistair Francis static void stm32f2xx_adc_class_init(ObjectClass *klass, void *data) 288d1f711d4SAlistair Francis { 289d1f711d4SAlistair Francis DeviceClass *dc = DEVICE_CLASS(klass); 290d1f711d4SAlistair Francis 291d1f711d4SAlistair Francis dc->reset = stm32f2xx_adc_reset; 292d1f711d4SAlistair Francis dc->vmsd = &vmstate_stm32f2xx_adc; 293d1f711d4SAlistair Francis } 294d1f711d4SAlistair Francis 295d1f711d4SAlistair Francis static const TypeInfo stm32f2xx_adc_info = { 296d1f711d4SAlistair Francis .name = TYPE_STM32F2XX_ADC, 297d1f711d4SAlistair Francis .parent = TYPE_SYS_BUS_DEVICE, 298d1f711d4SAlistair Francis .instance_size = sizeof(STM32F2XXADCState), 299d1f711d4SAlistair Francis .instance_init = stm32f2xx_adc_init, 300d1f711d4SAlistair Francis .class_init = stm32f2xx_adc_class_init, 301d1f711d4SAlistair Francis }; 302d1f711d4SAlistair Francis 303d1f711d4SAlistair Francis static void stm32f2xx_adc_register_types(void) 304d1f711d4SAlistair Francis { 305d1f711d4SAlistair Francis type_register_static(&stm32f2xx_adc_info); 306d1f711d4SAlistair Francis } 307d1f711d4SAlistair Francis 308d1f711d4SAlistair Francis type_init(stm32f2xx_adc_register_types) 309