diff mbox

[v3,3/7] STM32F2xx: Add the ADC device

Message ID f60661c1a266437df52be29720da81bb8391046c.1453187657.git.alistair@alistair23.me (mailing list archive)
State New, archived
Headers show

Commit Message

Alistair Francis Jan. 19, 2016, 7:23 a.m. UTC
Add the STM32F2xx ADC device. This device randomly
generates values on each read.

This also includes creating a hw/adc directory.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
V2:
 - Address Peter C's comments
 - Create a ADC folder and move the file in there
 - Move some of the registers into arrays

 default-configs/arm-softmmu.mak |   1 +
 hw/Makefile.objs                |   1 +
 hw/adc/Makefile.objs            |   1 +
 hw/adc/stm32f2xx_adc.c          | 283 ++++++++++++++++++++++++++++++++++++++++
 include/hw/adc/stm32f2xx_adc.h  |  94 +++++++++++++
 5 files changed, 380 insertions(+)
 create mode 100644 hw/adc/Makefile.objs
 create mode 100644 hw/adc/stm32f2xx_adc.c
 create mode 100644 include/hw/adc/stm32f2xx_adc.h

Comments

Peter Maydell Feb. 2, 2016, 3:17 p.m. UTC | #1
On 19 January 2016 at 07:23, Alistair Francis <alistair23@gmail.com> wrote:
> Add the STM32F2xx ADC device. This device randomly
> generates values on each read.
>
> This also includes creating a hw/adc directory.
>
> Signed-off-by: Alistair Francis <alistair@alistair23.me>

> +static uint32_t stm32f2xx_adc_generate_value(STM32F2XXADCState *s)
> +{
> +    /* Attempts to fake some ADC values */
> +#ifdef RAND_AVALIABLE
> +    s->adc_dr = s->adc_dr + rand();
> +#else
> +    s->adc_dr = s->adc_dr + 7;
> +#endif

We shouldn't be using rand() in devices I think. (Among other things
it means we won't be deterministic, which will break record-replay.)

In any case you've typoed your #ifdef constant name, which means
that code is never used :-)

> +static uint64_t stm32f2xx_adc_read(void *opaque, hwaddr addr,
> +                                     unsigned int size)
> +{
> +    STM32F2XXADCState *s = opaque;
> +
> +    DB_PRINT("Address: 0x%"HWADDR_PRIx"\n", addr);

Spaces around the HWADDR_PRIx would be nice.

> +
> +    if (addr >= ADC_COMMON_ADDRESS) {
> +        qemu_log_mask(LOG_UNIMP,
> +                      "%s: ADC Common Register Unsupported\n", __func__);
> +    }
> +
> +    switch (addr) {
> +    case ADC_SR:
> +        return s->adc_sr;
> +    case ADC_CR1:
> +        return s->adc_cr1;
> +    case ADC_CR2:
> +        return s->adc_cr2 & 0xFFFFFFF;
> +    case ADC_SMPR1:
> +        return s->adc_smpr1;
> +    case ADC_SMPR2:
> +        return s->adc_smpr2;
> +    case ADC_JOFR1:
> +    case ADC_JOFR2:
> +    case ADC_JOFR3:
> +    case ADC_JOFR4:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "included for compatability\n", __func__);

"compatibility"

> +        return s->adc_jofr[(addr - ADC_JOFR1) / 4];
> +    case ADC_HTR:
> +        return s->adc_htr;
> +    case ADC_LTR:
> +        return s->adc_ltr;
> +    case ADC_SQR1:
> +        return s->adc_sqr1;
> +    case ADC_SQR2:
> +        return s->adc_sqr2;
> +    case ADC_SQR3:
> +        return s->adc_sqr3;
> +    case ADC_JSQR:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "included for compatability\n", __func__);
> +        return s->adc_jsqr;
> +    case ADC_JDR1:
> +    case ADC_JDR2:
> +    case ADC_JDR3:
> +    case ADC_JDR4:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "included for compatability\n", __func__);
> +        return s->adc_jdr[(addr - ADC_JDR1) / 4] -
> +               s->adc_jofr[(addr - ADC_JDR1) / 4];
> +    case ADC_DR:
> +        if ((s->adc_cr2 & ADC_CR2_ADON) && (s->adc_cr2 & ADC_CR2_SWSTART)) {
> +            s->adc_cr2 ^= ADC_CR2_SWSTART;
> +            return stm32f2xx_adc_generate_value(s);
> +        } else {
> +            return 0x00000000;

Just "0" seems more readable to me.

> +#ifdef RAND_MAX
> +/* The rand() function is avaliable */
> +#define RAND_AVAILABLE
> +#undef RAND_MAX
> +#define RAND_MAX 0xFF
> +#endif /* RAND_MAX */

What platforms don't have rand()?
If we need an "exists everywhere" random number function
then there is one in glib.

(but as noted earlier I don't think we should be using rand() here)

> +
> +typedef struct {
> +    /* <private> */
> +    SysBusDevice parent_obj;
> +
> +    /* <public> */
> +    MemoryRegion mmio;
> +
> +    uint32_t adc_sr;
> +    uint32_t adc_cr1;
> +    uint32_t adc_cr2;
> +    uint32_t adc_smpr1;
> +    uint32_t adc_smpr2;
> +    uint32_t adc_jofr[4];
> +    uint32_t adc_htr;
> +    uint32_t adc_ltr;
> +    uint32_t adc_sqr1;
> +    uint32_t adc_sqr2;
> +    uint32_t adc_sqr3;
> +    uint32_t adc_jsqr;
> +    uint32_t adc_jdr[4];
> +    uint32_t adc_dr;
> +
> +    qemu_irq irq;
> +} STM32F2XXADCState;
> +
> +#endif /* HW_STM32F2XX_ADC_H */

You need to implement the VMState structure for migration.

thanks
-- PMM
Alistair Francis Feb. 21, 2016, 11:12 p.m. UTC | #2
On Tue, Feb 2, 2016 at 7:17 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 19 January 2016 at 07:23, Alistair Francis <alistair23@gmail.com> wrote:
>> Add the STM32F2xx ADC device. This device randomly
>> generates values on each read.
>>
>> This also includes creating a hw/adc directory.
>>
>> Signed-off-by: Alistair Francis <alistair@alistair23.me>
>
>> +static uint32_t stm32f2xx_adc_generate_value(STM32F2XXADCState *s)
>> +{
>> +    /* Attempts to fake some ADC values */
>> +#ifdef RAND_AVALIABLE
>> +    s->adc_dr = s->adc_dr + rand();
>> +#else
>> +    s->adc_dr = s->adc_dr + 7;
>> +#endif
>
> We shouldn't be using rand() in devices I think. (Among other things
> it means we won't be deterministic, which will break record-replay.)
>
> In any case you've typoed your #ifdef constant name, which means
> that code is never used :-)

Woops, I didn't realise that. I'll take the rand() function out then.

I have made all of the other changes as well.

Thanks,

Alistair


>
>> +static uint64_t stm32f2xx_adc_read(void *opaque, hwaddr addr,
>> +                                     unsigned int size)
>> +{
>> +    STM32F2XXADCState *s = opaque;
>> +
>> +    DB_PRINT("Address: 0x%"HWADDR_PRIx"\n", addr);
>
> Spaces around the HWADDR_PRIx would be nice.
>
>> +
>> +    if (addr >= ADC_COMMON_ADDRESS) {
>> +        qemu_log_mask(LOG_UNIMP,
>> +                      "%s: ADC Common Register Unsupported\n", __func__);
>> +    }
>> +
>> +    switch (addr) {
>> +    case ADC_SR:
>> +        return s->adc_sr;
>> +    case ADC_CR1:
>> +        return s->adc_cr1;
>> +    case ADC_CR2:
>> +        return s->adc_cr2 & 0xFFFFFFF;
>> +    case ADC_SMPR1:
>> +        return s->adc_smpr1;
>> +    case ADC_SMPR2:
>> +        return s->adc_smpr2;
>> +    case ADC_JOFR1:
>> +    case ADC_JOFR2:
>> +    case ADC_JOFR3:
>> +    case ADC_JOFR4:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "included for compatability\n", __func__);
>
> "compatibility"
>
>> +        return s->adc_jofr[(addr - ADC_JOFR1) / 4];
>> +    case ADC_HTR:
>> +        return s->adc_htr;
>> +    case ADC_LTR:
>> +        return s->adc_ltr;
>> +    case ADC_SQR1:
>> +        return s->adc_sqr1;
>> +    case ADC_SQR2:
>> +        return s->adc_sqr2;
>> +    case ADC_SQR3:
>> +        return s->adc_sqr3;
>> +    case ADC_JSQR:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "included for compatability\n", __func__);
>> +        return s->adc_jsqr;
>> +    case ADC_JDR1:
>> +    case ADC_JDR2:
>> +    case ADC_JDR3:
>> +    case ADC_JDR4:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "included for compatability\n", __func__);
>> +        return s->adc_jdr[(addr - ADC_JDR1) / 4] -
>> +               s->adc_jofr[(addr - ADC_JDR1) / 4];
>> +    case ADC_DR:
>> +        if ((s->adc_cr2 & ADC_CR2_ADON) && (s->adc_cr2 & ADC_CR2_SWSTART)) {
>> +            s->adc_cr2 ^= ADC_CR2_SWSTART;
>> +            return stm32f2xx_adc_generate_value(s);
>> +        } else {
>> +            return 0x00000000;
>
> Just "0" seems more readable to me.
>
>> +#ifdef RAND_MAX
>> +/* The rand() function is avaliable */
>> +#define RAND_AVAILABLE
>> +#undef RAND_MAX
>> +#define RAND_MAX 0xFF
>> +#endif /* RAND_MAX */
>
> What platforms don't have rand()?
> If we need an "exists everywhere" random number function
> then there is one in glib.
>
> (but as noted earlier I don't think we should be using rand() here)
>
>> +
>> +typedef struct {
>> +    /* <private> */
>> +    SysBusDevice parent_obj;
>> +
>> +    /* <public> */
>> +    MemoryRegion mmio;
>> +
>> +    uint32_t adc_sr;
>> +    uint32_t adc_cr1;
>> +    uint32_t adc_cr2;
>> +    uint32_t adc_smpr1;
>> +    uint32_t adc_smpr2;
>> +    uint32_t adc_jofr[4];
>> +    uint32_t adc_htr;
>> +    uint32_t adc_ltr;
>> +    uint32_t adc_sqr1;
>> +    uint32_t adc_sqr2;
>> +    uint32_t adc_sqr3;
>> +    uint32_t adc_jsqr;
>> +    uint32_t adc_jdr[4];
>> +    uint32_t adc_dr;
>> +
>> +    qemu_irq irq;
>> +} STM32F2XXADCState;
>> +
>> +#endif /* HW_STM32F2XX_ADC_H */
>
> You need to implement the VMState structure for migration.
>
> thanks
> -- PMM
diff mbox

Patch

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index d9b90a5..520b209 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -85,6 +85,7 @@  CONFIG_ZYNQ=y
 CONFIG_STM32F2XX_TIMER=y
 CONFIG_STM32F2XX_USART=y
 CONFIG_STM32F2XX_SYSCFG=y
+CONFIG_STM32F2XX_ADC=y
 CONFIG_STM32F205_SOC=y
 
 CONFIG_VERSATILE_PCI=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 4a07ed4..0ffd281 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -1,5 +1,6 @@ 
 devices-dirs-$(call land, $(CONFIG_VIRTIO),$(call land,$(CONFIG_VIRTFS),$(CONFIG_PCI))) += 9pfs/
 devices-dirs-$(CONFIG_ACPI) += acpi/
+devices-dirs-$(CONFIG_SOFTMMU) += adc/
 devices-dirs-$(CONFIG_SOFTMMU) += audio/
 devices-dirs-$(CONFIG_SOFTMMU) += block/
 devices-dirs-$(CONFIG_SOFTMMU) += bt/
diff --git a/hw/adc/Makefile.objs b/hw/adc/Makefile.objs
new file mode 100644
index 0000000..3f6dfde
--- /dev/null
+++ b/hw/adc/Makefile.objs
@@ -0,0 +1 @@ 
+obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o
diff --git a/hw/adc/stm32f2xx_adc.c b/hw/adc/stm32f2xx_adc.c
new file mode 100644
index 0000000..f7249b7
--- /dev/null
+++ b/hw/adc/stm32f2xx_adc.c
@@ -0,0 +1,283 @@ 
+/*
+ * STM32F2XX ADC
+ *
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+#include "hw/adc/stm32f2xx_adc.h"
+
+#ifndef STM_ADC_ERR_DEBUG
+#define STM_ADC_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do { \
+    if (STM_ADC_ERR_DEBUG >= lvl) { \
+        qemu_log("%s: " fmt, __func__, ## args); \
+    } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+static void stm32f2xx_adc_reset(DeviceState *dev)
+{
+    STM32F2XXADCState *s = STM32F2XX_ADC(dev);
+
+    s->adc_sr = 0x00000000;
+    s->adc_cr1 = 0x00000000;
+    s->adc_cr2 = 0x00000000;
+    s->adc_smpr1 = 0x00000000;
+    s->adc_smpr2 = 0x00000000;
+    s->adc_jofr[0] = 0x00000000;
+    s->adc_jofr[1] = 0x00000000;
+    s->adc_jofr[2] = 0x00000000;
+    s->adc_jofr[3] = 0x00000000;
+    s->adc_htr = 0x00000FFF;
+    s->adc_ltr = 0x00000000;
+    s->adc_sqr1 = 0x00000000;
+    s->adc_sqr2 = 0x00000000;
+    s->adc_sqr3 = 0x00000000;
+    s->adc_jsqr = 0x00000000;
+    s->adc_jdr[0] = 0x00000000;
+    s->adc_jdr[1] = 0x00000000;
+    s->adc_jdr[2] = 0x00000000;
+    s->adc_jdr[3] = 0x00000000;
+    s->adc_dr = 0x00000000;
+}
+
+static uint32_t stm32f2xx_adc_generate_value(STM32F2XXADCState *s)
+{
+    /* Attempts to fake some ADC values */
+#ifdef RAND_AVALIABLE
+    s->adc_dr = s->adc_dr + rand();
+#else
+    s->adc_dr = s->adc_dr + 7;
+#endif
+
+    switch ((s->adc_cr1 & ADC_CR1_RES) >> 24) {
+    case 0:
+        /* 12-bit */
+        s->adc_dr &= 0xFFF;
+        break;
+    case 1:
+        /* 10-bit */
+        s->adc_dr &= 0x3FF;
+        break;
+    case 2:
+        /* 8-bit */
+        s->adc_dr &= 0xFF;
+        break;
+    default:
+        /* 6-bit */
+        s->adc_dr &= 0x3F;
+    }
+
+    if (s->adc_cr2 & ADC_CR2_ALIGN) {
+        return (s->adc_dr << 1) & 0xFFF0;
+    } else {
+        return s->adc_dr;
+    }
+}
+
+static uint64_t stm32f2xx_adc_read(void *opaque, hwaddr addr,
+                                     unsigned int size)
+{
+    STM32F2XXADCState *s = opaque;
+
+    DB_PRINT("Address: 0x%"HWADDR_PRIx"\n", addr);
+
+    if (addr >= ADC_COMMON_ADDRESS) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: ADC Common Register Unsupported\n", __func__);
+    }
+
+    switch (addr) {
+    case ADC_SR:
+        return s->adc_sr;
+    case ADC_CR1:
+        return s->adc_cr1;
+    case ADC_CR2:
+        return s->adc_cr2 & 0xFFFFFFF;
+    case ADC_SMPR1:
+        return s->adc_smpr1;
+    case ADC_SMPR2:
+        return s->adc_smpr2;
+    case ADC_JOFR1:
+    case ADC_JOFR2:
+    case ADC_JOFR3:
+    case ADC_JOFR4:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "included for compatability\n", __func__);
+        return s->adc_jofr[(addr - ADC_JOFR1) / 4];
+    case ADC_HTR:
+        return s->adc_htr;
+    case ADC_LTR:
+        return s->adc_ltr;
+    case ADC_SQR1:
+        return s->adc_sqr1;
+    case ADC_SQR2:
+        return s->adc_sqr2;
+    case ADC_SQR3:
+        return s->adc_sqr3;
+    case ADC_JSQR:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "included for compatability\n", __func__);
+        return s->adc_jsqr;
+    case ADC_JDR1:
+    case ADC_JDR2:
+    case ADC_JDR3:
+    case ADC_JDR4:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "included for compatability\n", __func__);
+        return s->adc_jdr[(addr - ADC_JDR1) / 4] -
+               s->adc_jofr[(addr - ADC_JDR1) / 4];
+    case ADC_DR:
+        if ((s->adc_cr2 & ADC_CR2_ADON) && (s->adc_cr2 & ADC_CR2_SWSTART)) {
+            s->adc_cr2 ^= ADC_CR2_SWSTART;
+            return stm32f2xx_adc_generate_value(s);
+        } else {
+            return 0x00000000;
+        }
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+
+    return 0;
+}
+
+static void stm32f2xx_adc_write(void *opaque, hwaddr addr,
+                       uint64_t val64, unsigned int size)
+{
+    STM32F2XXADCState *s = opaque;
+    uint32_t value = (uint32_t) val64;
+
+    DB_PRINT("Address: 0x%"HWADDR_PRIx", Value: 0x%x\n",
+             addr, value);
+
+    if (addr >= 0x100) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: ADC Common Register Unsupported\n", __func__);
+    }
+
+    switch (addr) {
+    case ADC_SR:
+        s->adc_sr &= (value & 0x3F);
+        break;
+    case ADC_CR1:
+        s->adc_cr1 = value;
+        break;
+    case ADC_CR2:
+        s->adc_cr2 = value;
+        break;
+    case ADC_SMPR1:
+        s->adc_smpr1 = value;
+        break;
+    case ADC_SMPR2:
+        s->adc_smpr2 = value;
+        break;
+    case ADC_JOFR1:
+    case ADC_JOFR2:
+    case ADC_JOFR3:
+    case ADC_JOFR4:
+        s->adc_jofr[(addr - ADC_JOFR1) / 4] = (value & 0xFFF);
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "included for compatability\n", __func__);
+        break;
+    case ADC_HTR:
+        s->adc_htr = value;
+        break;
+    case ADC_LTR:
+        s->adc_ltr = value;
+        break;
+    case ADC_SQR1:
+        s->adc_sqr1 = value;
+        break;
+    case ADC_SQR2:
+        s->adc_sqr2 = value;
+        break;
+    case ADC_SQR3:
+        s->adc_sqr3 = value;
+        break;
+    case ADC_JSQR:
+        s->adc_jsqr = value;
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "included for compatability\n", __func__);
+        break;
+    case ADC_JDR1:
+    case ADC_JDR2:
+    case ADC_JDR3:
+    case ADC_JDR4:
+        s->adc_jdr[(addr - ADC_JDR1) / 4] = value;
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "included for compatability\n", __func__);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+}
+
+static const MemoryRegionOps stm32f2xx_adc_ops = {
+    .read = stm32f2xx_adc_read,
+    .write = stm32f2xx_adc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stm32f2xx_adc_init(Object *obj)
+{
+    STM32F2XXADCState *s = STM32F2XX_ADC(obj);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+    memory_region_init_io(&s->mmio, obj, &stm32f2xx_adc_ops, s,
+                          TYPE_STM32F2XX_ADC, 0xFF);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void stm32f2xx_adc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = stm32f2xx_adc_reset;
+}
+
+static const TypeInfo stm32f2xx_adc_info = {
+    .name          = TYPE_STM32F2XX_ADC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(STM32F2XXADCState),
+    .instance_init = stm32f2xx_adc_init,
+    .class_init    = stm32f2xx_adc_class_init,
+};
+
+static void stm32f2xx_adc_register_types(void)
+{
+    type_register_static(&stm32f2xx_adc_info);
+}
+
+type_init(stm32f2xx_adc_register_types)
diff --git a/include/hw/adc/stm32f2xx_adc.h b/include/hw/adc/stm32f2xx_adc.h
new file mode 100644
index 0000000..4095d9a
--- /dev/null
+++ b/include/hw/adc/stm32f2xx_adc.h
@@ -0,0 +1,94 @@ 
+/*
+ * STM32F2XX ADC
+ *
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_STM32F2XX_ADC_H
+#define HW_STM32F2XX_ADC_H
+
+#define ADC_SR    0x00
+#define ADC_CR1   0x04
+#define ADC_CR2   0x08
+#define ADC_SMPR1 0x0C
+#define ADC_SMPR2 0x10
+#define ADC_JOFR1 0x14
+#define ADC_JOFR2 0x18
+#define ADC_JOFR3 0x1C
+#define ADC_JOFR4 0x20
+#define ADC_HTR   0x24
+#define ADC_LTR   0x28
+#define ADC_SQR1  0x2C
+#define ADC_SQR2  0x30
+#define ADC_SQR3  0x34
+#define ADC_JSQR  0x38
+#define ADC_JDR1  0x3C
+#define ADC_JDR2  0x40
+#define ADC_JDR3  0x44
+#define ADC_JDR4  0x48
+#define ADC_DR    0x4C
+
+#define ADC_CR2_ADON    0x01
+#define ADC_CR2_CONT    0x02
+#define ADC_CR2_ALIGN   0x800
+#define ADC_CR2_SWSTART 0x40000000
+
+#define ADC_CR1_RES 0x3000000
+
+#define ADC_COMMON_ADDRESS 0x100
+
+#define TYPE_STM32F2XX_ADC "stm32f2xx-adc"
+#define STM32F2XX_ADC(obj) \
+    OBJECT_CHECK(STM32F2XXADCState, (obj), TYPE_STM32F2XX_ADC)
+
+#ifdef RAND_MAX
+/* The rand() function is avaliable */
+#define RAND_AVAILABLE
+#undef RAND_MAX
+#define RAND_MAX 0xFF
+#endif /* RAND_MAX */
+
+typedef struct {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion mmio;
+
+    uint32_t adc_sr;
+    uint32_t adc_cr1;
+    uint32_t adc_cr2;
+    uint32_t adc_smpr1;
+    uint32_t adc_smpr2;
+    uint32_t adc_jofr[4];
+    uint32_t adc_htr;
+    uint32_t adc_ltr;
+    uint32_t adc_sqr1;
+    uint32_t adc_sqr2;
+    uint32_t adc_sqr3;
+    uint32_t adc_jsqr;
+    uint32_t adc_jdr[4];
+    uint32_t adc_dr;
+
+    qemu_irq irq;
+} STM32F2XXADCState;
+
+#endif /* HW_STM32F2XX_ADC_H */