qemu/hw/sd/zynqmp-sdhci.c
<<
>>
Prefs
   1/*
   2 * ZynqMP SDHCI controller.
   3 *
   4 * Copyright (c) 2013 Xilinx Inc
   5 * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "hw/sysbus.h"
  28#include "qemu/log.h"
  29
  30#include "qemu/bitops.h"
  31#include "qapi/qmp/qerror.h"
  32#include "sysemu/blockdev.h"
  33#include "sysemu/block-backend.h"
  34#include "migration/vmstate.h"
  35#include "hw/qdev-properties.h"
  36
  37#include "hw/fdt_generic_util.h"
  38
  39#include "hw/sd/sd.h"
  40#include "hw/sd/sdhci.h"
  41#include "qapi/error.h"
  42#include "sdhci-internal.h"
  43
  44#ifndef ZYNQMP_SDHCI_ERR_DEBUG
  45#define ZYNQMP_SDHCI_ERR_DEBUG 0
  46#endif
  47
  48#define TYPE_ZYNQMP_SDHCI "xilinx.zynqmp-sdhci"
  49
  50#define ZYNQMP_SDHCI(obj) \
  51     OBJECT_CHECK(ZynqMPSDHCIState, (obj), TYPE_ZYNQMP_SDHCI)
  52
  53#define ZYNQMP_SDHCI_PARENT_CLASS \
  54    object_class_get_parent(object_class_by_name(TYPE_ZYNQMP_SDHCI))
  55
  56typedef struct ZynqMPSDHCIState {
  57    /*< private >*/
  58    SDHCIState parent_obj;
  59
  60    /*< public >*/
  61    SDState *card;
  62    uint8_t drive_index;
  63    bool is_mmc;
  64} ZynqMPSDHCIState;
  65
  66static void zynqmp_sdhci_slottype_handler(void *opaque, int n, int level)
  67{
  68    ZynqMPSDHCIState *s = ZYNQMP_SDHCI(opaque);
  69
  70    assert(n == 0);
  71
  72    if (!s->card) {
  73        /* Card Not Connected */
  74        return;
  75    }
  76
  77    if (level != s->is_mmc) {
  78        qemu_log_mask(LOG_GUEST_ERROR, "WARNING: Inserted %s Card but"
  79                      " Slot configured as %s\n",
  80                      s->is_mmc ? "MMC" : "SD",
  81                      level ? "MMC" : "SD");
  82    }
  83}
  84
  85static void zynqmp_sdhci_reset(DeviceState *dev)
  86{
  87    DeviceClass *dc_parent = DEVICE_CLASS(ZYNQMP_SDHCI_PARENT_CLASS);
  88    ZynqMPSDHCIState *s = ZYNQMP_SDHCI(dev);
  89    SDHCIState *p = SYSBUS_SDHCI(dev);
  90
  91    dc_parent->reset(dev);
  92    if (s->is_mmc) {
  93        p->capareg = deposit64(p->capareg, R_SDHC_CAPAB_SLOT_TYPE_SHIFT,
  94                  R_SDHC_CAPAB_SLOT_TYPE_LENGTH, 0x01);
  95    }
  96}
  97
  98static void zynqmp_sdhci_realize(DeviceState *dev, Error **errp)
  99{
 100    DeviceClass *dc_parent = DEVICE_CLASS(ZYNQMP_SDHCI_PARENT_CLASS);
 101    ZynqMPSDHCIState *s = ZYNQMP_SDHCI(dev);
 102    DriveInfo *di_sd, *di_mmc;
 103    DeviceState *carddev_sd;
 104    static int index_offset = 0;
 105
 106    /* Xilinx: This device is used in some Zynq-7000 devices which don't
 107     * set the drive-index property. In order to avoid errors we increament
 108     * the drive index each time we call this.
 109     * The other solution could be to just ignore the error returned when
 110     * connecting the drive. That seems risky though.
 111     */
 112    if (!s->drive_index) {
 113        s->drive_index += index_offset;
 114        index_offset++;
 115    }
 116
 117    qdev_prop_set_uint8(dev, "sd-spec-version", 3);
 118    qdev_prop_set_uint64(dev, "capareg", 0x280737ec6481);
 119    qdev_prop_set_uint8(dev, "uhs", UHS_I);
 120    carddev_sd = qdev_new(TYPE_SD_CARD);
 121    object_property_add_child(OBJECT(dev), "sd-card",
 122                              OBJECT(carddev_sd));
 123    object_property_set_bool(OBJECT(carddev_sd), "spi", false, &error_fatal);
 124
 125    /*
 126     * drive_index is used to attach a card in SD mode.
 127     * drive_index + 2 is used to attach a card in MMC mode.
 128     *
 129     * If the user attaches a card to both slots, we bail out.
 130     */
 131
 132    di_sd = drive_get_by_index(IF_SD , s->drive_index);
 133    di_mmc = drive_get_by_index(IF_SD, (s->drive_index + 2));
 134
 135    if (di_sd && di_mmc) {
 136        if (!di_sd->is_default && !di_mmc->is_default) {
 137            error_setg(&error_fatal, "Cannot attach both an MMC"
 138                                     " and an SD card into the same slot");
 139        }
 140    }
 141
 142    if (di_sd) {
 143        qdev_prop_set_drive(carddev_sd, "drive", blk_by_legacy_dinfo(di_sd));
 144        object_property_set_bool(OBJECT(carddev_sd), "mmc", false, &error_fatal);
 145    }
 146
 147    if (di_mmc) {
 148        qdev_prop_set_uint8(carddev_sd, "spec_version", SD_PHY_SPECv3_01_VERS);
 149        qdev_prop_set_drive(carddev_sd, "drive", blk_by_legacy_dinfo(di_mmc));
 150        object_property_set_bool(OBJECT(carddev_sd), "mmc", true, &error_fatal);
 151        s->is_mmc = true;
 152    }
 153
 154    qdev_realize(carddev_sd,
 155                           qdev_get_child_bus(DEVICE(dev), "sd-bus"),
 156                           &error_abort);
 157    qdev_init_gpio_in_named(dev, zynqmp_sdhci_slottype_handler, "SLOTTYPE", 1);
 158    s->card = SD_CARD(carddev_sd);
 159
 160    dc_parent->realize(dev, errp);
 161}
 162
 163static Property zynqmp_sdhci_properties[] = {
 164    DEFINE_PROP_UINT8("drive-index", ZynqMPSDHCIState, drive_index, 0),
 165    DEFINE_PROP_END_OF_LIST(),
 166};
 167
 168static void zynqmp_sdhci_class_init(ObjectClass *klass, void *data)
 169{
 170    DeviceClass *dc = DEVICE_CLASS(klass);
 171
 172    dc->realize = zynqmp_sdhci_realize;
 173    device_class_set_props(dc, zynqmp_sdhci_properties);
 174    dc->reset = zynqmp_sdhci_reset;
 175}
 176
 177static const TypeInfo zynqmp_sdhci_info = {
 178    .name          = TYPE_ZYNQMP_SDHCI,
 179    .parent        = TYPE_SYSBUS_SDHCI,
 180    .class_init    = zynqmp_sdhci_class_init,
 181    .instance_size = sizeof(ZynqMPSDHCIState),
 182};
 183
 184static void zynqmp_sdhci_register_types(void)
 185{
 186    type_register_static(&zynqmp_sdhci_info);
 187}
 188
 189type_init(zynqmp_sdhci_register_types)
 190