r3726 - in trunk/src/host/qemu-neo1973: . hw
andrew at sita.openmoko.org
andrew at sita.openmoko.org
Sat Dec 22 04:01:34 CET 2007
Author: andrew
Date: 2007-12-22 04:01:31 +0100 (Sat, 22 Dec 2007)
New Revision: 3726
Added:
trunk/src/host/qemu-neo1973/hw/ar6000.c
Modified:
trunk/src/host/qemu-neo1973/Makefile
trunk/src/host/qemu-neo1973/hw/neo1973.c
trunk/src/host/qemu-neo1973/hw/pcmcia.h
trunk/src/host/qemu-neo1973/hw/sd.c
trunk/src/host/qemu-neo1973/hw/sd.h
Log:
Initial version of Atheros AR6001 files. Currently it's a dummy SDIO with the
correct Manufacturer/Card ID. SDIO FBR I/O not yet support.
Attach the AR6k to the SD slot of gta02fake.
Add missing CISTPL IDs to pcmcia.h as they're now shared with SDIO.
Modified: trunk/src/host/qemu-neo1973/Makefile
===================================================================
--- trunk/src/host/qemu-neo1973/Makefile 2007-12-22 02:30:17 UTC (rev 3725)
+++ trunk/src/host/qemu-neo1973/Makefile 2007-12-22 03:01:31 UTC (rev 3726)
@@ -59,7 +59,7 @@
OBJS+=scsi-disk.o cdrom.o
OBJS+=usb.o usb-hub.o usb-linux.o usb-linux-gadget.o
OBJS+=usb-hid.o usb-msd.o usb-wacom.o usb-net.o usb-bt.o
-OBJS+=sd.o ssi-sd.o
+OBJS+=sd.o ssi-sd.o ar6000.o
ifdef CONFIG_WIN32
OBJS+=tap-win32.o
Added: trunk/src/host/qemu-neo1973/hw/ar6000.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/ar6000.c 2007-12-22 02:30:17 UTC (rev 3725)
+++ trunk/src/host/qemu-neo1973/hw/ar6000.c 2007-12-22 03:01:31 UTC (rev 3726)
@@ -0,0 +1,880 @@
+/*
+ * Atheros AR600X Wireless Ethernet SDIO cards. Firmware 1.3.
+ *
+ * Copyright (c) 2007 OpenMoko, Inc.
+ * Author: Andrzej Zaborowski <andrew at openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * BIG TODO: Merge all the generic SDIO emulation back into sd.c and
+ * allow hooks for card specific functions to be registered. This file
+ * then would solely provide callbacks for these hooks.
+ */
+
+#include "hw.h"
+#include "net.h"
+#include "sd.h"
+#include "pcmcia.h"
+
+typedef enum {
+ sd_r0 = 0, /* no response */
+ sd_r1, /* normal response command */
+ sd_r2_i, /* CID register */
+ sd_r2_s, /* CSD register */
+ sd_r3, /* OCR register */
+ sd_r4, /* SDIO OCR register */
+ sd_r5, /* SDIO direct I/O */
+ sd_r6, /* Published RCA response */
+ sd_r7, /* Operating voltage */
+ sd_r1b = -1,
+} sd_rsp_type_t;
+
+struct sdio_s {
+ enum sd_state_e state;
+ uint32_t ioocr;
+ uint16_t rca;
+ uint32_t card_status;
+
+ struct {
+ uint8_t io_enable;
+ uint8_t intr_enable;
+ uint8_t intr;
+ uint8_t bus;
+ uint8_t e4mi;
+ uint8_t power;
+ uint8_t speed;
+ } cccr;
+ struct {
+ } fbr[7];
+ const uint8_t *cis;
+ int cislen;
+
+ qemu_irq *func_irq;
+
+ int spi;
+ int sdio_ok;
+ int current_cmd;
+ uint16_t blk_len[8];
+ struct {
+ uint8_t func;
+ int dir;
+ int blk_len;
+ int blk_num;
+ int step;
+ uint32_t data_start;
+ uint32_t data_offset;
+ uint8_t data[2048];
+ } transfer; /* TODO Move to per function struct to support sspnd */
+
+ void (*write[8])(struct sdio_s *sd, uint32_t addr, uint8_t *data, int len);
+ void (*read[8])(struct sdio_s *sd, uint32_t addr, uint8_t *data, int len);
+ void (*reset)(struct sdio_s *sd);
+
+ struct sd_card_s card;
+};
+
+#define SDIO_SIZE 0x20000
+#define SDIO_ADDR_MASK (SDIO_SIZE - 1)
+
+static const sd_cmd_type_t sd_cmd_type[64] = {
+ sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac,
+ sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac,
+ sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none,
+ sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none,
+ sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
+ sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+static const int sd_cmd_class[64] = {
+ 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6,
+ 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7,
+ 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8,
+};
+
+static void sd_set_ioocr(struct sdio_s *sd)
+{
+ /* 2.0 - 3.6 V, No memory present, One function only */
+ sd->ioocr = 0x00ffff00;
+}
+
+static void sd_set_rca(struct sdio_s *sd)
+{
+ sd->rca += 0x4567;
+}
+
+static void sd_set_cardstatus(struct sdio_s *sd)
+{
+ sd->card_status = 0x00001e00;
+}
+
+static int sd_req_crc_validate(struct sd_request_s *req)
+{
+ uint8_t buffer[5];
+ buffer[0] = 0x40 | req->cmd;
+ buffer[1] = (req->arg >> 24) & 0xff;
+ buffer[2] = (req->arg >> 16) & 0xff;
+ buffer[3] = (req->arg >> 8) & 0xff;
+ buffer[4] = (req->arg >> 0) & 0xff;
+ return 0;
+ return sd_crc7(buffer, 5) != req->crc; /* TODO */
+}
+
+static void sd_response_r1_make(struct sdio_s *sd,
+ uint8_t *response, uint32_t last_status)
+{
+ uint32_t mask = CARD_STATUS_B ^ ILLEGAL_COMMAND;
+ uint32_t status;
+
+ status = (sd->card_status & ~mask) | (last_status & mask);
+ sd->card_status &= ~CARD_STATUS_C;
+
+ response[0] = (status >> 24) & 0xff;
+ response[1] = (status >> 16) & 0xff;
+ response[2] = (status >> 8) & 0xff;
+ response[3] = (status >> 0) & 0xff;
+}
+
+static void sd_response_r4_make(struct sdio_s *sd, uint8_t *response)
+{
+ response[0] = (sd->ioocr >> 24) & 0xff;
+ response[1] = (sd->ioocr >> 16) & 0xff;
+ response[2] = (sd->ioocr >> 8) & 0xff;
+ response[3] = (sd->ioocr >> 0) & 0xff;
+}
+
+static void sd_response_r5_make(struct sdio_s *sd, uint8_t *response)
+{
+ int byte = 0;
+ uint8_t status, state;
+
+ switch (sd->state) {
+ case sd_initialization_state:
+ case sd_standby_state:
+ case sd_inactive_state:
+ default:
+ state = 0x00;
+ break;
+ case sd_command_state:
+ state = 0x01;
+ break;
+ case sd_transfer_state:
+ state = 0x02;
+ break;
+ }
+
+ if (sd->spi) {
+ status = ((sd->card_status & 0xb7380003) ? (1 << 6) : 0) |
+ ((sd->card_status & ADDRESS_ERROR) ? (1 << 4) : 0) |
+ ((sd->card_status & COM_CRC_ERROR) ? (1 << 3) : 0) |
+ ((sd->card_status & ILLEGAL_COMMAND) ? (1 << 2) : 0) |
+ ((state == 0x00) ? (1 << 0) : 0);
+ } else {
+ status = ((sd->card_status & COM_CRC_ERROR) ? (1 << 7) : 0) |
+ ((sd->card_status & ILLEGAL_COMMAND) ? (1 << 6) : 0) |
+ (state << 4) |
+ ((sd->card_status & 0x37380003) ? (1 << 3) : 0) |
+ ((sd->card_status & ADDRESS_ERROR) ? (1 << 1) : 0) |
+ ((sd->card_status & OUT_OF_RANGE) ? (1 << 0) : 0);
+
+ response[byte ++] = 0;
+ response[byte ++] = 0;
+ }
+ sd->card_status &= ~0xf7f80003; /* TODO check */
+
+ response[byte ++] = status;
+ response[byte ++] = sd->transfer.data[sd->transfer.data_offset];
+}
+
+static void sd_response_r6_make(struct sdio_s *sd, uint8_t *response)
+{
+ uint16_t arg;
+ uint16_t status;
+
+ arg = sd->rca;
+ status =
+ ((sd->card_status & SD_ERROR) ? (1 << 13) : 0) |
+ ((sd->card_status & ILLEGAL_COMMAND) ? (1 << 14) : 0) |
+ ((sd->card_status & COM_CRC_ERROR) ? (1 << 15) : 0);
+
+ response[0] = (arg >> 8) & 0xff;
+ response[1] = arg & 0xff;
+ response[2] = (status >> 8) & 0xff;
+ response[3] = 0;
+}
+
+static void sdio_intr_update(struct sdio_s *sd)
+{
+ int level;
+
+ if (!(sd->cccr.intr_enable & 1) || /* IENM */
+ ((sd->cccr.bus & 3) == 2 && /* BusWidth */
+ !sd->cccr.e4mi && sd->state == sd_transfer_state) ||
+ (sd->spi && !((sd->cccr.bus & (1 << 6)) && /* SCSI */
+ (sd->cccr.bus & (1 << 5))))) /* ECSI */
+ level = 0;
+ else
+ level = !!((sd->cccr.intr << 1) & sd->cccr.intr_enable);
+
+ qemu_set_irq(sd->card.irq, level);
+}
+
+static void sdio_reset(struct sdio_s *sd)
+{
+ int i;
+
+ sd->state = sd_initialization_state;
+ sd->rca = 0x0000;
+ sd->sdio_ok = 0;
+ sd_set_ioocr(sd);
+ sd_set_cardstatus(sd);
+
+ memset(&sd->cccr, 0, sizeof(sd->cccr));
+ memset(&sd->fbr, 0, sizeof(sd->fbr));
+ for (i = 0; i < 8; i ++)
+ sd->blk_len[i] = 0;
+ if (sd->reset)
+ sd->reset(sd);
+
+ /* TODO: should preserve CDDisable (sd->cccr.bus & (1 << 7)) */
+}
+
+static void sdio_transfer_done(struct sdio_s *sd)
+{
+ sd->state = sd_command_state;
+
+ /* Must check interrupts because of 4-wire mode Interrupt Period. */
+ if ((sd->cccr.bus & 3) == 2) /* BusWidth */
+ sdio_intr_update(sd);
+}
+
+static sd_rsp_type_t sdio_normal_command(struct sdio_s *sd,
+ struct sd_request_s req)
+{
+ uint32_t rca = 0x0000;
+ uint32_t addr;
+ uint8_t fun;
+
+ if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
+ rca = req.arg >> 16;
+
+ switch (req.cmd) {
+ /* Basic commands (Class 0) */
+ case 0: /* CMD0: GO_IDLE_STATE */
+ /* XXX: Used to switch to SPI mode and back */
+ printf("%s: Bus mode switch attempt\n", __FUNCTION__);
+ break;
+
+ case 3: /* CMD3: SEND_RELATIVE_ADDR */
+ if (sd->spi || !sd->sdio_ok)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_initialization_state:
+ case sd_standby_state:
+ sd->state = sd_standby_state;
+ sd_set_rca(sd);
+ return sd_r6;
+
+ default:
+ break;
+ }
+ break;
+
+ /* I/O mode commands (Class 9) */
+ case 5: /* CMD5: IO_SEND_OP_COND */
+ switch (sd->state) {
+ case sd_initialization_state:
+ /* We accept any voltage. 10000 V is nothing. */
+ if (req.arg) {
+ sd->state = sd_ready_state;
+ sd->sdio_ok = 1;
+ }
+
+ return sd_r4;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Basic commands (Class 0) */
+ case 7: /* CMD7: SELECT/DESELECT_CARD */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_standby_state:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ sd->state = sd_command_state;
+ return sd_r1b;
+
+ case sd_command_state:
+ if (sd->rca != rca)
+ sd->state = sd_standby_state;
+
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ case 15: /* CMD15: GO_INACTIVE_STATE */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_initialization_state:
+ case sd_standby_state:
+ case sd_command_state:
+ if (sd->rca && sd->rca != rca)
+ return sd_r0;
+
+ sd->state = sd_inactive_state;
+ return sd_r0;
+
+ default:
+ break;
+ }
+ break;
+
+ /* I/O mode commands (Class 9) */
+ case 52: /* CMD52: IO_RW_DIRECT */
+ /* XXX In some situations this must preserve cmdNo and restore later */
+ switch (sd->state) {
+ case sd_command_state:
+ case sd_transfer_state:
+ fun = (req.arg >> 28) & 7;
+ addr = (req.arg >> 9) & SDIO_ADDR_MASK;
+ sd->transfer.data_offset = 0;
+ sd->transfer.step = 1;
+
+ if (unlikely(fun > ((sd->ioocr >> 28) & 7))) {
+ sd->card_status |= ADDRESS_ERROR;
+ sd->transfer.data[sd->transfer.data_offset] = req.arg & 0xff;
+ return sd_r5;
+ }
+
+ if ((req.arg >> 31) & 1) { /* R/W */
+ sd->transfer.data[sd->transfer.data_offset] = req.arg & 0xff;
+ sd->write[fun](sd, addr, sd->transfer.data +
+ sd->transfer.data_offset, 1);
+ }
+
+ if (((~req.arg >> 31) & 1) || /* !R/W */
+ ((req.arg >> 27) & 1)) /* RAW */
+ sd->read[fun](sd, addr, sd->transfer.data +
+ sd->transfer.data_offset, 1);
+
+ return sd_r5;
+
+ default:
+ break;
+ }
+ break;
+
+ case 53: /* CMD53: IO_RW_EXTENDED */
+ switch (sd->state) {
+ case sd_command_state:
+ fun = (req.arg >> 28) & 7;
+ addr = (req.arg >> 9) & SDIO_ADDR_MASK;
+
+ if (unlikely(fun > ((sd->ioocr >> 28) & 7))) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1b;
+ }
+
+ sd->transfer.dir = (req.arg >> 31) & 1; /* R/W */
+ sd->transfer.step = (req.arg >> 27) & 1; /* OPCode */
+ sd->transfer.func = fun;
+ sd->transfer.data_start = addr;
+ sd->transfer.data_offset = 0;
+ if ((req.arg >> 28) & 1) { /* BlockMode */
+ if (sd->blk_len[fun] < 1 || sd->blk_len[fun] > 2048)
+ return sd_r1b;
+
+ sd->transfer.blk_len = sd->blk_len[fun];
+ } else
+ sd->transfer.blk_len = 1;
+ sd->transfer.blk_num = ((req.arg >> 0) & 0x1ff) ?:
+ ((req.arg >> 28) & 1) ? -1 : 0x200; /* BlockMode */
+
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Basic commands (Class 0) */
+ case 59: /* CMD59: CRC_ON_OFF */
+ if (!sd->spi)
+ goto bad_cmd;
+ /* TODO */
+ return sd_r1;
+
+ default:
+ bad_cmd:
+ sd->card_status |= ILLEGAL_COMMAND;
+
+ printf("%s: Unknown CMD%i\n", __FUNCTION__, req.cmd);
+ return sd->spi ? sd_r1 : sd_r0;
+ }
+
+ sd->card_status |= ILLEGAL_COMMAND;
+ printf("%s: CMD%i in a wrong state\n", __FUNCTION__, req.cmd);
+ return sd->spi ? sd_r1 : sd_r0;
+}
+
+static int sdio_do_command(struct sdio_s *sd, struct sd_request_s *req,
+ uint8_t *response)
+{
+ uint32_t last_status = sd->card_status;
+ sd_rsp_type_t rtype;
+ int rsplen;
+
+ if (sd_req_crc_validate(req)) {
+ sd->card_status &= ~COM_CRC_ERROR;
+ return 0;
+ }
+
+ sd->card_status &= ~CARD_STATUS_B;
+
+ rtype = sdio_normal_command(sd, *req);
+
+ sd->current_cmd = req->cmd;
+
+ switch (rtype) {
+ case sd_r1:
+ case sd_r1b:
+ sd_response_r1_make(sd, response, last_status);
+ rsplen = 4;
+ break;
+
+ case sd_r4:
+ sd_response_r4_make(sd, response);
+ rsplen = 4;
+ break;
+
+ case sd_r5:
+ sd_response_r5_make(sd, response);
+ rsplen = sd->spi ? 2 : 4;
+ break;
+
+ case sd_r6:
+ sd_response_r6_make(sd, response);
+ rsplen = 4;
+ break;
+
+ case sd_r0:
+ default:
+ rsplen = 0;
+ break;
+ }
+
+ if (sd->card_status & ILLEGAL_COMMAND)
+ rsplen = 0;
+
+ return rsplen;
+}
+
+static void sdio_write_data(struct sdio_s *sd, uint8_t value)
+{
+ if (sd->state != sd_transfer_state) {
+ printf("%s: not in Transfer state\n", __FUNCTION__);
+ return;
+ }
+
+ if (sd->card_status & (ADDRESS_ERROR | OUT_OF_RANGE | SD_ERROR))
+ return;
+
+ switch (sd->current_cmd) {
+ case 53: /* CMD53: IO_RW_EXTENDED */
+ if (!sd->transfer.dir)
+ goto bad_cmd;
+
+ sd->transfer.data[sd->transfer.data_offset ++] = value;
+ if (sd->transfer.data_offset >= sd->transfer.blk_len) {
+ /* TODO: Check CRC before committing */
+ sd->write[sd->transfer.func](sd, sd->transfer.data_start,
+ sd->transfer.data, sd->transfer.blk_len);
+
+ if (sd->transfer.blk_num >= 0)
+ if (!-- sd->transfer.blk_num) {
+ sdio_transfer_done(sd);
+ break;
+ }
+
+ sd->transfer.data_start +=
+ sd->transfer.blk_len * sd->transfer.step;
+ sd->transfer.data_offset = 0;
+ }
+ break;
+
+ default:
+ bad_cmd:
+ printf("%s: unknown command\n", __FUNCTION__);
+ break;
+ }
+}
+
+static uint8_t sdio_read_data(struct sdio_s *sd)
+{
+ /* TODO: Append CRCs */
+ uint8_t ret;
+
+ if (sd->state != sd_receivingdata_state) {
+ printf("%s: not in Transfer state\n", __FUNCTION__);
+ return 0x00;
+ }
+
+ if (sd->card_status & (ADDRESS_ERROR | OUT_OF_RANGE | SD_ERROR))
+ return 0x00;
+
+ switch (sd->current_cmd) {
+ case 53: /* CMD53: IO_RW_EXTENDED */
+ if (sd->transfer.dir)
+ goto bad_cmd;
+
+ if (!sd->transfer.data_offset)
+ sd->read[sd->transfer.func](sd, sd->transfer.data_start,
+ sd->transfer.data, sd->transfer.blk_len);
+
+ ret = sd->transfer.data[sd->transfer.data_offset ++];
+
+ if (sd->transfer.data_offset >= sd->transfer.blk_len) {
+ if (sd->transfer.blk_num >= 0)
+ if (!-- sd->transfer.blk_num) {
+ sdio_transfer_done(sd);
+ break;
+ }
+
+ sd->transfer.data_start +=
+ sd->transfer.blk_len * sd->transfer.step;
+ sd->transfer.data_offset = 0;
+ }
+ break;
+
+ default:
+ bad_cmd:
+ printf("%s: unknown command\n", __FUNCTION__);
+ return 0x00;
+ }
+
+ return ret;
+}
+
+static int sdio_data_ready(struct sdio_s *sd)
+{
+ return sd->state == sd_transfer_state;
+}
+
+static void sdio_cccr_write(struct sdio_s *sd, uint32_t offset, uint8_t value)
+{
+ switch (offset) {
+ case 0x02: /* I/O Enable */
+ sd->cccr.io_enable =
+ value & (((1 << ((sd->ioocr >> 28) & 7)) - 1) << 1);
+ /* TODO: reset the disabled functions */
+ break;
+
+ case 0x04: /* Int Enable */
+ sd->cccr.intr_enable = value;
+ sdio_intr_update(sd);
+ break;
+
+ case 0x06: /* I/O Abort */
+ if ((value >> 3) & 1) /* RES */
+ sdio_reset(sd);
+ else if ((value >> 0) & 7) { /* ASx */
+ if (sd->state == sd_transfer_state &&
+ sd->transfer.func == (value & 7) && !sd->spi)
+ sdio_transfer_done(sd);
+ else
+ fprintf(stderr, "%s: no transfer to abort for function %i\n",
+ __FUNCTION__, value & 7);
+ }
+
+ break;
+
+ case 0x07: /* Bus Interface Control */
+ sd->cccr.bus = (value & 0xe3) | 0x40;
+ if (value & 1)
+ fprintf(stderr, "%s: wrong bus-width selected\n", __FUNCTION__);
+ if (sd->spi)
+ sdio_intr_update(sd);
+ /* XXX Possibly toggle some SD_DETECT gpio on CDDisable change */
+ break;
+
+ case 0x08: /* Card Capability */
+ sd->cccr.e4mi = value & 0x20;
+ break;
+
+ /* Since SBS == 0, Function Select and Bus Suspend are R/O. */
+
+ case 0x0f: /* Ready Flags */
+ break;
+
+ case 0x10: /* Fn0 Block Size LSB */
+ sd->blk_len[0] &= 0xff << 8;
+ sd->blk_len[0] |= value;
+ break;
+
+ case 0x11: /* Fn0 Block Size MSB */
+ sd->blk_len[0] &= 0xff;
+ sd->blk_len[0] |= value << 8;
+ break;
+
+ case 0x12: /* Power Control */
+ sd->cccr.power |= value & 0x02; /* EMPC */
+ break;
+
+ case 0x13: /* High-Speed */
+ sd->cccr.speed |= value & 0x02; /* EHS */
+ break;
+
+ default:
+ printf("%s: unknown register %02x\n", __FUNCTION__, offset);
+ }
+}
+
+static uint8_t sdio_cccr_read(struct sdio_s *sd, uint32_t offset)
+{
+ switch (offset) {
+ case 0x00: /* CCCR/SDIO Revison */
+ return 0x32;
+
+ case 0x01: /* SD Specification Revision */
+ return 0x02; /* SD Physical Specification Version 2.00 (May 2006) */
+
+ case 0x02: /* I/O Enable */
+ return sd->cccr.io_enable;
+
+ case 0x03: /* I/O Ready */
+ return sd->cccr.io_enable;
+
+ case 0x04: /* Int Enable */
+ return sd->cccr.intr_enable;
+
+ case 0x05: /* Int Pending */
+ return sd->cccr.intr << 1;
+
+ case 0x07: /* Bus Interface Control */
+ return sd->cccr.bus;
+
+ case 0x08: /* Card Capability */
+ /* XXX: set SDC (01) when CMD52 learns to preserve current_cmd */
+ return 0x12 | sd->cccr.e4mi; /* SMB | S4MI | E4MI | Full-Speed */
+
+ case 0x09: /* Common CIS Pointer */
+ case 0x0a: /* Common CIS Pointer */
+ case 0x0b: /* Common CIS Pointer */
+ return 0x00; /* Starts at 0x1000 */
+
+ case 0x1c: /* Bus Suspend */
+ return 0x00;
+
+ case 0x1d: /* Function Select */
+ return sd->transfer.func;
+
+ case 0x0e: /* Exec Flags */
+ return 0x00;
+
+ case 0x0f: /* Ready Flags */
+ return 0x00;
+
+ case 0x10: /* Fn0 Block Size LSB */
+ return (sd->blk_len[0] >> 0) & 0xff;
+
+ case 0x11: /* Fn0 Block Size MSB */
+ return (sd->blk_len[0] >> 8) & 0xff;
+
+ case 0x12: /* Power Control */
+ return sd->cccr.power | 0x01;
+
+ case 0x13: /* High-Speed */
+ return sd->cccr.speed | 0x01;
+
+ default:
+ printf("%s: unknown register %02x\n", __FUNCTION__, offset);
+ return 0;
+ }
+}
+
+static void sdio_fbr_write(struct sdio_s *sd,
+ int fn, uint32_t offset, uint8_t value)
+{
+ switch (offset) {
+ default:
+ printf("%s: unknown register %02x\n", __FUNCTION__, offset);
+ }
+}
+
+static uint8_t sdio_fbr_read(struct sdio_s *sd,
+ int fn, uint32_t offset)
+{
+ switch (offset) {
+ default:
+ printf("%s: unknown register %02x\n", __FUNCTION__, offset);
+ return 0;
+ }
+}
+
+static void sdio_cia_write(struct sdio_s *sd,
+ uint32_t addr, uint8_t *data, int len)
+{
+ /* CCCR */
+ for (; len && addr < 0x100; len --, addr += sd->transfer.step)
+ sdio_cccr_write(sd, addr, *data ++);
+
+ /* FBR */
+ for (; len && addr < 0x800; len --, addr += sd->transfer.step)
+ sdio_fbr_write(sd, addr >> 8, addr & 0xff, *data ++);
+
+ if (len)
+ fprintf(stderr, "%s: bad write at %x (%i bytes)\n",
+ __FUNCTION__, addr, len);
+}
+
+static void sdio_cia_read(struct sdio_s *sd,
+ uint32_t addr, uint8_t *data, int len)
+{
+ int llen;
+
+ /* CCCR */
+ for (; len && addr < 0x100; len --, addr += sd->transfer.step)
+ *data ++ = sdio_cccr_read(sd, addr);
+
+ /* FBR */
+ for (; len && addr < 0x800; len --, addr += sd->transfer.step)
+ *data ++ = sdio_fbr_read(sd, addr >> 8, addr & 0xff);
+
+ /* RFU */
+ if (len && unlikely(addr < 0x1000)) {
+ llen = sd->transfer.step ? MIN(len, 0x1000 - addr) : len;
+ memset(data, 0, llen);
+ data += llen;
+ len -= llen;
+ addr = 0x1000;
+ }
+
+ /* CIS */
+ addr -= 0x1000;
+ if (len && addr < sd->cislen) {
+ llen = MIN(len, sd->cislen - addr);
+ memcpy(data, sd->cis + addr, llen);
+ data += llen;
+ len -= llen;
+ }
+
+ /* RFU */
+ if (len)
+ memset(data, 0, len);
+}
+
+static void sdio_dummy_write(struct sdio_s *sd,
+ uint32_t addr, uint8_t *data, int len)
+{
+ fprintf(stderr, "%s: writing %i bytes at %x\n", __FUNCTION__, len, addr);
+}
+
+static void sdio_dummy_read(struct sdio_s *sd,
+ uint32_t addr, uint8_t *data, int len)
+{
+ fprintf(stderr, "%s: reading %i bytes at %x\n", __FUNCTION__, len, addr);
+ memset(data, 0, sd->transfer.step ? len : 1);
+}
+
+static void sdio_set_irq(void *opaque, int line, int level)
+{
+ struct sdio_s *s = opaque;
+
+ if (level)
+ s->cccr.intr |= 1 << line;
+ else
+ s->cccr.intr &= ~(1 << line);
+
+ sdio_intr_update(s);
+}
+
+struct sd_card_s *sdio_init(struct sdio_s *s)
+{
+ int i;
+
+ for (i = 0; i < 8; i ++) {
+ s->write[i] = i ? sdio_dummy_write : sdio_cia_write;
+ s->read[i] = i ? sdio_dummy_read : sdio_cia_read;
+ }
+
+ s->func_irq = qemu_allocate_irqs(sdio_set_irq, s, 7);
+
+ s->card.opaque = s;
+ s->card.do_command = (void *) sdio_do_command;
+ s->card.write_data = (void *) sdio_write_data;
+ s->card.read_data = (void *) sdio_read_data;
+ s->card.data_ready = (void *) sdio_data_ready;
+
+ sdio_reset(s);
+
+ return &s->card;
+}
+
+struct ar6k_s {
+ struct sdio_s sd;
+ NICInfo *nd;
+};
+
+static void ar6k_set_ioocr(struct sdio_s *sd)
+{
+ /* 2.9 - 3.6 V, No memory present, Two functions only */
+ sd->ioocr = 0x10fe0000;
+}
+
+static void ar6k_reset(struct ar6k_s *s)
+{
+ ar6k_set_ioocr(&s->sd);
+}
+
+/* TODO: dump real values from an Atheros AR6001 - need hw access! */
+const static uint8_t ar6k_cis[] = {
+ CISTPL_MANFID, 4,
+ 0x0a, 0x01, /* SDIO Card manufacturer code */
+ 0x71, 0x02, /* Manufacturer information (Part No, Rev) */
+
+ CISTPL_FUNCID, 2,
+ 0x0c, /* Card funcion code: SDIO */
+ 0x00, /* System initialization mask */
+
+ CISTPL_END, 0xff,
+};
+
+struct sd_card_s *ar6k_init(NICInfo *nd)
+{
+ struct ar6k_s *s = (struct ar6k_s *) qemu_mallocz(sizeof(struct ar6k_s));
+ struct sd_card_s *ret = sdio_init(&s->sd);
+
+ s->nd = nd;
+ s->sd.reset = (void *) ar6k_reset;
+ s->sd.cis = ar6k_cis;
+ s->sd.cislen = sizeof(ar6k_cis);
+
+ ar6k_reset(s);
+
+ return ret;
+}
Modified: trunk/src/host/qemu-neo1973/hw/neo1973.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/neo1973.c 2007-12-22 02:30:17 UTC (rev 3725)
+++ trunk/src/host/qemu-neo1973/hw/neo1973.c 2007-12-22 03:01:31 UTC (rev 3726)
@@ -27,6 +27,12 @@
#define GTA01Bv4 1
+enum {
+ NEO1973_GTA01 = 1182,
+ NEO1973_GTA02 = 1304,
+ NEO1973_GTA02F = 1555,
+};
+
/* Wiring common to all revisions */
#define GTA01_GPIO_BACKLIGHT S3C_GPB(0)
#define GTA01_GPIO_GPS_PWRON S3C_GPB(1)
@@ -261,7 +267,9 @@
s3c_timers_cmp_handler_set(s->cpu->timers, 0, neo_bl_intensity, s);
- sd_set_cb(s->mmc, 0, s3c_gpio_in_get(s->cpu->io)[GTA01_IRQ_nSD_DETECT]);
+ if (s->id == NEO1973_GTA01)
+ sd_set_cb(s->mmc, 0,
+ s3c_gpio_in_get(s->cpu->io)[GTA01_IRQ_nSD_DETECT]);
}
/* PMB 2520 Hammerhead A-GPS chip */
@@ -524,7 +532,7 @@
sd = sd_init(drives_table[sd_idx].bdrv, 0);
neo1973_init_common(ram_size, ds,
- kernel_filename, cpu_model, sd, 1304);
+ kernel_filename, cpu_model, sd, NEO1973_GTA01);
}
static void gta02f_init(int ram_size, int vga_ram_size,
@@ -533,9 +541,10 @@
const char *initrd_filename, const char *cpu_model)
{
struct neo_board_s *neo;
+ struct sd_card_s *sd = ar6k_init(&nd_table[0]);
neo = neo1973_init_common(ram_size, ds,
- kernel_filename, cpu_model, 0, 1555);
+ kernel_filename, cpu_model, sd, NEO1973_GTA02F);
neo_gps_setup(neo);
neo_machid_init(neo);
Modified: trunk/src/host/qemu-neo1973/hw/pcmcia.h
===================================================================
--- trunk/src/host/qemu-neo1973/hw/pcmcia.h 2007-12-22 02:30:17 UTC (rev 3725)
+++ trunk/src/host/qemu-neo1973/hw/pcmcia.h 2007-12-22 03:01:31 UTC (rev 3726)
@@ -28,9 +28,12 @@
void (*io_write)(void *state, uint32_t address, uint16_t value);
};
+#define CISTPL_NULL 0x00 /* Null Tuple */
#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */
+#define CISTPL_CHECKSUM 0x10 /* Checksum Control Tuple */
#define CISTPL_NO_LINK 0x14 /* No Link Tuple */
#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */
+#define CISTPL_ALTSTR 0x16 /* The Alternate Language String */
#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */
#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */
#define CISTPL_CONFIG 0x1a /* Configuration Tuple */
@@ -39,12 +42,16 @@
#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */
#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */
#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */
-#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */
+#define CISTPL_MANFID 0x20 /* Manufacture ID String Tuple */
#define CISTPL_FUNCID 0x21 /* Function ID Tuple */
#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */
-#define CISTPL_END 0xff /* Tuple End */
+#define CISTPL_END 0xff /* End-of-chain Tuple */
#define CISTPL_ENDMARK 0xff
+/* SDIO Unique Tuples */
+#define CISTPL_SDIO_STD 0x91 /* Application Spec. Support Info */
+#define CISTPL_SDIO_EXT 0x92 /* SDIO Future-Use Reserved */
+
/* dscm1xxxx.c */
struct pcmcia_card_s *dscm1xxxx_init(BlockDriverState *bdrv);
Modified: trunk/src/host/qemu-neo1973/hw/sd.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/sd.c 2007-12-22 02:30:17 UTC (rev 3725)
+++ trunk/src/host/qemu-neo1973/hw/sd.c 2007-12-22 03:01:31 UTC (rev 3726)
@@ -149,39 +149,6 @@
7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8,
};
-static uint8_t sd_crc7(void *message, size_t width)
-{
- int i, bit;
- uint8_t shift_reg = 0x00;
- uint8_t *msg = (uint8_t *) message;
-
- for (i = 0; i < width; i ++, msg ++)
- for (bit = 7; bit >= 0; bit --) {
- shift_reg <<= 1;
- if ((shift_reg >> 7) ^ ((*msg >> bit) & 1))
- shift_reg ^= 0x89;
- }
-
- return shift_reg;
-}
-
-static uint16_t sd_crc16(void *message, size_t width)
-{
- int i, bit;
- uint16_t shift_reg = 0x0000;
- uint16_t *msg = (uint16_t *) message;
- width <<= 1;
-
- for (i = 0; i < width; i ++, msg ++)
- for (bit = 15; bit >= 0; bit --) {
- shift_reg <<= 1;
- if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
- shift_reg ^= 0x1011;
- }
-
- return shift_reg;
-}
-
static void sd_set_ocr(SDState *sd)
{
/* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
Modified: trunk/src/host/qemu-neo1973/hw/sd.h
===================================================================
--- trunk/src/host/qemu-neo1973/hw/sd.h 2007-12-22 02:30:17 UTC (rev 3725)
+++ trunk/src/host/qemu-neo1973/hw/sd.h 2007-12-22 03:01:31 UTC (rev 3726)
@@ -118,6 +118,39 @@
return sd->data_ready(sd->opaque);
}
+static inline uint8_t sd_crc7(void *message, size_t width)
+{
+ int i, bit;
+ uint8_t shift_reg = 0x00;
+ uint8_t *msg = (uint8_t *) message;
+
+ for (i = 0; i < width; i ++, msg ++)
+ for (bit = 7; bit >= 0; bit --) {
+ shift_reg <<= 1;
+ if ((shift_reg >> 7) ^ ((*msg >> bit) & 1))
+ shift_reg ^= 0x89;
+ }
+
+ return shift_reg;
+}
+
+static inline uint16_t sd_crc16(void *message, size_t width)
+{
+ int i, bit;
+ uint16_t shift_reg = 0x0000;
+ uint16_t *msg = (uint16_t *) message;
+ width <<= 1;
+
+ for (i = 0; i < width; i ++, msg ++)
+ for (bit = 15; bit >= 0; bit --) {
+ shift_reg <<= 1;
+ if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
+ shift_reg ^= 0x1011;
+ }
+
+ return shift_reg;
+}
+
/* sd.c */
struct sd_card_s *sd_init(BlockDriverState *bs, int is_spi);
void sd_set_cb(struct sd_card_s *sd, qemu_irq readonly, qemu_irq insert);
More information about the commitlog
mailing list