qemu/hw/gpio/bcm2835_gpio.c
<<
>>
Prefs
   1/*
   2 * Raspberry Pi (BCM2835) GPIO Controller
   3 *
   4 * Copyright (c) 2017 Antfield SAS
   5 *
   6 * Authors:
   7 *  Clement Deschamps <clement.deschamps@antfield.fr>
   8 *  Luc Michel <luc.michel@antfield.fr>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  11 * See the COPYING file in the top-level directory.
  12 */
  13
  14#include "qemu/osdep.h"
  15#include "qemu/log.h"
  16#include "qemu/module.h"
  17#include "qemu/timer.h"
  18#include "qapi/error.h"
  19#include "hw/sysbus.h"
  20#include "migration/vmstate.h"
  21#include "hw/sd/sd.h"
  22#include "hw/gpio/bcm2835_gpio.h"
  23#include "hw/irq.h"
  24
  25#define GPFSEL0   0x00
  26#define GPFSEL1   0x04
  27#define GPFSEL2   0x08
  28#define GPFSEL3   0x0C
  29#define GPFSEL4   0x10
  30#define GPFSEL5   0x14
  31#define GPSET0    0x1C
  32#define GPSET1    0x20
  33#define GPCLR0    0x28
  34#define GPCLR1    0x2C
  35#define GPLEV0    0x34
  36#define GPLEV1    0x38
  37#define GPEDS0    0x40
  38#define GPEDS1    0x44
  39#define GPREN0    0x4C
  40#define GPREN1    0x50
  41#define GPFEN0    0x58
  42#define GPFEN1    0x5C
  43#define GPHEN0    0x64
  44#define GPHEN1    0x68
  45#define GPLEN0    0x70
  46#define GPLEN1    0x74
  47#define GPAREN0   0x7C
  48#define GPAREN1   0x80
  49#define GPAFEN0   0x88
  50#define GPAFEN1   0x8C
  51#define GPPUD     0x94
  52#define GPPUDCLK0 0x98
  53#define GPPUDCLK1 0x9C
  54
  55static uint32_t gpfsel_get(BCM2835GpioState *s, uint8_t reg)
  56{
  57    int i;
  58    uint32_t value = 0;
  59    for (i = 0; i < 10; i++) {
  60        uint32_t index = 10 * reg + i;
  61        if (index < sizeof(s->fsel)) {
  62            value |= (s->fsel[index] & 0x7) << (3 * i);
  63        }
  64    }
  65    return value;
  66}
  67
  68static void gpfsel_set(BCM2835GpioState *s, uint8_t reg, uint32_t value)
  69{
  70    int i;
  71    for (i = 0; i < 10; i++) {
  72        uint32_t index = 10 * reg + i;
  73        if (index < sizeof(s->fsel)) {
  74            int fsel = (value >> (3 * i)) & 0x7;
  75            s->fsel[index] = fsel;
  76        }
  77    }
  78
  79    /* SD controller selection (48-53) */
  80    if (s->sd_fsel != 0
  81            && (s->fsel[48] == 0) /* SD_CLK_R */
  82            && (s->fsel[49] == 0) /* SD_CMD_R */
  83            && (s->fsel[50] == 0) /* SD_DATA0_R */
  84            && (s->fsel[51] == 0) /* SD_DATA1_R */
  85            && (s->fsel[52] == 0) /* SD_DATA2_R */
  86            && (s->fsel[53] == 0) /* SD_DATA3_R */
  87            ) {
  88        /* SDHCI controller selected */
  89        sdbus_reparent_card(s->sdbus_sdhost, s->sdbus_sdhci);
  90        s->sd_fsel = 0;
  91    } else if (s->sd_fsel != 4
  92            && (s->fsel[48] == 4) /* SD_CLK_R */
  93            && (s->fsel[49] == 4) /* SD_CMD_R */
  94            && (s->fsel[50] == 4) /* SD_DATA0_R */
  95            && (s->fsel[51] == 4) /* SD_DATA1_R */
  96            && (s->fsel[52] == 4) /* SD_DATA2_R */
  97            && (s->fsel[53] == 4) /* SD_DATA3_R */
  98            ) {
  99        /* SDHost controller selected */
 100        sdbus_reparent_card(s->sdbus_sdhci, s->sdbus_sdhost);
 101        s->sd_fsel = 4;
 102    }
 103}
 104
 105static int gpfsel_is_out(BCM2835GpioState *s, int index)
 106{
 107    if (index >= 0 && index < 54) {
 108        return s->fsel[index] == 1;
 109    }
 110    return 0;
 111}
 112
 113static void gpset(BCM2835GpioState *s,
 114        uint32_t val, uint8_t start, uint8_t count, uint32_t *lev)
 115{
 116    uint32_t changes = val & ~*lev;
 117    uint32_t cur = 1;
 118
 119    int i;
 120    for (i = 0; i < count; i++) {
 121        if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
 122            qemu_set_irq(s->out[start + i], 1);
 123        }
 124        cur <<= 1;
 125    }
 126
 127    *lev |= val;
 128}
 129
 130static void gpclr(BCM2835GpioState *s,
 131        uint32_t val, uint8_t start, uint8_t count, uint32_t *lev)
 132{
 133    uint32_t changes = val & *lev;
 134    uint32_t cur = 1;
 135
 136    int i;
 137    for (i = 0; i < count; i++) {
 138        if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
 139            qemu_set_irq(s->out[start + i], 0);
 140        }
 141        cur <<= 1;
 142    }
 143
 144    *lev &= ~val;
 145}
 146
 147static uint64_t bcm2835_gpio_read(void *opaque, hwaddr offset,
 148        unsigned size)
 149{
 150    BCM2835GpioState *s = (BCM2835GpioState *)opaque;
 151
 152    switch (offset) {
 153    case GPFSEL0:
 154    case GPFSEL1:
 155    case GPFSEL2:
 156    case GPFSEL3:
 157    case GPFSEL4:
 158    case GPFSEL5:
 159        return gpfsel_get(s, offset / 4);
 160    case GPSET0:
 161    case GPSET1:
 162        /* Write Only */
 163        return 0;
 164    case GPCLR0:
 165    case GPCLR1:
 166        /* Write Only */
 167        return 0;
 168    case GPLEV0:
 169        return s->lev0;
 170    case GPLEV1:
 171        return s->lev1;
 172    case GPEDS0:
 173    case GPEDS1:
 174    case GPREN0:
 175    case GPREN1:
 176    case GPFEN0:
 177    case GPFEN1:
 178    case GPHEN0:
 179    case GPHEN1:
 180    case GPLEN0:
 181    case GPLEN1:
 182    case GPAREN0:
 183    case GPAREN1:
 184    case GPAFEN0:
 185    case GPAFEN1:
 186    case GPPUD:
 187    case GPPUDCLK0:
 188    case GPPUDCLK1:
 189        /* Not implemented */
 190        return 0;
 191    default:
 192        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 193                __func__, offset);
 194        break;
 195    }
 196
 197    return 0;
 198}
 199
 200static void bcm2835_gpio_write(void *opaque, hwaddr offset,
 201        uint64_t value, unsigned size)
 202{
 203    BCM2835GpioState *s = (BCM2835GpioState *)opaque;
 204
 205    switch (offset) {
 206    case GPFSEL0:
 207    case GPFSEL1:
 208    case GPFSEL2:
 209    case GPFSEL3:
 210    case GPFSEL4:
 211    case GPFSEL5:
 212        gpfsel_set(s, offset / 4, value);
 213        break;
 214    case GPSET0:
 215        gpset(s, value, 0, 32, &s->lev0);
 216        break;
 217    case GPSET1:
 218        gpset(s, value, 32, 22, &s->lev1);
 219        break;
 220    case GPCLR0:
 221        gpclr(s, value, 0, 32, &s->lev0);
 222        break;
 223    case GPCLR1:
 224        gpclr(s, value, 32, 22, &s->lev1);
 225        break;
 226    case GPLEV0:
 227    case GPLEV1:
 228        /* Read Only */
 229        break;
 230    case GPEDS0:
 231    case GPEDS1:
 232    case GPREN0:
 233    case GPREN1:
 234    case GPFEN0:
 235    case GPFEN1:
 236    case GPHEN0:
 237    case GPHEN1:
 238    case GPLEN0:
 239    case GPLEN1:
 240    case GPAREN0:
 241    case GPAREN1:
 242    case GPAFEN0:
 243    case GPAFEN1:
 244    case GPPUD:
 245    case GPPUDCLK0:
 246    case GPPUDCLK1:
 247        /* Not implemented */
 248        break;
 249    default:
 250        goto err_out;
 251    }
 252    return;
 253
 254err_out:
 255    qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 256            __func__, offset);
 257}
 258
 259static void bcm2835_gpio_reset(DeviceState *dev)
 260{
 261    BCM2835GpioState *s = BCM2835_GPIO(dev);
 262
 263    int i;
 264    for (i = 0; i < 6; i++) {
 265        gpfsel_set(s, i, 0);
 266    }
 267
 268    s->sd_fsel = 0;
 269
 270    /* SDHCI is selected by default */
 271    sdbus_reparent_card(&s->sdbus, s->sdbus_sdhci);
 272
 273    s->lev0 = 0;
 274    s->lev1 = 0;
 275}
 276
 277static const MemoryRegionOps bcm2835_gpio_ops = {
 278    .read = bcm2835_gpio_read,
 279    .write = bcm2835_gpio_write,
 280    .endianness = DEVICE_NATIVE_ENDIAN,
 281};
 282
 283static const VMStateDescription vmstate_bcm2835_gpio = {
 284    .name = "bcm2835_gpio",
 285    .version_id = 1,
 286    .minimum_version_id = 1,
 287    .fields = (VMStateField[]) {
 288        VMSTATE_UINT8_ARRAY(fsel, BCM2835GpioState, 54),
 289        VMSTATE_UINT32(lev0, BCM2835GpioState),
 290        VMSTATE_UINT32(lev1, BCM2835GpioState),
 291        VMSTATE_UINT8(sd_fsel, BCM2835GpioState),
 292        VMSTATE_END_OF_LIST()
 293    }
 294};
 295
 296static void bcm2835_gpio_init(Object *obj)
 297{
 298    BCM2835GpioState *s = BCM2835_GPIO(obj);
 299    DeviceState *dev = DEVICE(obj);
 300    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 301
 302    qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(s), "sd-bus");
 303
 304    memory_region_init_io(&s->iomem, obj,
 305            &bcm2835_gpio_ops, s, "bcm2835_gpio", 0x1000);
 306    sysbus_init_mmio(sbd, &s->iomem);
 307    qdev_init_gpio_out(dev, s->out, 54);
 308}
 309
 310static void bcm2835_gpio_realize(DeviceState *dev, Error **errp)
 311{
 312    BCM2835GpioState *s = BCM2835_GPIO(dev);
 313    Object *obj;
 314
 315    obj = object_property_get_link(OBJECT(dev), "sdbus-sdhci", &error_abort);
 316    s->sdbus_sdhci = SD_BUS(obj);
 317
 318    obj = object_property_get_link(OBJECT(dev), "sdbus-sdhost", &error_abort);
 319    s->sdbus_sdhost = SD_BUS(obj);
 320}
 321
 322static void bcm2835_gpio_class_init(ObjectClass *klass, void *data)
 323{
 324    DeviceClass *dc = DEVICE_CLASS(klass);
 325
 326    dc->vmsd = &vmstate_bcm2835_gpio;
 327    dc->realize = &bcm2835_gpio_realize;
 328    dc->reset = &bcm2835_gpio_reset;
 329}
 330
 331static const TypeInfo bcm2835_gpio_info = {
 332    .name          = TYPE_BCM2835_GPIO,
 333    .parent        = TYPE_SYS_BUS_DEVICE,
 334    .instance_size = sizeof(BCM2835GpioState),
 335    .instance_init = bcm2835_gpio_init,
 336    .class_init    = bcm2835_gpio_class_init,
 337};
 338
 339static void bcm2835_gpio_register_types(void)
 340{
 341    type_register_static(&bcm2835_gpio_info);
 342}
 343
 344type_init(bcm2835_gpio_register_types)
 345