123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /*
- * SD card bus interface code.
- *
- * Copyright (c) 2015 Linaro Limited
- *
- * Author:
- * Peter Maydell <peter.maydell@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2 or later, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "qemu/osdep.h"
- #include "hw/qdev-core.h"
- #include "hw/sd/sd.h"
- #include "qemu/module.h"
- #include "trace.h"
- static inline const char *sdbus_name(SDBus *sdbus)
- {
- return sdbus->qbus.name;
- }
- static SDState *get_card(SDBus *sdbus)
- {
- /* We only ever have one child on the bus so just return it */
- BusChild *kid = QTAILQ_FIRST(&sdbus->qbus.children);
- if (!kid) {
- return NULL;
- }
- return SD_CARD(kid->child);
- }
- uint8_t sdbus_get_dat_lines(SDBus *sdbus)
- {
- SDState *slave = get_card(sdbus);
- uint8_t dat_lines = 0b1111; /* 4 bit bus width */
- if (slave) {
- SDCardClass *sc = SD_CARD_GET_CLASS(slave);
- if (sc->get_dat_lines) {
- dat_lines = sc->get_dat_lines(slave);
- }
- }
- trace_sdbus_get_dat_lines(sdbus_name(sdbus), dat_lines);
- return dat_lines;
- }
- bool sdbus_get_cmd_line(SDBus *sdbus)
- {
- SDState *slave = get_card(sdbus);
- bool cmd_line = true;
- if (slave) {
- SDCardClass *sc = SD_CARD_GET_CLASS(slave);
- if (sc->get_cmd_line) {
- cmd_line = sc->get_cmd_line(slave);
- }
- }
- trace_sdbus_get_cmd_line(sdbus_name(sdbus), cmd_line);
- return cmd_line;
- }
- void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts)
- {
- SDState *card = get_card(sdbus);
- trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts);
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- assert(sc->set_voltage);
- sc->set_voltage(card, millivolts);
- }
- }
- int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response)
- {
- SDState *card = get_card(sdbus);
- trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg);
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- return sc->do_command(card, req, response);
- }
- return 0;
- }
- void sdbus_write_data(SDBus *sdbus, uint8_t value)
- {
- SDState *card = get_card(sdbus);
- trace_sdbus_write(sdbus_name(sdbus), value);
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- sc->write_data(card, value);
- }
- }
- uint8_t sdbus_read_data(SDBus *sdbus)
- {
- SDState *card = get_card(sdbus);
- uint8_t value = 0;
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- value = sc->read_data(card);
- }
- trace_sdbus_read(sdbus_name(sdbus), value);
- return value;
- }
- bool sdbus_data_ready(SDBus *sdbus)
- {
- SDState *card = get_card(sdbus);
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- return sc->data_ready(card);
- }
- return false;
- }
- bool sdbus_get_inserted(SDBus *sdbus)
- {
- SDState *card = get_card(sdbus);
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- return sc->get_inserted(card);
- }
- return false;
- }
- bool sdbus_get_readonly(SDBus *sdbus)
- {
- SDState *card = get_card(sdbus);
- if (card) {
- SDCardClass *sc = SD_CARD_GET_CLASS(card);
- return sc->get_readonly(card);
- }
- return false;
- }
- void sdbus_set_inserted(SDBus *sdbus, bool inserted)
- {
- SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus);
- BusState *qbus = BUS(sdbus);
- if (sbc->set_inserted) {
- sbc->set_inserted(qbus->parent, inserted);
- }
- }
- void sdbus_set_readonly(SDBus *sdbus, bool readonly)
- {
- SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus);
- BusState *qbus = BUS(sdbus);
- if (sbc->set_readonly) {
- sbc->set_readonly(qbus->parent, readonly);
- }
- }
- void sdbus_reparent_card(SDBus *from, SDBus *to)
- {
- SDState *card = get_card(from);
- SDCardClass *sc;
- bool readonly;
- /* We directly reparent the card object rather than implementing this
- * as a hotpluggable connection because we don't want to expose SD cards
- * to users as being hotpluggable, and we can get away with it in this
- * limited use case. This could perhaps be implemented more cleanly in
- * future by adding support to the hotplug infrastructure for "device
- * can be hotplugged only via code, not by user".
- */
- if (!card) {
- return;
- }
- sc = SD_CARD_GET_CLASS(card);
- readonly = sc->get_readonly(card);
- sdbus_set_inserted(from, false);
- qdev_set_parent_bus(DEVICE(card), &to->qbus);
- sdbus_set_inserted(to, true);
- sdbus_set_readonly(to, readonly);
- }
- static const TypeInfo sd_bus_info = {
- .name = TYPE_SD_BUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(SDBus),
- .class_size = sizeof(SDBusClass),
- };
- static void sd_bus_register_types(void)
- {
- type_register_static(&sd_bus_info);
- }
- type_init(sd_bus_register_types)
|