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