1 /* 2 * Aspeed ADC 3 * 4 * Andrew Jeffery <andrew@aj.id.au> 5 * 6 * Copyright 2017 IBM Corp. 7 * 8 * This code is licensed under the GPL version 2 or later. See 9 * the COPYING file in the top-level directory. 10 */ 11 12 #include "qemu/osdep.h" 13 #include "hw/adc/aspeed_adc.h" 14 #include "qapi/error.h" 15 #include "qemu/log.h" 16 #include "migration/vmstate.h" 17 18 #define ASPEED_ADC_ENGINE_CTRL 0x00 19 #define ASPEED_ADC_ENGINE_CH_EN_MASK 0xffff0000 20 #define ASPEED_ADC_ENGINE_CH_EN(x) ((BIT(x)) << 16) 21 #define ASPEED_ADC_ENGINE_INIT BIT(8) 22 #define ASPEED_ADC_ENGINE_AUTO_COMP BIT(5) 23 #define ASPEED_ADC_ENGINE_COMP BIT(4) 24 #define ASPEED_ADC_ENGINE_MODE_MASK 0x0000000e 25 #define ASPEED_ADC_ENGINE_MODE_OFF (0b000 << 1) 26 #define ASPEED_ADC_ENGINE_MODE_STANDBY (0b001 << 1) 27 #define ASPEED_ADC_ENGINE_MODE_NORMAL (0b111 << 1) 28 #define ASPEED_ADC_ENGINE_EN BIT(0) 29 #define ASPEED_ADC_HYST_EN BIT(31) 30 31 #define ASPEED_ADC_L_MASK ((1 << 10) - 1) 32 #define ASPEED_ADC_L(x) ((x) & ASPEED_ADC_L_MASK) 33 #define ASPEED_ADC_H(x) (((x) >> 16) & ASPEED_ADC_L_MASK) 34 #define ASPEED_ADC_LH_MASK (ASPEED_ADC_L_MASK << 16 | ASPEED_ADC_L_MASK) 35 36 static inline uint32_t update_channels(uint32_t current) 37 { 38 return ((((current >> 16) & ASPEED_ADC_L_MASK) + 7) << 16) | 39 ((current + 5) & ASPEED_ADC_L_MASK); 40 } 41 42 static bool breaks_threshold(AspeedADCState *s, int ch_off) 43 { 44 const uint32_t a = ASPEED_ADC_L(s->channels[ch_off]); 45 const uint32_t a_lower = ASPEED_ADC_L(s->bounds[2 * ch_off]); 46 const uint32_t a_upper = ASPEED_ADC_H(s->bounds[2 * ch_off]); 47 const uint32_t b = ASPEED_ADC_H(s->channels[ch_off]); 48 const uint32_t b_lower = ASPEED_ADC_L(s->bounds[2 * ch_off + 1]); 49 const uint32_t b_upper = ASPEED_ADC_H(s->bounds[2 * ch_off + 1]); 50 51 return ((a < a_lower || a > a_upper)) || 52 ((b < b_lower || b > b_upper)); 53 } 54 55 static uint32_t read_channel_sample(AspeedADCState *s, int ch_off) 56 { 57 uint32_t ret; 58 59 /* Poor man's sampling */ 60 ret = s->channels[ch_off]; 61 s->channels[ch_off] = update_channels(s->channels[ch_off]); 62 63 if (breaks_threshold(s, ch_off)) { 64 qemu_irq_raise(s->irq); 65 } 66 67 return ret; 68 } 69 70 #define TO_INDEX(addr, base) (((addr) - (base)) >> 2) 71 72 static uint64_t aspeed_adc_read(void *opaque, hwaddr addr, 73 unsigned int size) 74 { 75 AspeedADCState *s = ASPEED_ADC(opaque); 76 uint64_t ret; 77 78 switch (addr) { 79 case 0x00: 80 ret = s->engine_ctrl; 81 break; 82 case 0x04: 83 ret = s->irq_ctrl; 84 break; 85 case 0x08: 86 ret = s->vga_detect_ctrl; 87 break; 88 case 0x0c: 89 ret = s->adc_clk_ctrl; 90 break; 91 case 0x10 ... 0x2e: 92 ret = read_channel_sample(s, TO_INDEX(addr, 0x10)); 93 break; 94 case 0x30 ... 0x6e: 95 ret = s->bounds[TO_INDEX(addr, 0x30)]; 96 break; 97 case 0x70 ... 0xae: 98 ret = s->hysteresis[TO_INDEX(addr, 0x70)]; 99 break; 100 case 0xc0: 101 ret = s->irq_src; 102 break; 103 case 0xc4: 104 ret = s->comp_trim; 105 break; 106 default: 107 qemu_log_mask(LOG_UNIMP, "%s: addr: 0x%" HWADDR_PRIx ", size: %u\n", 108 __func__, addr, size); 109 ret = 0; 110 break; 111 } 112 113 return ret; 114 } 115 116 static void aspeed_adc_write(void *opaque, hwaddr addr, uint64_t val, 117 unsigned int size) 118 { 119 AspeedADCState *s = ASPEED_ADC(opaque); 120 121 switch (addr) { 122 case 0x00: 123 { 124 uint32_t init; 125 126 init = !!(val & ASPEED_ADC_ENGINE_EN); 127 init *= ASPEED_ADC_ENGINE_INIT; 128 129 val &= ~ASPEED_ADC_ENGINE_INIT; 130 val |= init; 131 } 132 133 val &= ~ASPEED_ADC_ENGINE_AUTO_COMP; 134 s->engine_ctrl = val; 135 136 break; 137 case 0x04: 138 s->irq_ctrl = val; 139 break; 140 case 0x08: 141 s->vga_detect_ctrl = val; 142 break; 143 case 0x0c: 144 s->adc_clk_ctrl = val; 145 break; 146 case 0x10 ... 0x2e: 147 s->channels[TO_INDEX(addr, 0x10)] = (val & ASPEED_ADC_LH_MASK); 148 break; 149 case 0x30 ... 0x6e: 150 s->bounds[TO_INDEX(addr, 0x30)] = (val & ASPEED_ADC_LH_MASK); 151 break; 152 case 0x70 ... 0xae: 153 s->hysteresis[TO_INDEX(addr, 0x70)] = 154 (val & (ASPEED_ADC_HYST_EN | ASPEED_ADC_LH_MASK)); 155 break; 156 case 0xc0: 157 s->irq_src = (val & 0xffff); 158 break; 159 case 0xc4: 160 s->comp_trim = (val & 0xf); 161 break; 162 default: 163 qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr); 164 break; 165 } 166 } 167 168 static const MemoryRegionOps aspeed_adc_ops = { 169 .read = aspeed_adc_read, 170 .write = aspeed_adc_write, 171 .endianness = DEVICE_LITTLE_ENDIAN, 172 .valid = { 173 .min_access_size = 1, 174 .max_access_size = 4, 175 }, 176 }; 177 178 static void aspeed_adc_reset(DeviceState *dev) 179 { 180 struct AspeedADCState *s = ASPEED_ADC(dev); 181 182 s->engine_ctrl = 0; 183 s->irq_ctrl = 0; 184 s->vga_detect_ctrl = 0x0000000f; 185 s->adc_clk_ctrl = 0x0000000f; 186 memset(s->channels, 0, sizeof(s->channels)); 187 memset(s->bounds, 0, sizeof(s->bounds)); 188 memset(s->hysteresis, 0, sizeof(s->hysteresis)); 189 s->irq_src = 0; 190 s->comp_trim = 0; 191 } 192 193 static void aspeed_adc_realize(DeviceState *dev, Error **errp) 194 { 195 AspeedADCState *s = ASPEED_ADC(dev); 196 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 197 198 sysbus_init_irq(sbd, &s->irq); 199 200 memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_adc_ops, s, 201 TYPE_ASPEED_ADC, 0x1000); 202 203 sysbus_init_mmio(sbd, &s->mmio); 204 } 205 206 static const VMStateDescription vmstate_aspeed_adc = { 207 .name = TYPE_ASPEED_ADC, 208 .version_id = 1, 209 .minimum_version_id = 1, 210 .fields = (VMStateField[]) { 211 VMSTATE_UINT32(engine_ctrl, AspeedADCState), 212 VMSTATE_UINT32(irq_ctrl, AspeedADCState), 213 VMSTATE_UINT32(vga_detect_ctrl, AspeedADCState), 214 VMSTATE_UINT32(adc_clk_ctrl, AspeedADCState), 215 VMSTATE_UINT32_ARRAY(channels, AspeedADCState, 216 ASPEED_ADC_NR_CHANNELS / 2), 217 VMSTATE_UINT32_ARRAY(bounds, AspeedADCState, ASPEED_ADC_NR_CHANNELS), 218 VMSTATE_UINT32_ARRAY(hysteresis, AspeedADCState, 219 ASPEED_ADC_NR_CHANNELS), 220 VMSTATE_UINT32(irq_src, AspeedADCState), 221 VMSTATE_UINT32(comp_trim, AspeedADCState), 222 VMSTATE_END_OF_LIST(), 223 } 224 }; 225 226 static void aspeed_adc_class_init(ObjectClass *klass, void *data) 227 { 228 DeviceClass *dc = DEVICE_CLASS(klass); 229 230 dc->realize = aspeed_adc_realize; 231 dc->reset = aspeed_adc_reset; 232 dc->desc = "Aspeed Analog-to-Digital Converter", 233 dc->vmsd = &vmstate_aspeed_adc; 234 } 235 236 static const TypeInfo aspeed_adc_info = { 237 .name = TYPE_ASPEED_ADC, 238 .parent = TYPE_SYS_BUS_DEVICE, 239 .instance_size = sizeof(AspeedADCState), 240 .class_init = aspeed_adc_class_init, 241 }; 242 243 static void aspeed_adc_register_types(void) 244 { 245 type_register_static(&aspeed_adc_info); 246 } 247 248 type_init(aspeed_adc_register_types); 249