[U-Boot] [PATCH v2] mmc: add generic mmc spi driver
Thomas Chou
thomas at wytron.com.tw
Tue Apr 27 03:51:41 CEST 2010
This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.
The crc7 lib func is merged from linux and used to compute mmc
command checksum.
There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.
Signed-off-by: Thomas Chou <thomas at wytron.com.tw>
---
v2 add crc7, use cmd58 to read ocr, add subcommand mmc_spi.
common/Makefile | 1 +
common/cmd_mmc_spi.c | 92 ++++++++++++++
drivers/mmc/Makefile | 1 +
drivers/mmc/mmc_spi.c | 316 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/crc7.h | 14 ++
include/mmc_spi.h | 19 +++
lib/Makefile | 1 +
lib/crc7.c | 62 ++++++++++
8 files changed, 506 insertions(+), 0 deletions(-)
create mode 100644 common/cmd_mmc_spi.c
create mode 100644 drivers/mmc/mmc_spi.c
create mode 100644 include/linux/crc7.h
create mode 100644 include/mmc_spi.h
create mode 100644 lib/crc7.c
diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -118,6 +118,7 @@ COBJS-$(CONFIG_CMD_MII) += miiphyutil.o
COBJS-$(CONFIG_CMD_MII) += cmd_mii.o
COBJS-$(CONFIG_CMD_MISC) += cmd_misc.o
COBJS-$(CONFIG_CMD_MMC) += cmd_mmc.o
+COBJS-$(CONFIG_CMD_MMC_SPI) += cmd_mmc_spi.o
COBJS-$(CONFIG_MP) += cmd_mp.o
COBJS-$(CONFIG_CMD_MTDPARTS) += cmd_mtdparts.o
COBJS-$(CONFIG_CMD_NAND) += cmd_nand.o
diff --git a/common/cmd_mmc_spi.c b/common/cmd_mmc_spi.c
new file mode 100644
index 0000000..578d7a7
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,92 @@
+/*
+ * Command for mmc_spi setup.
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas at wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_spi.h>
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int dev_num;
+ uint bus;
+ uint cs;
+ uint speed;
+ uint mode;
+ char *endp;
+ struct mmc *mmc = NULL;
+ struct mmc_spi_priv *priv;
+
+ if (argc < 2)
+ goto usage;
+
+ dev_num = simple_strtoul(argv[1], &endp, 0);
+ if (*endp != 0)
+ goto usage;
+ mmc = find_mmc_device(dev_num);
+ if (!mmc || strcmp(mmc->name, "MMC_SPI"))
+ goto usage;
+ priv = mmc->priv;
+ bus = priv->bus;
+ cs = priv->cs;
+ speed = priv->speed;
+ mode = priv->mode;
+
+ if (argc < 3)
+ goto info;
+
+ cs = simple_strtoul(argv[2], &endp, 0);
+ if (*argv[2] == 0 || (*endp != 0 && *endp != ':'))
+ goto usage;
+ if (*endp == ':') {
+ if (endp[1] == 0)
+ goto usage;
+ bus = cs;
+ cs = simple_strtoul(endp + 1, &endp, 0);
+ if (*endp != 0)
+ goto usage;
+ }
+
+ if (argc >= 4) {
+ speed = simple_strtoul(argv[3], &endp, 0);
+ if (*argv[3] == 0 || *endp != 0)
+ goto usage;
+ }
+ if (argc >= 5) {
+ mode = simple_strtoul(argv[4], &endp, 16);
+ if (*argv[4] == 0 || *endp != 0)
+ goto usage;
+ }
+
+ if (bus != priv->bus || cs != priv->cs ||
+ speed != priv->speed || mode != priv->speed) {
+ priv->bus = bus;
+ priv->cs = cs;
+ priv->speed = speed;
+ priv->mode = mode;
+ if (priv->slave) {
+ free(priv->slave);
+ priv->slave = NULL;
+ }
+ }
+info:
+ printf("%s:%d at %u:%u %u %u\n", mmc->name, dev_num,
+ bus, cs, speed, mode);
+ return 0;
+
+usage:
+ cmd_usage(cmdtp);
+ return 1;
+}
+
+U_BOOT_CMD(
+ mmc_spi, 5, 0, do_mmc_spi,
+ "mmc_spi setup",
+ "dev_num [bus:][cs] [hz] [mode] - setup mmc_spi device on given\n"
+ " SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 1b8f5bd..d03eb47 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -31,6 +31,7 @@ LIB := $(obj)libmmc.a
COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..3ff171f
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,316 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas at wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <part.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_spi.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ u8 cmdo[7];
+ u8 r1;
+ int i;
+ cmdo[0] = 0xff;
+ cmdo[1] = 0x40 + cmdidx;
+ cmdo[2] = cmdarg >> 24;
+ cmdo[3] = cmdarg >> 16;
+ cmdo[4] = cmdarg >> 8;
+ cmdo[5] = cmdarg;
+ cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
+ spi_xfer(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+ for (i = 0; i < CTOUT; i++) {
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if ((r1 & 0x80) == 0)
+ break;
+ }
+ debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+ return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+ u32 bcnt, u32 bsize)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ u8 r1;
+ u8 crc[2];
+ int i;
+ while (bcnt--) {
+ for (i = 0; i < RTOUT; i++) {
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if (r1 != 0xff)
+ break;
+ }
+ debug("%s:tok%d %x\n", __func__, i, r1);
+ if (r1 == 0xfe) {
+ spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+ buf += bsize;
+ spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+ r1 = 0;
+ } else
+ break;
+ }
+ return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+ u32 bcnt, u32 bsize)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ u8 r1;
+ u8 tok[2] = { 0xff, 0xfe };
+ u8 crc[2];
+ int i;
+ while (bcnt--) {
+ spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+ spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+ buf += bsize;
+ spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0x05) {
+ for (i = 0; i < WTOUT; i++) {
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ debug("%s:wtout %x\n", __func__, r1);
+ r1 = 0x04;
+ break;
+ }
+ } else
+ break;
+ }
+ return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+ u32 bcnt, u32 bsize)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ u8 r1;
+ u8 tok[2] = { 0xff, 0xfc };
+ u8 stop[2] = { 0xff, 0xfd };
+ u8 crc[2];
+ int i;
+ while (bcnt--) {
+ spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+ spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+ buf += bsize;
+ spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0x05) {
+ for (i = 0; i < WTOUT; i++) {
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ debug("%s:wtout %x\n", __func__, r1);
+ r1 = 0x04;
+ break;
+ }
+ }
+ }
+ if (bcnt == 0 && r1 == 0) {
+ spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+ for (i = 0; i < WTOUT; i++) {
+ spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ debug("%s:wtout %x\n", __func__, r1);
+ r1 = 0x04;
+ }
+ }
+ return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+ spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ u8 r1;
+ ushort cmdidx = cmd->cmdidx;
+ uint cmdarg = cmd->cmdarg;
+ u8 resp[16];
+ int i;
+ int ret = 0;
+ debug("%s:cmd%d %x %x %x\n", __func__,
+ cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+ if (cmdidx == MMC_CMD_ALL_SEND_CID)
+ cmdidx = MMC_CMD_SEND_CID;
+ r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+ if (r1 == 0xff) {
+ ret = NO_CARD_ERR;
+ goto done;
+ } else if (cmd->resp_type == MMC_RSP_R2) {
+ r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+ for (i = 0; i < 4; i++)
+ cmd->response[i] = rswab(resp + i * 4);
+ debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+ cmd->response[2], cmd->response[3]);
+ } else {
+ if (!data) {
+ spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+ cmd->response[0] = rswab(resp);
+ debug("r32 %x\n", cmd->response[0]);
+ }
+ if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+ mmc_spi_deactivate(mmc);
+ r1 = mmc_spi_sendcmd(mmc, 58, 0);
+ spi_xfer(priv->slave, 4 * 8,
+ NULL, resp, 0);
+ priv->ocr58 = rswab(resp) & ~OCR_BUSY;
+ debug("ocr58 %x\n", priv->ocr58);
+ } else if (cmdidx == SD_CMD_SEND_IF_COND) {
+ if ((r1 & 0x7e) == 0)
+ priv->sd_if_cond = 1;
+ } else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+ cmdidx == MMC_CMD_SEND_OP_COND) {
+ if (r1 & 0x04)
+ ret = TIMEOUT;
+ else {
+ if (r1 & 0x01)
+ cmd->response[0] = priv->ocr58;
+ else {
+ mmc_spi_deactivate(mmc);
+ r1 = mmc_spi_sendcmd(mmc, 58, 0);
+ spi_xfer(priv->slave, 4 * 8,
+ NULL, resp, 0);
+ priv->ocr58 = rswab(resp);
+ debug("ocr58 %x\n", priv->ocr58);
+ cmd->response[0] = priv->ocr58;
+ }
+ }
+ } else {
+ cmd->response[0] = 0;
+ if (r1 & 0x7e)
+ cmd->response[0] |= (1 << 19);
+ if (r1 & 0x40)
+ cmd->response[0] |= (1 << 31);
+ if (r1 & 0x20)
+ cmd->response[0] |= (1 << 30);
+ if (r1 & 0x10)
+ cmd->response[0] |= (1 << 28);
+ if (r1 & 0x08)
+ cmd->response[0] |= (1 << 23);
+ if (r1 & 0x04)
+ cmd->response[0] |= (1 << 22);
+ if (r1 & 0x02)
+ cmd->response[0] |= (1 << 13);
+ }
+ }
+ if (data) {
+ debug("%s:data %x %x %x\n", __func__,
+ data->flags, data->blocks, data->blocksize);
+ if (data->flags == MMC_DATA_READ) {
+ r1 = mmc_spi_readdata(mmc, data->dest,
+ data->blocks, data->blocksize);
+ } else if (data->flags == MMC_DATA_WRITE) {
+ if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+ r1 = mmc_spi_writeblock(mmc, data->src,
+ data->blocks, data->blocksize);
+ else
+ r1 = mmc_spi_writedata(mmc, data->src,
+ data->blocks, data->blocksize);
+ }
+ }
+done:
+ mmc_spi_deactivate(mmc);
+ return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+ debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+ struct mmc_spi_priv *priv = mmc->priv;
+ debug("%s:\n", __func__);
+ if (!priv->slave) {
+ debug("%s: setup %u:%u %u %u\n", __func__,
+ priv->bus, priv->cs,
+ priv->speed, priv->mode);
+ priv->slave = spi_setup_slave(priv->bus, priv->cs,
+ priv->speed, priv->mode);
+ if (!priv->slave)
+ return -ENOMEM;
+ }
+ priv->sd_if_cond = 0;
+ priv->ocr58 = 0;
+ spi_claim_bus(priv->slave);
+ /* power on initialization */
+ spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+ SPI_XFER_BEGIN | SPI_XFER_END);
+ spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+ return 0;
+}
+
+int mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+ struct mmc *mmc;
+ struct mmc_spi_priv *priv;
+
+ mmc = malloc(sizeof(*mmc));
+ if (!mmc)
+ return -ENOMEM;
+ priv = malloc(sizeof(*priv));
+ if (!priv) {
+ free(mmc);
+ return -ENOMEM;
+ }
+ mmc->priv = priv;
+ priv->bus = bus;
+ priv->cs = cs;
+ priv->speed = speed;
+ priv->mode = mode;
+ sprintf(mmc->name, "MMC_SPI");
+ mmc->send_cmd = mmc_spi_request;
+ mmc->set_ios = mmc_spi_set_ios;
+ mmc->init = mmc_spi_init_p;
+ mmc->host_caps = 0;
+
+ mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->f_max = speed;
+ mmc->f_min = mmc->f_max >> 9;
+ mmc->block_dev.part_type = PART_TYPE_DOS;
+
+ mmc_register(mmc);
+
+ return 0;
+}
diff --git a/include/linux/crc7.h b/include/linux/crc7.h
new file mode 100644
index 0000000..1786e77
--- /dev/null
+++ b/include/linux/crc7.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_CRC7_H
+#define _LINUX_CRC7_H
+#include <linux/types.h>
+
+extern const u8 crc7_syndrome_table[256];
+
+static inline u8 crc7_byte(u8 crc, u8 data)
+{
+ return crc7_syndrome_table[(crc << 1) ^ data];
+}
+
+extern u8 crc7(u8 crc, const u8 *buffer, size_t len);
+
+#endif
diff --git a/include/mmc_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..66aa6aa
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,19 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas at wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+struct mmc_spi_priv {
+ struct spi_slave *slave;
+ uint bus;
+ uint cs;
+ uint speed;
+ uint mode;
+ uint ocr58;
+ uint sd_if_cond:1;
+};
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index c45f07c..aef2893 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,6 +32,7 @@ COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o
COBJS-$(CONFIG_BZIP2) += bzlib_randtable.o
COBJS-$(CONFIG_BZIP2) += bzlib_huffman.o
COBJS-$(CONFIG_USB_TTY) += circbuf.o
+COBJS-y += crc7.o
COBJS-y += crc16.o
COBJS-y += crc32.o
COBJS-y += ctype.o
diff --git a/lib/crc7.c b/lib/crc7.c
new file mode 100644
index 0000000..e635c9c
--- /dev/null
+++ b/lib/crc7.c
@@ -0,0 +1,62 @@
+/*
+ * crc7.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/crc7.h>
+
+
+/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */
+const u8 crc7_syndrome_table[256] = {
+ 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
+ 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
+ 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
+ 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
+ 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
+ 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
+ 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
+ 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
+ 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
+ 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
+ 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
+ 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
+ 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
+ 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
+ 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
+ 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
+ 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
+ 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
+ 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
+ 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
+ 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
+ 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
+ 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
+ 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
+ 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
+ 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
+ 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
+ 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
+ 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
+ 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
+ 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
+ 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
+};
+
+/**
+ * crc7 - update the CRC7 for the data buffer
+ * @crc: previous CRC7 value
+ * @buffer: data pointer
+ * @len: number of bytes in the buffer
+ * Context: any
+ *
+ * Returns the updated CRC7 value.
+ */
+u8 crc7(u8 crc, const u8 *buffer, size_t len)
+{
+ while (len--)
+ crc = crc7_byte(crc, *buffer++);
+ return crc;
+}
--
1.6.6.1
More information about the U-Boot
mailing list