[v1,3/7] STM32F2xx: Add the ADC device
diff mbox

Message ID 4d37417b7c7024ee782779dd48c9fcd949dcc449.1429949062.git.alistair@alistair23.me
State New
Headers show

Commit Message

Alistair Francis April 25, 2015, 8:18 a.m. UTC
Add the STM32F2xx ADC device. This device randomly
generates values on each read.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---

 default-configs/arm-softmmu.mak |   1 +
 hw/misc/Makefile.objs           |   1 +
 hw/misc/stm32f2xx_adc.c         | 332 ++++++++++++++++++++++++++++++++++++++++
 include/hw/misc/stm32f2xx_adc.h |  96 ++++++++++++
 4 files changed, 430 insertions(+)
 create mode 100644 hw/misc/stm32f2xx_adc.c
 create mode 100644 include/hw/misc/stm32f2xx_adc.h

Comments

Peter Crosthwaite April 25, 2015, 6:43 p.m. UTC | #1
On Sat, Apr 25, 2015 at 1:18 AM, Alistair Francis <alistair23@gmail.com> wrote:
> Add the STM32F2xx ADC device. This device randomly
> generates values on each read.
>
> Signed-off-by: Alistair Francis <alistair@alistair23.me>
> ---
>
>  default-configs/arm-softmmu.mak |   1 +
>  hw/misc/Makefile.objs           |   1 +
>  hw/misc/stm32f2xx_adc.c         | 332 ++++++++++++++++++++++++++++++++++++++++
>  include/hw/misc/stm32f2xx_adc.h |  96 ++++++++++++
>  4 files changed, 430 insertions(+)
>  create mode 100644 hw/misc/stm32f2xx_adc.c
>  create mode 100644 include/hw/misc/stm32f2xx_adc.h
>
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index a767e4b..2b16590 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -84,6 +84,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/misc/Makefile.objs b/hw/misc/Makefile.objs
> index 4aa76ff..b65ec3d 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -37,6 +37,7 @@ obj-$(CONFIG_OMAP) += omap_tap.o
>  obj-$(CONFIG_SLAVIO) += slavio_misc.o
>  obj-$(CONFIG_ZYNQ) += zynq_slcr.o
>  obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
> +obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o
>
>  obj-$(CONFIG_PVPANIC) += pvpanic.o
>  obj-$(CONFIG_EDU) += edu.o
> diff --git a/hw/misc/stm32f2xx_adc.c b/hw/misc/stm32f2xx_adc.c
> new file mode 100644
> index 0000000..659f14f
> --- /dev/null
> +++ b/hw/misc/stm32f2xx_adc.c
> @@ -0,0 +1,332 @@
> +/*
> + * 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/misc/stm32f2xx_adc.h"
> +

Misc is starting to pile up in random features whereas ADC seems to be
a nice self contained category. Can we create hw/adc? Are there any
other ADCs in misc?

> +#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_jofr1 = 0x00000000;
> +    s->adc_jofr2 = 0x00000000;
> +    s->adc_jofr3 = 0x00000000;
> +    s->adc_jofr4 = 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_jdr1 = 0x00000000;
> +    s->adc_jdr2 = 0x00000000;
> +    s->adc_jdr3 = 0x00000000;
> +    s->adc_jdr4 = 0x00000000;
> +    s->adc_dr = 0x00000000;

I'm guessing the documentation prefixes all the register symbolic
names with "ADC". In these cases where the doc adds such prefixes I
usually drop them from variables names as the doc is usually
documenting them as part of a bigger register block. In this case the
fact that it is adc is implicit so can the prefix be dropped and save
some text?

> +}
> +
> +static uint32_t stm32f2xx_adc_generate_value(STM32F2XXAdcState *s)
> +{
> +    /* Attempts to fake some ADC values */
> +    #ifdef RAND_AVALIABLE

#ifdef should be non-indented.

Although I have been thinking about this, and perhaps it is better to
make the value (or its randomization factor) runtime controllable. Can
you propertyify something and then you can set the ADC value from the
monitor using a property setter? Even if it cannot be set from the
monitor it might help to at least be start-time configurable using
-global.

> +    s->adc_dr = s->adc_dr + rand();
> +    #else
> +    s->adc_dr = s->adc_dr + 7;
> +    #endif
> +
> +    if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 0) {

extract to get field value.

switch (s->adc_cr1 & ADC_CR1_RES) >> 24

But could be handled with:

bit_width = 12 - 2 * extract32(a->adc_cr1, ADC_CR1_RES_SHIFT,
ADC_CR1_RES_LENGTH);
s->adc_dr &= (1 << bit_width) - 1;

> +        /* 12-bit */
> +        s->adc_dr = s->adc_dr & 0xFFF;

&=

> +    } else if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 1) {
> +        /* 10-bit */
> +        s->adc_dr = s->adc_dr & 0x3FF;
> +    } else if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 2) {
> +        /* 8-bit */
> +        s->adc_dr = s->adc_dr & 0xFF;
> +    } else {
> +        /* 6-bit */
> +        s->adc_dr = 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("%s: Address: 0x%"HWADDR_PRIx"\n", __func__, addr);
> +
> +    if (addr >= 0x100) {

Macro for register offset (even if unimplemented)

> +        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:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);

"included"

> +        return s->adc_jofr1;
> +    case ADC_JOFR2:

Can you use an array for the JOFRs as they are consecutive registers
with the same semantics. Then the 4X repitition can be removed.

> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jofr2;
> +    case ADC_JOFR3:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jofr3;
> +    case ADC_JOFR4:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jofr4;
> +    case ADC_HTR:
> +        return s->adc_htr;
> +    case ADC_LTR:
> +        return s->adc_ltr;
> +    case ADC_SQR1:

Same for SQRs

> +        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 " \
> +                      "includded for compatability\n", __func__);

"Included".

> +        return s->adc_jsqr;
> +    case ADC_JDR1:

Array here too.

> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jdr1 - s->adc_jofr1;
> +    case ADC_JDR2:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jdr2 - s->adc_jofr2;
> +    case ADC_JDR3:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jdr3 - s->adc_jofr3;
> +    case ADC_JDR4:
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        return s->adc_jdr4 - s->adc_jofr4;
> +    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("%s: Address: 0x%"HWADDR_PRIx", Value: 0x%x\n",
> +             __func__, 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:
> +        s->adc_jofr1 = (value & 0xFFF);
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JOFR2:
> +        s->adc_jofr2 = (value & 0xFFF);
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JOFR3:
> +        s->adc_jofr3 = (value & 0xFFF);
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JOFR4:
> +        s->adc_jofr4 = (value & 0xFFF);
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded 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 " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JDR1:
> +        s->adc_jdr1 = value;
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JDR2:
> +        s->adc_jdr2 = value;
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JDR3:
> +        s->adc_jdr3 = value;
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded for compatability\n", __func__);
> +        break;
> +    case ADC_JDR4:
> +        s->adc_jdr4 = value;
> +        qemu_log_mask(LOG_UNIMP, "%s: " \
> +                      "Injection ADC is not implemented, the registers are " \
> +                      "includded 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/misc/stm32f2xx_adc.h b/include/hw/misc/stm32f2xx_adc.h
> new file mode 100644
> index 0000000..e0ec1d7
> --- /dev/null
> +++ b/include/hw/misc/stm32f2xx_adc.h
> @@ -0,0 +1,96 @@
> +/*
> + * 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
> +

I'm acutally starting to see a big problem with having the reg defs in
the common header, as it is not namespace safe. If you have two
periphs defining a reg with the same name in the same soc you get a
collision. The only safe way to do it would be to preface every symbol
with the full type name. This would be super verbose.

On the other side if we don't headerify them they are not available to
test code.

Out of scope so don't need to do anything for now but maybe we need to
start a top lvl thread.

> +#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_AVALIABLE

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_jofr1;
> +    uint32_t adc_jofr2;
> +    uint32_t adc_jofr3;
> +    uint32_t adc_jofr4;
> +    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_jdr1;
> +    uint32_t adc_jdr2;
> +    uint32_t adc_jdr3;
> +    uint32_t adc_jdr4;
> +    uint32_t adc_dr;
> +
> +    qemu_irq irq;
> +} STM32F2XXAdcState;

STM32F2XXADCState

Regards,
Peter

> +
> +#endif /* HW_STM32F2XX_ADC_H */
> --
> 2.1.4
>
>
Alistair Francis April 27, 2015, 12:02 p.m. UTC | #2
On Sun, Apr 26, 2015 at 4:43 AM, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sat, Apr 25, 2015 at 1:18 AM, Alistair Francis <alistair23@gmail.com> wrote:
>> Add the STM32F2xx ADC device. This device randomly
>> generates values on each read.
>>
>> Signed-off-by: Alistair Francis <alistair@alistair23.me>
>> ---
>>
>>  default-configs/arm-softmmu.mak |   1 +
>>  hw/misc/Makefile.objs           |   1 +
>>  hw/misc/stm32f2xx_adc.c         | 332 ++++++++++++++++++++++++++++++++++++++++
>>  include/hw/misc/stm32f2xx_adc.h |  96 ++++++++++++
>>  4 files changed, 430 insertions(+)
>>  create mode 100644 hw/misc/stm32f2xx_adc.c
>>  create mode 100644 include/hw/misc/stm32f2xx_adc.h
>>
>> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
>> index a767e4b..2b16590 100644
>> --- a/default-configs/arm-softmmu.mak
>> +++ b/default-configs/arm-softmmu.mak
>> @@ -84,6 +84,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/misc/Makefile.objs b/hw/misc/Makefile.objs
>> index 4aa76ff..b65ec3d 100644
>> --- a/hw/misc/Makefile.objs
>> +++ b/hw/misc/Makefile.objs
>> @@ -37,6 +37,7 @@ obj-$(CONFIG_OMAP) += omap_tap.o
>>  obj-$(CONFIG_SLAVIO) += slavio_misc.o
>>  obj-$(CONFIG_ZYNQ) += zynq_slcr.o
>>  obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
>> +obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o
>>
>>  obj-$(CONFIG_PVPANIC) += pvpanic.o
>>  obj-$(CONFIG_EDU) += edu.o
>> diff --git a/hw/misc/stm32f2xx_adc.c b/hw/misc/stm32f2xx_adc.c
>> new file mode 100644
>> index 0000000..659f14f
>> --- /dev/null
>> +++ b/hw/misc/stm32f2xx_adc.c
>> @@ -0,0 +1,332 @@
>> +/*
>> + * 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/misc/stm32f2xx_adc.h"
>> +
>
> Misc is starting to pile up in random features whereas ADC seems to be
> a nice self contained category. Can we create hw/adc? Are there any
> other ADCs in misc?

There are a few, but normally they are already included in another file (like
the machine that is using ADC). I do agree with you that the misc folder is
filling up, so I will create and ADC one (unless anyone has any objections?)

>
>> +#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_jofr1 = 0x00000000;
>> +    s->adc_jofr2 = 0x00000000;
>> +    s->adc_jofr3 = 0x00000000;
>> +    s->adc_jofr4 = 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_jdr1 = 0x00000000;
>> +    s->adc_jdr2 = 0x00000000;
>> +    s->adc_jdr3 = 0x00000000;
>> +    s->adc_jdr4 = 0x00000000;
>> +    s->adc_dr = 0x00000000;
>
> I'm guessing the documentation prefixes all the register symbolic
> names with "ADC". In these cases where the doc adds such prefixes I
> usually drop them from variables names as the doc is usually
> documenting them as part of a bigger register block. In this case the
> fact that it is adc is implicit so can the prefix be dropped and save
> some text?

Yes you're right, the documentation does prefix the register names
with adc_*. I don't see any issue with it though. It's not very long
and otherwise some of the register names would only be two letters
long, which I think is too short.

>
>> +}
>> +
>> +static uint32_t stm32f2xx_adc_generate_value(STM32F2XXAdcState *s)
>> +{
>> +    /* Attempts to fake some ADC values */
>> +    #ifdef RAND_AVALIABLE
>
> #ifdef should be non-indented.

Good catch, will fix.

>
> Although I have been thinking about this, and perhaps it is better to
> make the value (or its randomization factor) runtime controllable. Can
> you propertyify something and then you can set the ADC value from the
> monitor using a property setter? Even if it cannot be set from the
> monitor it might help to at least be start-time configurable using
> -global.

Hmmm... I like the idea of setting the property from the monitor, that
would be useful. I don't see any need to set it via the command-line
though, I can't think of any realistic use cases (maybe the
multiplier, but it still doesn't seem nessesary).

In saying that, at the moment I don't have time to look into it. It
would have to be something that I do after this (and a few more) patch
series get committed.

>
>> +    s->adc_dr = s->adc_dr + rand();
>> +    #else
>> +    s->adc_dr = s->adc_dr + 7;
>> +    #endif
>> +
>> +    if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 0) {
>
> extract to get field value.
>
> switch (s->adc_cr1 & ADC_CR1_RES) >> 24
>
> But could be handled with:
>
> bit_width = 12 - 2 * extract32(a->adc_cr1, ADC_CR1_RES_SHIFT,
> ADC_CR1_RES_LENGTH);
> s->adc_dr &= (1 << bit_width) - 1;

I think the switch statement is easier to read so I'll swap it over to that.
The bit_width method makes it a little harder to understand what's going on.

>
>> +        /* 12-bit */
>> +        s->adc_dr = s->adc_dr & 0xFFF;
>
> &=

Will fix.

>
>> +    } else if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 1) {
>> +        /* 10-bit */
>> +        s->adc_dr = s->adc_dr & 0x3FF;
>> +    } else if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 2) {
>> +        /* 8-bit */
>> +        s->adc_dr = s->adc_dr & 0xFF;
>> +    } else {
>> +        /* 6-bit */
>> +        s->adc_dr = 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("%s: Address: 0x%"HWADDR_PRIx"\n", __func__, addr);
>> +
>> +    if (addr >= 0x100) {
>
> Macro for register offset (even if unimplemented)

Will fix

>
>> +        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:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>
> "included"

Good catch, will fix them all.

>
>> +        return s->adc_jofr1;
>> +    case ADC_JOFR2:
>
> Can you use an array for the JOFRs as they are consecutive registers
> with the same semantics. Then the 4X repitition can be removed.

Yeah, I can do that

>
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jofr2;
>> +    case ADC_JOFR3:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jofr3;
>> +    case ADC_JOFR4:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jofr4;
>> +    case ADC_HTR:
>> +        return s->adc_htr;
>> +    case ADC_LTR:
>> +        return s->adc_ltr;
>> +    case ADC_SQR1:
>
> Same for SQRs

They don't have the long unimplemented messages, so I don't think I will bother.

>
>> +        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 " \
>> +                      "includded for compatability\n", __func__);
>
> "Included".
>
>> +        return s->adc_jsqr;
>> +    case ADC_JDR1:
>
> Array here too.

Will arraryify these though.

>
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jdr1 - s->adc_jofr1;
>> +    case ADC_JDR2:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jdr2 - s->adc_jofr2;
>> +    case ADC_JDR3:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jdr3 - s->adc_jofr3;
>> +    case ADC_JDR4:
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        return s->adc_jdr4 - s->adc_jofr4;
>> +    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("%s: Address: 0x%"HWADDR_PRIx", Value: 0x%x\n",
>> +             __func__, 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:
>> +        s->adc_jofr1 = (value & 0xFFF);
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JOFR2:
>> +        s->adc_jofr2 = (value & 0xFFF);
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JOFR3:
>> +        s->adc_jofr3 = (value & 0xFFF);
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JOFR4:
>> +        s->adc_jofr4 = (value & 0xFFF);
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded 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 " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JDR1:
>> +        s->adc_jdr1 = value;
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JDR2:
>> +        s->adc_jdr2 = value;
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JDR3:
>> +        s->adc_jdr3 = value;
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded for compatability\n", __func__);
>> +        break;
>> +    case ADC_JDR4:
>> +        s->adc_jdr4 = value;
>> +        qemu_log_mask(LOG_UNIMP, "%s: " \
>> +                      "Injection ADC is not implemented, the registers are " \
>> +                      "includded 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/misc/stm32f2xx_adc.h b/include/hw/misc/stm32f2xx_adc.h
>> new file mode 100644
>> index 0000000..e0ec1d7
>> --- /dev/null
>> +++ b/include/hw/misc/stm32f2xx_adc.h
>> @@ -0,0 +1,96 @@
>> +/*
>> + * 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
>> +
>
> I'm acutally starting to see a big problem with having the reg defs in
> the common header, as it is not namespace safe. If you have two
> periphs defining a reg with the same name in the same soc you get a
> collision. The only safe way to do it would be to preface every symbol
> with the full type name. This would be super verbose.
>
> On the other side if we don't headerify them they are not available to
> test code.
>
> Out of scope so don't need to do anything for now but maybe we need to
> start a top lvl thread.

I see your point, it's a hard problem to solve though.

>
>> +#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_AVALIABLE
>
> RAND_AVAILABLE

Will fix

>
>> +#undef RAND_MAX
>> +#define RAND_MAX 0xFF
>> +#endif /* RAND_MAX */
>> +
>> +typedef struct {
>
> /* <private> */
>
>> +    SysBusDevice parent_obj;
>> +
>
> /* <public> */

Will add.

>
>> +    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_jofr1;
>> +    uint32_t adc_jofr2;
>> +    uint32_t adc_jofr3;
>> +    uint32_t adc_jofr4;
>> +    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_jdr1;
>> +    uint32_t adc_jdr2;
>> +    uint32_t adc_jdr3;
>> +    uint32_t adc_jdr4;
>> +    uint32_t adc_dr;
>> +
>> +    qemu_irq irq;
>> +} STM32F2XXAdcState;
>
> STM32F2XXADCState

Ok, will fix

Thanks,

Alistair

>
> Regards,
> Peter
>
>> +
>> +#endif /* HW_STM32F2XX_ADC_H */
>> --
>> 2.1.4
>>
>>

Patch
diff mbox

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index a767e4b..2b16590 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -84,6 +84,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/misc/Makefile.objs b/hw/misc/Makefile.objs
index 4aa76ff..b65ec3d 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -37,6 +37,7 @@  obj-$(CONFIG_OMAP) += omap_tap.o
 obj-$(CONFIG_SLAVIO) += slavio_misc.o
 obj-$(CONFIG_ZYNQ) += zynq_slcr.o
 obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
+obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o
 
 obj-$(CONFIG_PVPANIC) += pvpanic.o
 obj-$(CONFIG_EDU) += edu.o
diff --git a/hw/misc/stm32f2xx_adc.c b/hw/misc/stm32f2xx_adc.c
new file mode 100644
index 0000000..659f14f
--- /dev/null
+++ b/hw/misc/stm32f2xx_adc.c
@@ -0,0 +1,332 @@ 
+/*
+ * 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/misc/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_jofr1 = 0x00000000;
+    s->adc_jofr2 = 0x00000000;
+    s->adc_jofr3 = 0x00000000;
+    s->adc_jofr4 = 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_jdr1 = 0x00000000;
+    s->adc_jdr2 = 0x00000000;
+    s->adc_jdr3 = 0x00000000;
+    s->adc_jdr4 = 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
+
+    if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 0) {
+        /* 12-bit */
+        s->adc_dr = s->adc_dr & 0xFFF;
+    } else if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 1) {
+        /* 10-bit */
+        s->adc_dr = s->adc_dr & 0x3FF;
+    } else if (((s->adc_cr1 & ADC_CR1_RES) >> 24) == 2) {
+        /* 8-bit */
+        s->adc_dr = s->adc_dr & 0xFF;
+    } else {
+        /* 6-bit */
+        s->adc_dr = 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("%s: Address: 0x%"HWADDR_PRIx"\n", __func__, addr);
+
+    if (addr >= 0x100) {
+        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:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jofr1;
+    case ADC_JOFR2:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jofr2;
+    case ADC_JOFR3:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jofr3;
+    case ADC_JOFR4:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jofr4;
+    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 " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jsqr;
+    case ADC_JDR1:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jdr1 - s->adc_jofr1;
+    case ADC_JDR2:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jdr2 - s->adc_jofr2;
+    case ADC_JDR3:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jdr3 - s->adc_jofr3;
+    case ADC_JDR4:
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        return s->adc_jdr4 - s->adc_jofr4;
+    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("%s: Address: 0x%"HWADDR_PRIx", Value: 0x%x\n",
+             __func__, 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:
+        s->adc_jofr1 = (value & 0xFFF);
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JOFR2:
+        s->adc_jofr2 = (value & 0xFFF);
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JOFR3:
+        s->adc_jofr3 = (value & 0xFFF);
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JOFR4:
+        s->adc_jofr4 = (value & 0xFFF);
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded 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 " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JDR1:
+        s->adc_jdr1 = value;
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JDR2:
+        s->adc_jdr2 = value;
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JDR3:
+        s->adc_jdr3 = value;
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded for compatability\n", __func__);
+        break;
+    case ADC_JDR4:
+        s->adc_jdr4 = value;
+        qemu_log_mask(LOG_UNIMP, "%s: " \
+                      "Injection ADC is not implemented, the registers are " \
+                      "includded 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/misc/stm32f2xx_adc.h b/include/hw/misc/stm32f2xx_adc.h
new file mode 100644
index 0000000..e0ec1d7
--- /dev/null
+++ b/include/hw/misc/stm32f2xx_adc.h
@@ -0,0 +1,96 @@ 
+/*
+ * 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 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_AVALIABLE
+#undef RAND_MAX
+#define RAND_MAX 0xFF
+#endif /* RAND_MAX */
+
+typedef struct {
+    SysBusDevice parent_obj;
+
+    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_jofr1;
+    uint32_t adc_jofr2;
+    uint32_t adc_jofr3;
+    uint32_t adc_jofr4;
+    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_jdr1;
+    uint32_t adc_jdr2;
+    uint32_t adc_jdr3;
+    uint32_t adc_jdr4;
+    uint32_t adc_dr;
+
+    qemu_irq irq;
+} STM32F2XXAdcState;
+
+#endif /* HW_STM32F2XX_ADC_H */