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