From 38e65127b1d02c5e5fb2886dda9a4981cbce24db Mon Sep 17 00:00:00 2001 From: Chuanxiao Dong Date: Tue, 25 Oct 2011 20:20:33 +0800 Subject: [PATCH] Panic: mmc: core: implement mmc panic ops BZ: 16578 Port this patch from R2 Ops for panic driver to write kernel logs or user space logs into eMMC card when panic happened. Change-Id: I84b9e397bb12610a5a93454a0be63b90a99baccf Signed-off-by: Chuanxiao Dong Reviewed-on: http://android.intel.com:8080/26320 Reviewed-by: Gross, Mark Reviewed-by: Yang, Fei Tested-by: Sun, Jianhua Reviewed-by: buildbot Tested-by: buildbot --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/core.c | 1 + drivers/mmc/core/mmc_panic_ops.c | 826 +++++++++++++++++++++++++++++++++++++++ include/linux/mmc/core.h | 1 + include/linux/mmc/host.h | 39 ++ 5 files changed, 868 insertions(+), 1 deletion(-) create mode 100644 drivers/mmc/core/mmc_panic_ops.c diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 6395019..8dcea5c 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,6 +7,6 @@ mmc_core-y := core.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ - quirks.o + quirks.o mmc_panic_ops.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2755cc5..8ec7387 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2135,6 +2135,7 @@ void mmc_rescan(struct work_struct *work) mmc_release_host(host); out: + mmc_emergency_setup(host); if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); } diff --git a/drivers/mmc/core/mmc_panic_ops.c b/drivers/mmc/core/mmc_panic_ops.c new file mode 100644 index 0000000..3ec85b6 --- /dev/null +++ b/drivers/mmc/core/mmc_panic_ops.c @@ -0,0 +1,826 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 4dbfc99..86064a9 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -118,6 +118,7 @@ struct mmc_data { unsigned int sg_len; /* size of scatter list */ struct scatterlist *sg; /* I/O scatter list */ s32 host_cookie; /* host private data */ + dma_addr_t dmabuf; /* used in panic mode */ }; struct mmc_request { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index b4d9452..a4971fc 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -77,6 +77,16 @@ struct mmc_ios { #define MMC_SET_DRIVER_TYPE_D 3 }; +struct mmc_panic_host; + +struct mmc_host_panic_ops { + void (*request)(struct mmc_panic_host *, struct mmc_request *); + int (*prepare)(struct mmc_panic_host *); + int (*setup)(struct mmc_panic_host *); + void (*set_ios)(struct mmc_panic_host *); + void (*dumpregs)(struct mmc_panic_host *); +}; + struct mmc_host_ops { /* * Hosts that support power saving can use the 'enable' and 'disable' @@ -166,6 +176,28 @@ struct mmc_async_req { int (*err_check) (struct mmc_card *, struct mmc_async_req *); }; +struct mmc_panic_host { + /* + * DMA buffer for the log + */ + dma_addr_t dmabuf; + void *logbuf; + const struct mmc_host_panic_ops *panic_ops; + unsigned int panic_ready; + unsigned int totalsecs; + unsigned int max_blk_size; + unsigned int max_blk_count; + unsigned int max_req_size; + unsigned int blkaddr; + unsigned int caps; + void __iomem *pmu_cfg; + u32 ocr; /* the current OCR setting */ + struct mmc_ios ios; /* current io bus settings */ + struct mmc_card *card; + struct mmc_host *mmc; + void *priv; +}; + struct mmc_host { struct device *parent; struct device class_dev; @@ -325,9 +357,16 @@ struct mmc_host { } embedded_sdio_data; #endif + struct mmc_panic_host *phost; unsigned long private[0] ____cacheline_aligned; }; +#define SECTOR_SIZE 512 +int mmc_emergency_init(void); +int mmc_emergency_write(char *, unsigned int); +void mmc_alloc_panic_host(struct mmc_host *, const struct mmc_host_panic_ops *); +void mmc_emergency_setup(struct mmc_host *host); + extern struct mmc_host *mmc_alloc_host(int extra, struct device *); extern int mmc_add_host(struct mmc_host *); extern void mmc_remove_host(struct mmc_host *); -- 2.7.4