[U-Boot] [PATCH v4] mmc: add generic mmc spi driver

Thomas Chou
2010-04-28T02:50:43Z

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@wytron.com.tw>
---
v4: change mmc_spi subcommand to search and create new mmc dev.
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

common/Makefile | 1 +
common/cmd_mmc_spi.c | 117 ++++++++++++++++++
drivers/mmc/Makefile | 1 +
drivers/mmc/mmc_spi.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/crc7.h | 14 ++
include/mmc_spi.h | 27 ++++
lib/Makefile | 1 +
lib/crc7.c | 62 ++++++++++
8 files changed, 541 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..d883837
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,117 @@
+/*
+ * Command for mmc_spi setup.
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@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>
+
+#define MMC_SPI_DEV_NUM_MAX 16 /* number of MMC devs to query */
+#ifndef CONFIG_MMC_SPI_BUS
+# define CONFIG_MMC_SPI_BUS 0
+#endif
+#ifndef CONFIG_MMC_SPI_CS
+# define CONFIG_MMC_SPI_CS 1
+#endif
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 30000000
+#endif
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_3
+#endif
+
+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;
+ struct mmc_spi_priv *priv;
+
+ for (dev_num = 0; dev_num < MMC_SPI_DEV_NUM_MAX; dev_num++) {
+ mmc = find_mmc_device(dev_num);
+ if (!mmc || strcmp(mmc->name, "MMC_SPI") == 0)
+ break;
+ }
+ if (!mmc || dev_num == MMC_SPI_DEV_NUM_MAX) {
+ printf("Create MMC Device\n");
+ mmc = mmc_spi_init(CONFIG_MMC_SPI_BUS,
+ CONFIG_MMC_SPI_CS,
+ CONFIG_MMC_SPI_SPEED,
+ CONFIG_MMC_SPI_MODE);
+ if (!mmc) {
+ printf("Failed to create MMC Device\n");
+ return 1;
+ }
+ dev_num = mmc->block_dev.dev;
+ }
+
+ priv = mmc->priv;
+ bus = priv->bus;
+ cs = priv->cs;
+ speed = priv->speed;
+ mode = priv->mode;
+
+ if (argc < 2)
+ goto info;
+ cs = simple_strtoul(argv[1], &endp, 0);
+ if (*argv[1] == 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 >= 3) {
+ speed = simple_strtoul(argv[2], &endp, 0);
+ if (*argv[2] == 0 || *endp != 0)
+ goto usage;
+ }
+ if (argc >= 4) {
+ mode = simple_strtoul(argv[3], &endp, 16);
+ if (*argv[3] == 0 || *endp != 0)
+ goto usage;
+ }
+ if (!spi_cs_is_valid(bus, cs)) {
+ printf("Invalid SPI bus %u cs %u\n", bus, cs);
+ return 1;
+ }
+
+ if (bus != priv->bus || cs != priv->cs ||
+ speed != priv->speed || mode != priv->mode) {
+ priv->bus = bus;
+ priv->cs = cs;
+ priv->speed = speed;
+ priv->mode = mode;
+ if (priv->slave) {
+ spi_free_slave(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, 4, 0, do_mmc_spi,
+ "mmc_spi setup",
+ "[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 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,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..dedcef7
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,318 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@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) {
+ debug("%s: failed to setup spi slave\n", __func__);
+ 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;
+}
+
+struct mmc *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 NULL;
+ priv = malloc(sizeof(*priv));
+ if (!priv) {
+ free(mmc);
+ return NULL;
+ }
+ 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 mmc;
+}
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..fbfa1ce
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,27 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+
+#include <spi.h>
+#include <mmc.h>
+#include <linux/types.h>
+
+struct mmc_spi_priv {
+ struct spi_slave *slave;
+ uint bus;
+ uint cs;
+ uint speed;
+ uint mode;
+ uint ocr58;
+ uint sd_if_cond:1;
+};
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
+#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