xref: /openbmc/qemu/hw/adc/aspeed_adc.c (revision 8a4968623a3a9d9cc0d1147ac25fcbab8b044e0e)
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