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