Commit da7fbe58 authored by Pierre Ossman's avatar Pierre Ossman

mmc: Separate out protocol ops

Move protocol operations and definitions into their own files
in an effort to separate protocol handling and bus
arbitration more clearly.
Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent aaac1b47
......@@ -32,8 +32,8 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <asm/system.h>
#include <asm/uaccess.h>
......
......@@ -7,5 +7,5 @@ ifeq ($(CONFIG_MMC_DEBUG),y)
endif
obj-$(CONFIG_MMC) += mmc_core.o
mmc_core-y := core.o sysfs.o
mmc_core-y := core.o sysfs.o mmc_ops.o sd_ops.o
......@@ -23,9 +23,14 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include "core.h"
#include "sysfs.h"
#include "mmc_ops.h"
#include "sd_ops.h"
#define CMD_RETRIES 3
......@@ -190,80 +195,6 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
EXPORT_SYMBOL(mmc_wait_for_cmd);
/**
* mmc_wait_for_app_cmd - start an application command and wait for
completion
* @host: MMC host to start command
* @rca: RCA to send MMC_APP_CMD to
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Sends a MMC_APP_CMD, checks the card response, sends the command
* in the parameter and waits for it to complete. Return any error
* that occurred while the command was executing. Do not attempt to
* parse the response.
*/
int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca,
struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
struct mmc_command appcmd;
int i, err;
BUG_ON(!host->claimed);
BUG_ON(retries < 0);
err = MMC_ERR_INVALID;
/*
* We have to resend MMC_APP_CMD for each attempt so
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
memset(&mrq, 0, sizeof(struct mmc_request));
appcmd.opcode = MMC_APP_CMD;
appcmd.arg = rca << 16;
appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
appcmd.retries = 0;
memset(appcmd.resp, 0, sizeof(appcmd.resp));
appcmd.data = NULL;
mrq.cmd = &appcmd;
appcmd.data = NULL;
mmc_wait_for_req(host, &mrq);
if (appcmd.error) {
err = appcmd.error;
continue;
}
/* Check that card supported application commands */
if (!(appcmd.resp[0] & R1_APP_CMD))
return MMC_ERR_FAILED;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = 0;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
err = cmd->error;
if (cmd->error == MMC_ERR_NONE)
break;
}
return err;
}
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
/**
* mmc_set_data_timeout - set the timeout for a data command
* @data: data phase for command
......@@ -385,60 +316,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
host->ops->set_ios(host, ios);
}
static int mmc_select_card(struct mmc_card *card)
void mmc_set_chip_select(struct mmc_host *host, int mode)
{
int err;
struct mmc_command cmd;
BUG_ON(!card->host->claimed);
cmd.opcode = MMC_SELECT_CARD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
/*
* We can only change the bus width of SD cards when
* they are selected so we have to put the handling
* here.
*
* The card is in 1 bit mode by default so
* we only need to change if it supports the
* wider version.
*/
if (mmc_card_sd(card) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) &&
(card->host->caps & MMC_CAP_4_BIT_DATA)) {
struct mmc_command cmd;
cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.arg = SD_BUS_WIDTH_4;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_app_cmd(card->host, card->rca,
&cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
card->host->ios.bus_width = MMC_BUS_WIDTH_4;
mmc_set_ios(card->host);
}
return MMC_ERR_NONE;
}
static inline void mmc_delay(unsigned int ms)
{
if (ms < 1000 / HZ) {
cond_resched();
mdelay(ms);
} else {
msleep(ms);
}
host->ios.chip_select = mode;
mmc_set_ios(host);
}
/*
......@@ -708,32 +589,6 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid)
return card;
}
/*
* Tell attached cards to go to IDLE state
*/
static void mmc_idle_cards(struct mmc_host *host)
{
struct mmc_command cmd;
host->ios.chip_select = MMC_CS_HIGH;
mmc_set_ios(host);
mmc_delay(1);
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1);
host->ios.chip_select = MMC_CS_DONTCARE;
mmc_set_ios(host);
mmc_delay(1);
}
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
......@@ -778,97 +633,6 @@ static void mmc_power_off(struct mmc_host *host)
mmc_set_ios(host);
}
static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
cmd.opcode = SD_APP_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2)
{
struct mmc_command cmd;
int err, sd2;
static const u8 test_pattern = 0xAA;
/*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
* before SD_APP_OP_COND. This command will harmlessly fail for
* SD 1.0 cards.
*/
cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err == MMC_ERR_NONE) {
if ((cmd.resp[0] & 0xFF) == test_pattern) {
sd2 = 1;
} else {
sd2 = 0;
err = MMC_ERR_FAILED;
}
} else {
/*
* Treat errors as SD 1.0 card.
*/
sd2 = 0;
err = MMC_ERR_NONE;
}
if (rsd2)
*rsd2 = sd2;
return err;
}
/*
* Discover the card by requesting its CID.
*
......@@ -878,27 +642,18 @@ static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2)
static void mmc_discover_card(struct mmc_host *host)
{
unsigned int err;
struct mmc_command cmd;
u32 cid[4];
BUG_ON(host->card);
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err == MMC_ERR_TIMEOUT) {
err = MMC_ERR_NONE;
return;
}
err = mmc_all_send_cid(host, cid);
if (err != MMC_ERR_NONE) {
printk(KERN_ERR "%s: error requesting CID: %d\n",
mmc_hostname(host), err);
return;
}
host->card = mmc_alloc_card(host, cmd.resp);
host->card = mmc_alloc_card(host, cid);
if (IS_ERR(host->card)) {
err = PTR_ERR(host->card);
host->card = NULL;
......@@ -908,16 +663,10 @@ static void mmc_discover_card(struct mmc_host *host)
if (host->mode == MMC_MODE_SD) {
host->card->type = MMC_TYPE_SD;
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
err = mmc_send_relative_addr(host, &host->card->rca);
if (err != MMC_ERR_NONE)
mmc_card_set_dead(host->card);
else {
host->card->rca = cmd.resp[0] >> 16;
if (!host->ops->get_ro) {
printk(KERN_WARNING "%s: host does not "
"support reading read-only "
......@@ -932,11 +681,7 @@ static void mmc_discover_card(struct mmc_host *host)
host->card->type = MMC_TYPE_MMC;
host->card->rca = 1;
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = host->card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
err = mmc_set_relative_addr(host->card);
if (err != MMC_ERR_NONE)
mmc_card_set_dead(host->card);
}
......@@ -944,7 +689,6 @@ static void mmc_discover_card(struct mmc_host *host)
static void mmc_read_csd(struct mmc_host *host)
{
struct mmc_command cmd;
int err;
if (!host->card)
......@@ -952,18 +696,12 @@ static void mmc_read_csd(struct mmc_host *host)
if (mmc_card_dead(host->card))
return;
cmd.opcode = MMC_SEND_CSD;
cmd.arg = host->card->rca << 16;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
err = mmc_send_csd(host->card, host->card->raw_csd);
if (err != MMC_ERR_NONE) {
mmc_card_set_dead(host->card);
return;
}
memcpy(host->card->raw_csd, cmd.resp, sizeof(host->card->raw_csd));
mmc_decode_csd(host->card);
mmc_decode_cid(host->card);
}
......@@ -971,13 +709,7 @@ static void mmc_read_csd(struct mmc_host *host)
static void mmc_process_ext_csd(struct mmc_host *host)
{
int err;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
u8 *ext_csd;
struct scatterlist sg;
if (!host->card)
return;
......@@ -1000,32 +732,8 @@ static void mmc_process_ext_csd(struct mmc_host *host)
return;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, host->card, 0);
data.blksz = 512;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, ext_csd, 512);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
err = mmc_send_ext_csd(host->card, ext_csd);
if (err != MMC_ERR_NONE) {
if (host->card->csd.capacity == (4096 * 512)) {
printk(KERN_ERR "%s: unable to read EXT_CSD "
"on a possible high capacity card. "
......@@ -1066,14 +774,8 @@ static void mmc_process_ext_csd(struct mmc_host *host)
if (host->caps & MMC_CAP_MMC_HIGHSPEED) {
/* Activate highspeed support. */
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_HS_TIMING << 16) |
(1 << 8) |
EXT_CSD_CMD_SET_NORMAL;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
err = mmc_switch(host->card, MMC_SWITCH_MODE_WRITE_BYTE,
EXT_CSD_HS_TIMING, 1);
if (err != MMC_ERR_NONE) {
printk("%s: failed to switch card to mmc v4 "
"high-speed mode.\n",
......@@ -1090,14 +792,9 @@ static void mmc_process_ext_csd(struct mmc_host *host)
/* Check for host support for wide-bus modes. */
if (host->caps & MMC_CAP_4_BIT_DATA) {
/* Activate 4-bit support. */
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_BUS_WIDTH << 16) |
(EXT_CSD_BUS_WIDTH_4 << 8) |
EXT_CSD_CMD_SET_NORMAL;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
err = mmc_switch(host->card, MMC_SWITCH_MODE_WRITE_BYTE,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4 |
EXT_CSD_CMD_SET_NORMAL);
if (err != MMC_ERR_NONE) {
printk("%s: failed to switch card to "
"mmc v4 4-bit bus mode.\n",
......@@ -1116,10 +813,6 @@ static void mmc_process_ext_csd(struct mmc_host *host)
static void mmc_read_scr(struct mmc_host *host)
{
int err;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
if (!host->card)
return;
......@@ -1128,61 +821,19 @@ static void mmc_read_scr(struct mmc_host *host)
if (!mmc_card_sd(host->card))
return;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_APP_CMD;
cmd.arg = host->card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) {
mmc_card_set_dead(host->card);
return;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, host->card, 0);
data.blksz = 1 << 3;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, (u8*)host->card->raw_scr, 8);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
err = mmc_app_send_scr(host->card, host->card->raw_scr);
if (err != MMC_ERR_NONE) {
mmc_card_set_dead(host->card);
return;
}
host->card->raw_scr[0] = ntohl(host->card->raw_scr[0]);
host->card->raw_scr[1] = ntohl(host->card->raw_scr[1]);
mmc_decode_scr(host->card);
}
static void mmc_read_switch_caps(struct mmc_host *host)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
int err;
unsigned char *status;
struct scatterlist sg;
if (!(host->caps & MMC_CAP_SD_HIGHSPEED))
return;
......@@ -1204,32 +855,9 @@ static void mmc_read_switch_caps(struct mmc_host *host)
return;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SWITCH;
cmd.arg = 0x00FFFFF1;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, host->card, 0);
data.blksz = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, status, 64);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
err = mmc_sd_switch(host->card, SD_SWITCH_CHECK,
SD_SWITCH_GRP_ACCESS, SD_SWITCH_ACCESS_HS, status);
if (err != MMC_ERR_NONE) {
printk("%s: unable to read switch capabilities, "
"performance might suffer.\n",
mmc_hostname(host));
......@@ -1239,33 +867,9 @@ static void mmc_read_switch_caps(struct mmc_host *host)
if (status[13] & 0x02)
host->card->sw_caps.hs_max_dtr = 50000000;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SWITCH;
cmd.arg = 0x80FFFFF1;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, host->card, 0);
data.blksz = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, status, 64);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE ||
(status[16] & 0xF) != 1) {
err = mmc_sd_switch(host->card, SD_SWITCH_SET,
SD_SWITCH_GRP_ACCESS, SD_SWITCH_ACCESS_HS, status);
if (err != MMC_ERR_NONE || (status[16] & 0xF) != 1) {
printk(KERN_WARNING "%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(host));
......@@ -1314,16 +918,11 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host)
*/
static void mmc_check_card(struct mmc_card *card)
{
struct mmc_command cmd;
int err;
BUG_ON(!card);
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES);
err = mmc_send_status(card, NULL);
if (err == MMC_ERR_NONE)
return;
......@@ -1338,9 +937,9 @@ static void mmc_setup(struct mmc_host *host)
host->mode = MMC_MODE_SD;
mmc_power_up(host);
mmc_idle_cards(host);
mmc_go_idle(host);
err = mmc_send_if_cond(host, host->ocr_avail, NULL);
err = mmc_send_if_cond(host, host->ocr_avail);
if (err != MMC_ERR_NONE) {
return;
}
......@@ -1369,7 +968,7 @@ static void mmc_setup(struct mmc_host *host)
* state. We wait 1ms to give cards time to
* respond.
*/
mmc_idle_cards(host);
mmc_go_idle(host);
/*
* Send the selected OCR multiple times... until the cards
......@@ -1377,17 +976,17 @@ static void mmc_setup(struct mmc_host *host)
* (My SanDisk card seems to need this.)
*/
if (host->mode == MMC_MODE_SD) {
int err, sd2;
err = mmc_send_if_cond(host, host->ocr, &sd2);
if (err == MMC_ERR_NONE) {
/*
* If SD_SEND_IF_COND indicates an SD 2.0
* compliant card and we should set bit 30
* of the ocr to indicate that we can handle
* block-addressed SDHC cards.
*/
mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL);
}
err = mmc_send_if_cond(host, host->ocr);
if (err == MMC_ERR_NONE)
ocr = host->ocr | (1 << 30);
mmc_send_app_op_cond(host, ocr, NULL);
} else {
/* The extra bit indicates that we support high capacity */
mmc_send_op_cond(host, host->ocr | (1 << 30), NULL);
......@@ -1409,6 +1008,24 @@ static void mmc_setup(struct mmc_host *host)
mmc_card_set_dead(host->card);
}
/*
* The card is in 1 bit mode by default so
* we only need to change if it supports the
* wider version.
*/
if (host->card && !mmc_card_dead(host->card) &&
mmc_card_sd(host->card) &&
(host->card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) &&
(host->card->host->caps & MMC_CAP_4_BIT_DATA)) {
err = mmc_app_set_bus_width(host->card, SD_BUS_WIDTH_4);
if (err != MMC_ERR_NONE)
mmc_card_set_dead(host->card);
else {
host->ios.bus_width = MMC_BUS_WIDTH_4;
mmc_set_ios(host);
}
}
if (host->mode == MMC_MODE_SD) {
mmc_read_scr(host);
mmc_read_switch_caps(host);
......
......@@ -2,24 +2,30 @@
* linux/drivers/mmc/core/core.h
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _MMC_CORE_H
#define _MMC_CORE_H
/* core-internal functions */
void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
int mmc_register_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card);
#ifndef _MMC_CORE_CORE_H
#define _MMC_CORE_CORE_H
struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev);
int mmc_add_host_sysfs(struct mmc_host *host);
void mmc_remove_host_sysfs(struct mmc_host *host);
void mmc_free_host_sysfs(struct mmc_host *host);
#include <linux/delay.h>
#define MMC_CMD_RETRIES 3
void mmc_set_chip_select(struct mmc_host *host, int mode);
static inline void mmc_delay(unsigned int ms)
{
if (ms < 1000 / HZ) {
cond_resched();
mdelay(ms);
} else {
msleep(ms);
}
}
int mmc_schedule_work(struct work_struct *work);
int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay);
void mmc_flush_scheduled_work(void);
#endif
/*
* linux/drivers/mmc/mmc_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* 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.
*/
#include <linux/types.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include "core.h"
#include "mmc_ops.h"
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SELECT_CARD;
if (card) {
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
}
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_select_card(struct mmc_card *card)
{
BUG_ON(!card);
return _mmc_select_card(card->host, card);
}
int mmc_deselect_cards(struct mmc_host *host)
{
return _mmc_select_card(host, NULL);
}
int mmc_go_idle(struct mmc_host *host)
{
int err;
struct mmc_command cmd;
mmc_set_chip_select(host, MMC_CS_HIGH);
mmc_delay(1);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
err = mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1);
mmc_set_chip_select(host, MMC_CS_DONTCARE);
mmc_delay(1);
return err;
}
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(!cid);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
memcpy(cid, cmd.resp, sizeof(u32) * 4);
return MMC_ERR_NONE;
}
int mmc_set_relative_addr(struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
BUG_ON(!csd);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_CSD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
memcpy(csd, cmd.resp, sizeof(u32) * 4);
return MMC_ERR_NONE;
}
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(!card->host);
BUG_ON(!ext_csd);
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 512;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, ext_csd, 512);
mmc_set_data_timeout(&data, card, 0);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE)
return cmd.error;
if (data.error != MMC_ERR_NONE)
return data.error;
return MMC_ERR_NONE;
}
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) |
(value << 8) |
set;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
if (status)
*status = cmd.resp[0];
return MMC_ERR_NONE;
}
/*
* linux/drivers/mmc/mmc_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* 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.
*/
#ifndef _MMC_MMC_OPS_H
#define _MMC_MMC_OPS_H
int mmc_select_card(struct mmc_card *card);
int mmc_deselect_cards(struct mmc_host *host);
int mmc_go_idle(struct mmc_host *host);
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
int mmc_set_relative_addr(struct mmc_card *card);
int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status);
#endif
/*
* linux/drivers/mmc/sd_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* 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.
*/
#include <linux/types.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include "core.h"
#include "sd_ops.h"
/**
* mmc_wait_for_app_cmd - start an application command and wait for
completion
* @host: MMC host to start command
* @rca: RCA to send MMC_APP_CMD to
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Sends a MMC_APP_CMD, checks the card response, sends the command
* in the parameter and waits for it to complete. Return any error
* that occurred while the command was executing. Do not attempt to
* parse the response.
*/
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
int i, err;
BUG_ON(!cmd);
BUG_ON(retries < 0);
err = MMC_ERR_INVALID;
/*
* We have to resend MMC_APP_CMD for each attempt so
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
memset(&mrq, 0, sizeof(struct mmc_request));
err = mmc_app_cmd(host, card);
if (err != MMC_ERR_NONE)
continue;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = 0;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
err = cmd->error;
if (cmd->error == MMC_ERR_NONE)
break;
}
return err;
}
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(card && (card->host != host));
cmd.opcode = MMC_APP_CMD;
if (card) {
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
}
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
return err;
/* Check that card supported application commands */
if (!(cmd.resp[0] & R1_APP_CMD))
return MMC_ERR_FAILED;
return MMC_ERR_NONE;
}
int mmc_app_set_bus_width(struct mmc_card *card, int width)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
switch (width) {
case MMC_BUS_WIDTH_1:
cmd.arg = SD_BUS_WIDTH_1;
break;
case MMC_BUS_WIDTH_4:
cmd.arg = SD_BUS_WIDTH_4;
break;
default:
return MMC_ERR_INVALID;
}
err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
struct mmc_command cmd;
int err;
static const u8 test_pattern = 0xAA;
/*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
* before SD_APP_OP_COND. This command will harmlessly fail for
* SD 1.0 cards.
*/
cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
return err;
if ((cmd.resp[0] & 0xFF) != test_pattern)
return MMC_ERR_FAILED;
return MMC_ERR_NONE;
}
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(!rca);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
*rca = cmd.resp[0] >> 16;
return MMC_ERR_NONE;
}
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
{
int err;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(!card->host);
BUG_ON(!scr);
err = mmc_app_cmd(card->host, card);
if (err != MMC_ERR_NONE)
return err;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 8;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, scr, 8);
mmc_set_data_timeout(&data, card, 0);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE)
return cmd.error;
if (data.error != MMC_ERR_NONE)
return data.error;
scr[0] = ntohl(scr[0]);
scr[1] = ntohl(scr[1]);
return MMC_ERR_NONE;
}
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(!card->host);
mode = !!mode;
value &= 0xF;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = SD_SWITCH;
cmd.arg = mode << 31 | 0x00FFFFFF;
cmd.arg &= ~(0xF << (group * 4));
cmd.arg |= value << (group * 4);
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, resp, 64);
mmc_set_data_timeout(&data, card, 0);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE)
return cmd.error;
if (data.error != MMC_ERR_NONE)
return data.error;
return MMC_ERR_NONE;
}
/*
* linux/drivers/mmc/sd_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* 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.
*/
#ifndef _MMC_SD_OPS_H
#define _MMC_SD_OPS_H
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
int mmc_app_set_bus_width(struct mmc_card *card, int width);
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
#endif
......@@ -18,7 +18,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "core.h"
#include "sysfs.h"
#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
......
/*
* linux/drivers/mmc/core/sysfs.h
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _MMC_CORE_SYSFS_H
#define _MMC_CORE_SYSFS_H
void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
int mmc_register_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card);
struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev);
int mmc_add_host_sysfs(struct mmc_host *host);
void mmc_remove_host_sysfs(struct mmc_host *host);
void mmc_free_host_sysfs(struct mmc_host *host);
int mmc_schedule_work(struct work_struct *work);
int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay);
void mmc_flush_scheduled_work(void);
#endif
......@@ -101,7 +101,7 @@ struct mmc_card;
extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int,
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int);
......
......@@ -22,8 +22,8 @@
* 15 May 2002
*/
#ifndef MMC_MMC_PROTOCOL_H
#define MMC_MMC_PROTOCOL_H
#ifndef MMC_MMC_H
#define MMC_MMC_H
/* Standard MMC commands (4.1) type argument response */
/* class 1 */
......@@ -78,21 +78,6 @@
#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
/* SD commands type argument response */
/* class 0 */
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
/* class 10 */
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
/* Application commands */
#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */
/*
* MMC_SWITCH argument format:
*
......@@ -104,27 +89,6 @@
* [02:00] Command Set
*/
/*
* SD_SWITCH argument format:
*
* [31] Check (0) or switch (1)
* [30:24] Reserved (0)
* [23:20] Function group 6
* [19:16] Function group 5
* [15:12] Function group 4
* [11:8] Function group 3
* [7:4] Function group 2
* [3:0] Function group 1
*/
/*
* SD_SEND_IF_COND argument format:
*
* [31:12] Reserved (0)
* [11:8] Host Voltage Supply Flags
* [7:0] Check Pattern (0xAA)
*/
/*
MMC status in R1
Type
......@@ -289,19 +253,5 @@ struct _mmc_csd {
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
/*
* SCR field definitions
*/
#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
/*
* SD bus widths
*/
#define SD_BUS_WIDTH_1 0
#define SD_BUS_WIDTH_4 2
#endif /* MMC_MMC_PROTOCOL_H */
/*
* include/linux/mmc/sd.h
*
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
*
* 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.
*/
#ifndef MMC_SD_H
#define MMC_SD_H
/* SD commands type argument response */
/* class 0 */
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
/* class 10 */
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
/* Application commands */
#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */
/*
* SD_SWITCH argument format:
*
* [31] Check (0) or switch (1)
* [30:24] Reserved (0)
* [23:20] Function group 6
* [19:16] Function group 5
* [15:12] Function group 4
* [11:8] Function group 3
* [7:4] Function group 2
* [3:0] Function group 1
*/
/*
* SD_SEND_IF_COND argument format:
*
* [31:12] Reserved (0)
* [11:8] Host Voltage Supply Flags
* [7:0] Check Pattern (0xAA)
*/
/*
* SCR field definitions
*/
#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
/*
* SD bus widths
*/
#define SD_BUS_WIDTH_1 0
#define SD_BUS_WIDTH_4 2
/*
* SD_SWITCH mode
*/
#define SD_SWITCH_CHECK 0
#define SD_SWITCH_SET 1
/*
* SD_SWITCH function groups
*/
#define SD_SWITCH_GRP_ACCESS 0
/*
* SD_SWITCH access modes
*/
#define SD_SWITCH_ACCESS_DEF 0
#define SD_SWITCH_ACCESS_HS 1
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment