--- /dev/null
+/*
+ * linux/drivers/mmc/core/mmc_panic_ops.c
+ *
+ * Copyright (C) 2011 Intel Corp
+ * Author: dongxing.zhang@intel.com
+ * Author: jun.zhang@intel.com
+ * Author: chuansheng.liu@intel.com
+ * Author: chuanxiao.dong@intel.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; version 2 of the License.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.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 "bus.h"
+#include "host.h"
+
+#include "mmc_ops.h"
+
+static struct mmc_panic_host *panic_host;
+
+static int mmc_emergency_prepare(void)
+{
+ struct mmc_panic_host *host = panic_host;
+ struct mmc_host *mmc = host->mmc;
+
+ if (!host->panic_ops)
+ return -EPERM;
+ /*
+ * once panic happened, we monopolize the host controller.
+ * so claim host without relase any more.
+ */
+ mmc->claimed = 1;
+ mmc->claimer = current;
+ mmc->claim_cnt += 1;
+#ifdef CONFIG_MMC_CLKGATE
+ /*
+ * disable the clock gating
+ */
+ mmc->clk_gated = false;
+ mmc->clk_requests++;
+ mmc->ios.clock = mmc->clk_old;
+#endif
+ /*
+ * prepare host controller
+ */
+ if (host->panic_ops->prepare)
+ return host->panic_ops->prepare(host);
+ return 0;
+}
+
+static void mmc_emergency_ready(void)
+{
+ panic_host->panic_ready = 1;
+}
+
+/*
+ * Return the card size in sectors.
+ *
+ * return value:
+ * the sector number
+ */
+static unsigned int mmc_get_capacity(struct mmc_card *card)
+{
+ if (!mmc_card_sd(card) && mmc_card_blockaddr(card))
+ return card->ext_csd.sectors;
+ else
+ return card->csd.capacity << (card->csd.read_blkbits - 9);
+}
+
+static void mmc_emergency_send_req(struct mmc_request *mrq)
+{
+ struct mmc_panic_host *host = panic_host;
+
+ mrq->cmd->error = 0;
+ mrq->cmd->mrq = mrq;
+
+ if (mrq->data) {
+ BUG_ON(mrq->data->blksz > host->max_blk_size);
+ BUG_ON(mrq->data->blocks > host->max_blk_count);
+ BUG_ON(mrq->data->blocks * mrq->data->blksz >
+ host->max_req_size);
+
+ mrq->cmd->data = mrq->data;
+ mrq->data->error = 0;
+ mrq->data->mrq = mrq;
+ if (mrq->stop) {
+ mrq->data->stop = mrq->stop;
+ mrq->stop->error = 0;
+ mrq->stop->mrq = mrq;
+ }
+ }
+
+ /*
+ * Send the request to host
+ *
+ * if request handling is successful, return.
+ * If request handling is failed and has rety, resend request.
+ * During retry, if request handling is still failed, core layer
+ * will keep on retry untill cmd->retries is 0.
+ *
+ * So in this way, makes retry blind to host driver. Totally
+ * controlled by core driver
+ */
+ host->panic_ops->request(host, mrq);
+
+ while((mrq->cmd->error || (mrq->data && (mrq->data->error ||
+ (mrq->data->stop && mrq->data->stop->error)))) &&
+ mrq->cmd->retries > 0) {
+ /* clear errors */
+ mrq->cmd->error = 0;
+ if (mrq->data) {
+ mrq->data->error = 0;
+ if (mrq->stop)
+ mrq->stop->error = 0;
+ }
+ host->panic_ops->request(host, mrq);
+ mrq->cmd->retries--;
+ }
+}
+
+static int mmc_emergency_send_cmd(struct mmc_command *cmd, int retries)
+{
+ struct mmc_request mrq;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ memset(cmd->resp, 0, sizeof(cmd->resp));
+ cmd->retries = retries;
+
+ mrq.cmd = cmd;
+ cmd->data = NULL;
+
+ mmc_emergency_send_req(&mrq);
+
+ return cmd->error;
+}
+
+static int __mmc_emergency_write(unsigned int blk_id)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+
+ 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_WRITE_BLOCK;
+ cmd.arg = blk_id;
+ if (!panic_host->blkaddr)
+ cmd.arg <<= 9;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ /*
+ * Fix these values;
+ */
+ data.blksz = 512;
+ data.blocks = 1;
+ data.dmabuf = panic_host->dmabuf;
+
+ mmc_emergency_send_req(&mrq);
+
+ return cmd.error;
+}
+
+static int mmc_emergency_go_idle(struct mmc_panic_host *host)
+{
+ int err;
+ struct mmc_command cmd;
+
+ /*
+ * Non-SPI hosts need to prevent chipselect going active during
+ * GO_IDLE; that would put chips into SPI mode. Remind them of
+ * that in case of hardware that won't pull up DAT3/nCS otherwise.
+ *
+ * SPI hosts ignore ios.chip_select; it's managed according to
+ * rules that must accomodate non-MMC slaves which this layer
+ * won't even know about.
+ */
+ if (!mmc_host_is_spi(host)) {
+ host->ios.chip_select = MMC_CS_HIGH;
+ host->panic_ops->set_ios(host);
+ mdelay(1);
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_GO_IDLE_STATE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
+
+ err = mmc_emergency_send_cmd(&cmd, 0);
+
+ mdelay(1);
+
+ if (!mmc_host_is_spi(host)) {
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->panic_ops->set_ios(host);
+ mdelay(1);
+ }
+
+ return err;
+}
+
+static int mmc_emergency_send_op_cond(struct mmc_panic_host *host,
+ u32 ocr, u32 *rocr)
+{
+ struct mmc_command cmd;
+ int i, err = 0;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_OP_COND;
+ cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
+
+ for (i = 100; i; i--) {
+ err = mmc_emergency_send_cmd(&cmd, 0);
+ if (err)
+ break;
+
+ /* if we're just probing, do a single pass */
+ if (ocr == 0)
+ break;
+
+ /* otherwise wait until reset completes */
+ if (mmc_host_is_spi(host)) {
+ if (!(cmd.resp[0] & R1_SPI_IDLE))
+ break;
+ } else {
+ if (cmd.resp[0] & MMC_CARD_BUSY)
+ break;
+ }
+
+ err = -ETIMEDOUT;
+
+ /*
+ * If command 1 is failed, wait 10ms and then
+ * have a retry. Card may need time to prepare
+ * for the next command 1
+ */
+ mdelay(10);
+ }
+
+ if (rocr && !mmc_host_is_spi(host))
+ *rocr = cmd.resp[0];
+
+ return err;
+}
+
+static int mmc_emergency_all_send_cid(u32 *cid)
+{
+ int err;
+ struct mmc_command cmd;
+
+ 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_emergency_send_cmd(&cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ memcpy(cid, cmd.resp, sizeof(u32) * 4);
+
+ return 0;
+}
+
+static int mmc_emergency_set_relative_addr(struct mmc_card *card)
+{
+ int err;
+ struct mmc_command cmd;
+
+ 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_emergency_send_cmd(&cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int mmc_emergency_select_card(struct mmc_card *card)
+{
+ int err;
+ struct mmc_command cmd;
+
+ 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_emergency_send_cmd(&cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int mmc_emergency_send_status(struct mmc_panic_host *host, u32 *status)
+{
+ struct mmc_card *card = host->card;
+ int err;
+ struct mmc_command cmd;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_STATUS;
+ if (!mmc_host_is_spi(host))
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_emergency_send_cmd(&cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ /* NOTE: callers are required to understand the difference
+ * between "native" and SPI format status words!
+ */
+ if (status)
+ *status = cmd.resp[0];
+
+ return 0;
+}
+
+static int mmc_emergency_switch(struct mmc_panic_host *host,
+ u8 set, u8 index, u8 value)
+{
+ struct mmc_card *card = host->card;
+ int err;
+ struct mmc_command cmd;
+ u32 status;
+
+ 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_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+
+ err = mmc_emergency_send_cmd(&cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ /* Must check status to be sure of no errors */
+ do {
+ err = mmc_emergency_send_status(host, &status);
+ if (err)
+ return err;
+ if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ break;
+ if (mmc_host_is_spi(host))
+ break;
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ if (mmc_host_is_spi(host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (status & 0xFDFFA000)
+ printk(KERN_WARNING "%s: unexpected status %#x after "
+ "switch", mmc_hostname(card->host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+static int mmc_emergency_spi_set_crc(struct mmc_panic_host *host, int use)
+{
+ return -1;
+}
+
+static int mmc_emergency_send_cid(struct mmc_panic_host *host, u32 *cid)
+{
+ return -1;
+}
+/*
+ * reinit card:
+ * should also consider about the SPI host
+ */
+static int mmc_emergency_reinit_card(void)
+{
+ struct mmc_panic_host *host = panic_host;
+ struct mmc_card *card = host->card;
+ u32 ocr = host->ocr;
+ int err, ddr = 0;
+ u32 cid[4];
+ unsigned int max_dtr;
+
+ /*
+ * low the clock to be init clock
+ */
+ if (mmc_host_is_spi(host)) {
+ host->ios.chip_select = MMC_CS_HIGH;
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ } else {
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ }
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ /*
+ * AS eMMC spec said, card init frequency cannot higher
+ * then 400Khz. But a good card should support for 400Khz
+ * frequence in initialize process.
+ */
+ host->ios.clock = 400000;
+ host->panic_ops->set_ios(host);
+
+ /*
+ * Since we're changing the OCR value, we seem to
+ * need to tell some cards to go back to the idle
+ * state. We wait 1ms to give cards time to
+ * respond.
+ */
+ mmc_emergency_go_idle(host);
+
+ /* The extra bit indicates that we support high capacity */
+ err = mmc_emergency_send_op_cond(host, ocr | (1 << 30), NULL);
+ if (err)
+ goto err;
+
+ /*
+ * For SPI, enable CRC as appropriate.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_emergency_spi_set_crc(host, 1);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Fetch CID from card.
+ */
+ if (mmc_host_is_spi(host))
+ err = mmc_emergency_send_cid(host, cid);
+ else
+ err = mmc_emergency_all_send_cid(cid);
+ if (err)
+ goto err;
+
+ if (memcmp(cid, card->raw_cid, sizeof(cid)) != 0) {
+ err = -ENOENT;
+ goto err;
+ }
+
+ /*
+ * For native busses: set card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_emergency_set_relative_addr(card);
+ if (err)
+ goto err;
+
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ host->panic_ops->set_ios(host);
+ }
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_emergency_select_card(card);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Activate high speed (if supported)
+ */
+ if ((card->ext_csd.hs_max_dtr != 0) &&
+ (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
+ err = mmc_emergency_switch(host, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 1);
+ if (err && err != -EBADMSG)
+ goto err;
+
+ if (err) {
+ printk(KERN_WARNING "%s: switch to highspeed failed\n",
+ __func__);
+ err = 0;
+ } else {
+ mmc_card_set_highspeed(card);
+ host->ios.timing = MMC_TIMING_MMC_HS;
+ host->panic_ops->set_ios(host);
+ }
+ }
+
+ /*
+ * Compute bus speed.
+ */
+ max_dtr = (unsigned int)-1;
+
+ if (mmc_card_highspeed(card)) {
+ if (max_dtr > card->ext_csd.hs_max_dtr)
+ max_dtr = card->ext_csd.hs_max_dtr;
+ } else if (max_dtr > card->csd.max_dtr) {
+ max_dtr = card->csd.max_dtr;
+ }
+
+ host->ios.clock = max_dtr;
+ host->panic_ops->set_ios(host);
+
+ /*
+ * Indicate DDR mode (if supported).
+ */
+ if (mmc_card_highspeed(card)) {
+ if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
+ && (host->caps & (MMC_CAP_1_8V_DDR)))
+ ddr = MMC_1_8V_DDR_MODE;
+ else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
+ && (host->caps & (MMC_CAP_1_2V_DDR)))
+ ddr = MMC_1_2V_DDR_MODE;
+ }
+
+ /*
+ * Activate wide bus and DDR (if supported).
+ */
+ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+ (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
+ unsigned ext_csd_bit, bus_width;
+
+ if (host->caps & MMC_CAP_8_BIT_DATA) {
+ if (ddr)
+ ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
+ else
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
+ bus_width = MMC_BUS_WIDTH_8;
+ } else {
+ if (ddr)
+ ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
+ else
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
+ bus_width = MMC_BUS_WIDTH_4;
+ }
+
+ err = mmc_emergency_switch(host, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, ext_csd_bit);
+
+ if (err && err != -EBADMSG)
+ goto err;
+
+ if (err) {
+ printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
+ "failed\n", __func__, 1 << bus_width, ddr);
+ err = 0;
+ } else {
+ if (ddr)
+ mmc_card_set_ddr_mode(card);
+ else
+ ddr = MMC_SDR_MODE;
+
+ host->ios.bus_width = bus_width;
+ host->ios.ddr = ddr;
+ host->panic_ops->set_ios(host);
+ }
+ }
+
+ return 0;
+err:
+ return err;
+}
+
+/*
+ * mmc_emergency_write - write 512Bytes to card in panic mode
+ * @data: data pointer which should pointed to an area no more than
+ * 512Bytes
+ * @blk_id: the block id need to write this 512B data
+ *
+ * This function is supplied to ipanic driver to write 512B data
+ * in panic mode. Please also make sure the data size should not be
+ * larger than 512B, otherwise data lossing.
+ */
+int mmc_emergency_write(char *data, unsigned int blk_id)
+{
+ struct mmc_panic_host *host = panic_host;
+ int ret;
+ if (host == NULL) {
+ pr_err("%s: no device for panic record\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!host->panic_ready) {
+ pr_err("%s: device is not ready for panic record\n", __func__);
+ return -EPERM;
+ }
+
+ if (!data) {
+ pr_err("%s: invalided writing data\n", __func__);
+ return -EINVAL;
+ }
+
+ if (blk_id > host->totalsecs || blk_id < 0) {
+ pr_err("%s: invalided writing blk_id\n", __func__);
+ return -EINVAL;
+ }
+ /*
+ * everything is OK. So, let's start panic record.
+ *
+ * Copy the message data to logbuf
+ */
+ memcpy(host->logbuf, data, SECTOR_SIZE);
+
+ ret = __mmc_emergency_write(blk_id);
+
+ return ret;
+}
+EXPORT_SYMBOL(mmc_emergency_write);
+
+/*
+ * mmc_emergency_init: init host controller and emmc card
+ * when kernel panic occures
+ *
+ * return value:
+ * 0 - init successfully
+ * negative value - Failed during init
+ * -ENODEV - emmc card was removed by driver
+ */
+int mmc_emergency_init(void)
+{
+ int ret;
+ if (panic_host == NULL) {
+ pr_err("%s: no device for panic record\n", __func__);
+ return -ENODEV;
+ }
+
+ if (panic_host->mmc == NULL) {
+ pr_err("%s: panic host was not setup\n", __func__);
+ return -ENODEV;
+ }
+
+ ret = mmc_emergency_prepare();
+ if (ret) {
+ pr_info("%s: prepare host controller failed\n", __func__);
+ return ret;
+ }
+
+ /*
+ * reset card since we are not sure whether card is in a good status
+ *
+ * Since in panic mode, we init a old card, so all the command to be
+ * used has no data. So we can reuse the sdhci ops
+ */
+ ret = mmc_emergency_reinit_card();
+ if (ret) {
+ pr_info("%s: reinit card failed\n", __func__);
+ return ret;
+ }
+
+ /*
+ * OK. we are ready
+ */
+ mmc_emergency_ready();
+
+ return 0;
+}
+EXPORT_SYMBOL(mmc_emergency_init);
+
+/*
+ * mmc_emergency_setup - init panic_host which is used for panic writing
+ * @host: mmc host
+ *
+ * This function can sample some important value for panic_host use to init
+ * host controller and card. It only works for the driver which has already
+ * called mmc_alloc_panic_host in its probing process
+ */
+void mmc_emergency_setup(struct mmc_host *mmc)
+{
+ struct mmc_panic_host *host = panic_host;
+
+ /*
+ * mmc host has no panic host
+ */
+ if (!mmc->phost)
+ return;
+
+ /*
+ * before setup panic host, make sure panic host is
+ * allocated
+ */
+ if (host == NULL)
+ return;
+
+ /*
+ * panic host has already been setup
+ */
+ if (host->mmc)
+ return;
+
+ /*
+ * mmc host didn't init card
+ */
+ if (!mmc->card)
+ return;
+ /*
+ * if is SDIO card or SD card, by pass
+ */
+ if (mmc_card_sdio(mmc->card) ||
+ mmc_card_sd(mmc->card))
+ return;
+
+ host->card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
+ if (!host->card) {
+ pr_err("%s: cannot alloc mmc_card for panic host\n",
+ __func__);
+ return;
+ }
+
+ memcpy(host->card, mmc->card, sizeof(struct mmc_card));
+ host->caps = mmc->caps;
+ host->mmc = mmc;
+ host->ocr = mmc->ocr;
+ host->totalsecs = mmc_get_capacity(mmc->card);
+ host->max_blk_size = mmc->max_blk_size;
+ host->max_blk_count = mmc->max_blk_count;
+ host->max_req_size = mmc->max_req_size;
+ if (mmc_card_blockaddr(mmc->card))
+ host->blkaddr = 1;
+ /*
+ * sample ios values
+ */
+ memcpy(&host->ios, &mmc->ios, sizeof(struct mmc_ios));
+#ifdef CONFIG_MMC_CLKGATE
+ if (mmc->ios.clock == 0)
+ host->ios.clock = mmc->clk_old;
+#endif
+ if (host->panic_ops && host->panic_ops->setup)
+ host->panic_ops->setup(host);
+
+ return;
+}
+EXPORT_SYMBOL(mmc_emergency_setup);
+
+/*
+ * mmc_alloc_panic_host - used for host layer driver to alloc mmc_panic_host.
+ * @host: mmc host
+ * @ops: this is a pointer which points to mmc_host_panic_ops. This ops should
+ * be defined in host layer driver
+ *
+ * This function need to know the mmc_host_panic_ops, host layer driver should
+ * call this function during probing.
+ *
+ */
+void mmc_alloc_panic_host(struct mmc_host *host,
+ const struct mmc_host_panic_ops *ops)
+{
+ if (panic_host) {
+ pr_info("%s: already allocate panic host\n", __func__);
+ return;
+ }
+
+ panic_host = kzalloc(sizeof(struct mmc_panic_host), GFP_KERNEL);
+ if (!panic_host) {
+ pr_err("%s %s: panic structure allocate error\n",
+ __func__, mmc_hostname(host));
+ return;
+ }
+ /*
+ * allocate log buffer and DMA buffer
+ * log buffer size is 512
+ */
+ panic_host->logbuf = kzalloc(SECTOR_SIZE, GFP_KERNEL);
+ if (!panic_host->logbuf) {
+ pr_err("%s %s: log buf allocate error\n",
+ __func__, mmc_hostname(host));
+ goto free_panic_host;
+ }
+
+ panic_host->dmabuf = dma_map_single(host->parent, panic_host->logbuf,
+ SECTOR_SIZE, DMA_TO_DEVICE);
+ if (!panic_host->dmabuf) {
+ pr_err("%s %s: DMA buf allocate error\n",
+ __func__, mmc_hostname(host));
+ goto free_logbuf;
+ }
+
+ panic_host->panic_ops = ops;
+ panic_host->mmc = NULL;
+ host->phost = panic_host;
+
+ return;
+
+free_logbuf:
+ kfree(panic_host->logbuf);
+free_panic_host:
+ kfree(panic_host);
+}
+EXPORT_SYMBOL(mmc_alloc_panic_host);