From c0e6859eb0180acab8a824ae277c0fb80e529e38 Mon Sep 17 00:00:00 2001 From: lijuan Date: Mon, 25 Nov 2024 15:53:05 +0800 Subject: [PATCH] pstore-blk: add mmc panic write when sys panic user manual: 1.config: enable MMC_SDHCI_OF_K1X_PANIC 2.config: CONFIG_PSTORE_BLK_BLKDEV,eg:/dev/mmcblk0p7 3.partiton:add pstore partiton test cmd: 1. mount -t pstore pstore /sys/fs/pstore 2. echo c > /proc/sysrq-trigger Change-Id: I88fbe8faedc5ccdffece72e2813e9d173dda9d65 Signed-off-by: lijuan --- drivers/mmc/host/Kconfig | 12 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-k1-panic.c | 599 ++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-k1-panic.h | 420 +++++++++++++++++++++ fs/pstore/blk.c | 131 ++++++- include/linux/pstore_blk.h | 26 ++ 6 files changed, 1182 insertions(+), 7 deletions(-) create mode 100644 drivers/mmc/host/sdhci-k1-panic.c create mode 100644 drivers/mmc/host/sdhci-k1-panic.h diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ebeaaaa800d1..c2655e79d431 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -277,6 +277,18 @@ config MMC_SDHCI_OF_K1X If unsure, say N. +config MMC_SDHCI_OF_K1X_PANIC + tristate "SDHCI OF support for the Spacemit K1X SDHCI PANIC WIRTE" + depends on MMC_SDHCI_OF_K1X + depends on PSTORE_BLK + help + This selects the Secure Digital Host Controller Interface (SDHCI) + found in the Spacemit SoC K1X panic write for pstore. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_CADENCE tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 1e6e0c7ac03c..3fe53a3e19e3 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_OF_K1PRO) += sdhci-of-k1pro.o obj-$(CONFIG_MMC_SDHCI_OF_K1X) += sdhci-of-k1x.o +obj-$(CONFIG_MMC_SDHCI_OF_K1X_PANIC)+= sdhci-k1-panic.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o diff --git a/drivers/mmc/host/sdhci-k1-panic.c b/drivers/mmc/host/sdhci-k1-panic.c new file mode 100644 index 000000000000..01eb6bbacc99 --- /dev/null +++ b/drivers/mmc/host/sdhci-k1-panic.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Spacemit Mobile Storage Host Controller + * + * Copyright (C) 2023 Spacemit + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdhci-k1-panic.h" + +extern struct bdev_info k1_bdev_info; +extern int k1_pstore_blk_get_bdev_info(struct bdev_info *bdev); + +#define MWR_TO_NS (250*1000*1000) +#define MWR_RFAIL -1 +#define MWR_ROK 0 + +#define k1_SMHC_BASE 0xD4281000 +#define k1_PMU_BASE 0xD4282800 + +static char *ghost_base_reg; + +static u8 __maybe_unused cur_com_reg[960]; /* 8 line, 120 character per line */ +static u8 __maybe_unused cur_pri_reg[960]; + +static int spacemit_reg[] = { + 0x100, 0x104, 0x108, 0x10c, 0x110, 0x114, 0x118, 0x11c, + 0x120, 0x124, 0x128, 0x12c, 0x130, 0x134, 0x160, 0x164, + 0x168, 0x16c, 0x170, 0x174, 0x178, 0x17c, 0x180, 0x184, + 0x188, 0x18c, 0x190, 0x1f0, 0x1f4, 0xFFF, +}; + +static void __maybe_unused dump_sdh_regs(char *host, u8 *com_reg, u8 *pri_reg) +{ + int val; + int offset; + int i; + int len; + u8 *buf; + + buf = com_reg; + len = 0; + i = 0; + for (offset = 0; offset < 0x70; offset += 4) { + val = sdhci_readl(host, offset); + if (i % 4 == 0) + len += sprintf(buf + len, "\n"); + len += sprintf(buf + len, "\toffset:0x%03x 0x%08x\t", offset, val); + i++; + } + + if (i % 4 == 0) + len += sprintf(buf + len, "\n"); + val = sdhci_readl(host, 0xe0); + len += sprintf(buf + len, "\toffset:0x%03x 0x%08x\t", 0xe0, val); + val = sdhci_readl(host, 0xfc); + len += sprintf(buf + len, "\toffset:0x%03x 0x%08x\t\n", 0xfc, val); + + buf = pri_reg; + len = 0; + i = 0; + do { + if (spacemit_reg[i] > 0x134) + break; + val = sdhci_readl(host, spacemit_reg[i]); + if (i % 4 == 0) + len += sprintf(buf + len, "\n"); + len += sprintf(buf + len, "\toffset:0x%03x 0x%08x\t", spacemit_reg[i], val); + i++; + } while (spacemit_reg[i] != 0xFFF); + len += sprintf(buf + len, "\n"); +} + +static void sdhci_reset(char *host, u8 mask) +{ + unsigned long timeout; + + /* Wait max 100 ms */ + timeout = 100; + sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); + while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { + if (timeout == 0) { + pr_err("%s: Reset 0x%x never completed.\n", + __func__, (int)mask); + return; + } + timeout--; + udelay(1000); + } +} + +static void sdhci_cmd_done(char *host, struct mmc_cmd *cmd) +{ + int i; + if (cmd->resp_type & MMC_RSP_136) { + /* CRC is stripped so we need to do some shifting. */ + for (i = 0; i < 4; i++) { + cmd->response[i] = sdhci_readl(host, + SDHCI_RESPONSE + (3-i)*4) << 8; + if (i != 3) + cmd->response[i] |= sdhci_readb(host, + SDHCI_RESPONSE + (3-i)*4-1); + } + } else { + cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE); + } +} + +static void sdhci_transfer_pio(char *host, struct mmc_data *data) +{ + int i; + char *offs; + for (i = 0; i < data->blocksize; i += 4) { + offs = data->dest + i; + if (data->flags == MMC_DATA_READ) + *(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER); + else + sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER); + } +} + +static int sdhci_transfer_data(char *host, struct mmc_data *data) +{ + unsigned int stat, rdy, mask, timeout, block = 0; + bool transfer_done = false; + + timeout = 1000000; + rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; + mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE; + do { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & SDHCI_INT_ERROR) { + pr_debug("%s: Error detected in status(0x%X)!\n", + __func__, stat); + return -EIO; + } + if (!transfer_done && (stat & rdy)) { + if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)) + continue; + sdhci_writel(host, rdy, SDHCI_INT_STATUS); + sdhci_transfer_pio(host, data); + data->dest += data->blocksize; + if (++block >= data->blocks) { + /* Keep looping until the SDHCI_INT_DATA_END is + * cleared, even if we finished sending all the + * blocks. + */ + transfer_done = true; + continue; + } + } + + if (timeout-- > 0) + udelay(10); + else { + pr_err("%s: Transfer data timeout\n", __func__); + return -ETIMEDOUT; + } + } while (!(stat & SDHCI_INT_DATA_END)); + + return 0; +} + +static int sdhci_wait_dat0(char *host, int state, + int timeout_us) +{ + int tmp; + unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); + + // readx_poll_timeout is unsuitable because sdhci_readl accepts + // two arguments + do { + tmp = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (!!(tmp & SDHCI_DATA_0_LVL_MASK) == !!state) + return 0; + } while (!timeout_us || !time_after(jiffies, timeout)); + + return -ETIMEDOUT; +} + +#define SDHCI_CMD_MAX_TIMEOUT 3200 +#define SDHCI_CMD_DEFAULT_TIMEOUT 100 +#define SDHCI_READ_STATUS_TIMEOUT 1000 + +static int sdhci_send_command(char *host, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + unsigned int stat = 0; + int ret = 0; + int trans_bytes = 0; + u32 mask, flags, mode; + unsigned int time = 0; + ulong start; + + /* Timeout unit - ms */ + static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; + + mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; + + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION || + ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) && !data)) + mask &= ~SDHCI_DATA_INHIBIT; + + while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { + if (time >= cmd_timeout) { + pr_err("%s: MMC busy ", __func__); + if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) { + cmd_timeout += cmd_timeout; + pr_err("timeout increasing to: %u ms.\n", + cmd_timeout); + } else { + return -ECOMM; + } + } + time++; + udelay(1000); + } + + sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); + + mask = SDHCI_INT_RESPONSE; + if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) && !data) + mask = SDHCI_INT_DATA_AVAIL; + + if (!(cmd->resp_type & MMC_RSP_PRESENT)) + flags = SDHCI_CMD_RESP_NONE; + else if (cmd->resp_type & MMC_RSP_136) + flags = SDHCI_CMD_RESP_LONG; + else if (cmd->resp_type & MMC_RSP_BUSY) { + flags = SDHCI_CMD_RESP_SHORT_BUSY; + mask |= SDHCI_INT_DATA_END; + } else + flags = SDHCI_CMD_RESP_SHORT; + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= SDHCI_CMD_CRC; + if (cmd->resp_type & MMC_RSP_OPCODE) + flags |= SDHCI_CMD_INDEX; + if (data || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) + flags |= SDHCI_CMD_DATA; + + /* Set Transfer mode regarding to data flag */ + if (data) { + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + mode = SDHCI_TRNS_BLK_CNT_EN; + trans_bytes = data->blocks * data->blocksize; + if (data->blocks > 1) + mode |= SDHCI_TRNS_MULTI; + + if (data->flags == MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; + + sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, + data->blocksize), + SDHCI_BLOCK_SIZE); + sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); + } else if (cmd->resp_type & MMC_RSP_BUSY) { + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + } + + sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); + start = jiffies; + do { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & SDHCI_INT_ERROR) + break; + + if (time_after(jiffies, start + usecs_to_jiffies(SDHCI_READ_STATUS_TIMEOUT))) { + pr_err("%s: Timeout for status update!\n", + __func__); + return -ETIMEDOUT; + } + } while ((stat & mask) != mask); + + if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { + sdhci_cmd_done(host, cmd); + sdhci_writel(host, mask, SDHCI_INT_STATUS); + } else + ret = -1; + + if (!ret && data) + ret = sdhci_transfer_data(host, data); + + stat = sdhci_readl(host, SDHCI_INT_STATUS); + sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); + if (!ret) { + return 0; + } + + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + if (stat & SDHCI_INT_TIMEOUT) + return -ETIMEDOUT; + else + return -ECOMM; +} + +static int sdhci_send_cmd_retry(char *host, struct mmc_cmd *cmd, + struct mmc_data *data, uint retries) +{ + int ret; + + do { + ret = sdhci_send_command(host, cmd, data); + } while (ret && retries--); + + return ret; +} + +static int sdhci_send_status(char *host, unsigned int *status) +{ + struct mmc_cmd cmd; + int ret; + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0x1 << 16; + + ret = sdhci_send_cmd_retry(host, &cmd, NULL, 4); + if (!ret) + *status = cmd.response[0]; + + return ret; +} + +static int sdhci_poll_for_busy(char *host, int timeout_ms) +{ + unsigned int status; + int err; + + err = sdhci_wait_dat0(host, 1, timeout_ms * 1000); + if (err != -ENOSYS) + return err; + + while (1) { + err = sdhci_send_status(host, &status); + if (err) + return err; + + if ((status & MMC_STATUS_RDY_FOR_DATA) && + (status & MMC_STATUS_CURR_STATE) != + MMC_STATE_PRG) + break; + + if (status & MMC_STATUS_MASK) { + return -ECOMM; + } + + if (timeout_ms-- <= 0) + break; + + udelay(1000); + } + + if (timeout_ms <= 0) { + return -ETIMEDOUT; + } + + return 0; +} + +static int __maybe_unused sdhci_set_blocklen(char *host, int len) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = len; + + return sdhci_send_cmd_retry(host, &cmd, NULL, 4); +} + +static ulong sdhci_write_blocks(char *host, ulong start, + ulong blkcnt, const void *src, bool high_capacity) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int timeout_ms = 1000; + + printk("sdhci_write_blocks start:0x%lx blkcnt:0x%lx src:0x%p\n", + start, blkcnt, src); + + if (blkcnt > 1) { + cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT; + cmd.cmdarg = blkcnt & 0x0000FFFF; + cmd.resp_type = MMC_RSP_R1; + if (sdhci_send_command(host, &cmd, NULL)) { + printk("fail to set block count\n"); + return 0; + } + } + + if (blkcnt == 0) + return 0; + else if (blkcnt == 1) + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + + if (high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * MMC_BLOCK_SIZE; + + cmd.resp_type = MMC_RSP_R1; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = MMC_BLOCK_SIZE; + data.flags = MMC_DATA_WRITE; + + if (sdhci_send_command(host, &cmd, &data)) { + printk("write failed\n"); + return 0; + } + + /* Waiting for the ready status */ + if (sdhci_poll_for_busy(host, timeout_ms)) { + printk("poll for busy failed\n"); + return 0; + } + + return blkcnt; +} + +static ulong sdhci_bwrite(char *host, ulong start, + ulong blkcnt, const void *src) +{ + ulong cur, blocks_todo = blkcnt; + int b_max = MMC_MAX_BLK_COUNT; + + do { + cur = (blocks_todo > b_max) ? b_max : blocks_todo; + if (sdhci_write_blocks(host, start, cur, src, true) != cur) + return 0; + blocks_todo -= cur; + start += cur; + src += cur * MMC_BLOCK_SIZE; + } while (blocks_todo > 0); + + return blkcnt; +} + +static int sdhci_init(char *host) +{ + /* Enable only interrupts served by the SD controller */ + sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, + SDHCI_INT_ENABLE); + /* Mask all sdhci interrupt sources */ + sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); + + return 0; +} + +/**use for panic situation,no irq,no lock,no dma**/ +int k1_mmc_panic_write(u32 sec_addr, u32 sec_cnt, const char *inbuf) +{ + char *reg = ghost_base_reg; + + BUG_ON(inbuf == NULL); + if (!ghost_base_reg) { + printk("hostreg has not init\n"); + return MWR_RFAIL; + } + + sdhci_init(reg); + return sdhci_bwrite(reg, sec_addr, sec_cnt, inbuf); +} + +ssize_t k1_mmc_panic_write_ps(const char *buf, size_t bytes, loff_t pos) +{ + ssize_t ret; + printk("%s, %d\n", __func__, __LINE__); + if (((pos + bytes) >> SECTOR_SHIFT) > k1_bdev_info.nr_sects) { + printk("%s size + off (%llu + %llu) exceed (%llu)\n", __func__, + (unsigned long long)bytes, (unsigned long long)pos, k1_bdev_info.nr_sects); + return -EOVERFLOW; + } + ghost_base_reg = ioremap(k1_SMHC_BASE, 0x1F4); + if (!ghost_base_reg) { + printk("*iormap host failed*\n"); + return -1; + } + + ret = k1_mmc_panic_write(k1_bdev_info.start_sect + (pos >> SECTOR_SHIFT), bytes >> SECTOR_SHIFT, buf); + if (ret) + return ret; + return 0; +} +#if 0 +int k1_mmc_panic_init_ps(void *data) +{ + int ret = 0; + struct pstore_zone_info zone = {}; + //struct pstore_device_info dev = {}; + + printk("%s, %d\n", __func__, __LINE__); + + + ret = k1_mmc_panic_init(); + if (ret <= MWR_RFAIL) + return ret; + + zone.owner = THIS_MODULE; + zone.max_reason = CONFIG_PSTORE_BLK_MAX_REASON; + zone.panic_write = k1_mmc_panic_write_ps; + ret = register_pstore_zone(&zone); + if (ret == -ENODEV) { + printk("%s, %d\n", __func__, __LINE__); + } + if (ret) + goto exit_panic; + + return 0; + +exit_panic: + k1_mmc_panic_exit(); + return ret; +} +#endif + +static void k1_mmc_panic_resource_release(void) +{ + iounmap(ghost_base_reg); +} + +static int k1_mmc_register_pstore_panic(enum pstore_blk_notifier_type type, + struct pstore_device_info *pdi) +{ + switch (type) { + case PSTORE_BLK_BACKEND_REGISTER: + case PSTORE_BLK_BACKEND_PANIC_DRV_REGISTER: + pdi->zone.panic_write = k1_mmc_panic_write_ps; + pr_info("register pstore backend %s panic write\n", pdi->zone.name); + break; + + case PSTORE_BLK_BACKEND_UNREGISTER: + case PSTORE_BLK_BACKEND_PANIC_DRV_UNREGISTER: + pdi->zone.panic_write = NULL; + + break; + default: + goto err; + + } + + return NOTIFY_OK; + +err: + return NOTIFY_BAD; +} + +static struct pstore_blk_notifier k1_mmc_panic = { + .notifier_call = k1_mmc_register_pstore_panic, +}; + +static int __init k1_mmc_panic_init(void) +{ + if (register_pstore_blk_panic_notifier(&k1_mmc_panic) == NOTIFY_OK) + return 0; + else + return -1; +} + + +static void __exit k1_mmc_panic_exit(void) +{ + unregister_pstore_blk_panic_notifier(&k1_mmc_panic); + + k1_mmc_panic_resource_release(); +} + + +module_init(k1_mmc_panic_init); +module_exit(k1_mmc_panic_exit); + +MODULE_DESCRIPTION("Spacemit SD/MMC Card Panic Pstore backend Support"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0.0"); diff --git a/drivers/mmc/host/sdhci-k1-panic.h b/drivers/mmc/host/sdhci-k1-panic.h new file mode 100644 index 000000000000..35c1cc0bd479 --- /dev/null +++ b/drivers/mmc/host/sdhci-k1-panic.h @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Spacemit Mobile Storage Host Controller + * + * Copyright (C) 2023 Spacemit + */ + +#ifndef __K1_MMC_PANIC_H__ +#define __K1_MMC_PANIC_H__ + +/* + * Controller registers + */ + +#define SDHCI_DMA_ADDRESS 0x00 +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS +#define SDHCI_32BIT_BLK_CNT SDHCI_DMA_ADDRESS + +#define SDHCI_BLOCK_SIZE 0x04 +#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +#define SDHCI_BLOCK_COUNT 0x06 + +#define SDHCI_ARGUMENT 0x08 + +#define SDHCI_TRANSFER_MODE 0x0C +#define SDHCI_TRNS_DMA 0x01 +#define SDHCI_TRNS_BLK_CNT_EN 0x02 +#define SDHCI_TRNS_AUTO_CMD12 0x04 +#define SDHCI_TRNS_AUTO_CMD23 0x08 +#define SDHCI_TRNS_AUTO_SEL 0x0C +#define SDHCI_TRNS_READ 0x10 +#define SDHCI_TRNS_MULTI 0x20 + +#define SDHCI_COMMAND 0x0E +#define SDHCI_CMD_RESP_MASK 0x03 +#define SDHCI_CMD_CRC 0x08 +#define SDHCI_CMD_INDEX 0x10 +#define SDHCI_CMD_DATA 0x20 +#define SDHCI_CMD_ABORTCMD 0xC0 + +#define SDHCI_CMD_RESP_NONE 0x00 +#define SDHCI_CMD_RESP_LONG 0x01 +#define SDHCI_CMD_RESP_SHORT 0x02 +#define SDHCI_CMD_RESP_SHORT_BUSY 0x03 + +#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) +#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f) + +#define SDHCI_RESPONSE 0x10 + +#define SDHCI_BUFFER 0x20 + +#define SDHCI_PRESENT_STATE 0x24 +#define SDHCI_CMD_INHIBIT 0x00000001 +#define SDHCI_DATA_INHIBIT 0x00000002 +#define SDHCI_DOING_WRITE 0x00000100 +#define SDHCI_DOING_READ 0x00000200 +#define SDHCI_SPACE_AVAILABLE 0x00000400 +#define SDHCI_DATA_AVAILABLE 0x00000800 +#define SDHCI_CARD_PRESENT 0x00010000 +#define SDHCI_CARD_PRES_SHIFT 16 +#define SDHCI_CD_STABLE 0x00020000 +#define SDHCI_CD_LVL 0x00040000 +#define SDHCI_CD_LVL_SHIFT 18 +#define SDHCI_WRITE_PROTECT 0x00080000 +#define SDHCI_DATA_LVL_MASK 0x00F00000 +#define SDHCI_DATA_LVL_SHIFT 20 +#define SDHCI_DATA_0_LVL_MASK 0x00100000 +#define SDHCI_CMD_LVL 0x01000000 + +#define SDHCI_HOST_CONTROL 0x28 +#define SDHCI_CTRL_LED 0x01 +#define SDHCI_CTRL_4BITBUS 0x02 +#define SDHCI_CTRL_HISPD 0x04 +#define SDHCI_CTRL_DMA_MASK 0x18 +#define SDHCI_CTRL_SDMA 0x00 +#define SDHCI_CTRL_ADMA1 0x08 +#define SDHCI_CTRL_ADMA32 0x10 +#define SDHCI_CTRL_ADMA64 0x18 +#define SDHCI_CTRL_ADMA3 0x18 +#define SDHCI_CTRL_8BITBUS 0x20 +#define SDHCI_CTRL_CDTEST_INS 0x40 +#define SDHCI_CTRL_CDTEST_EN 0x80 + +#define SDHCI_POWER_CONTROL 0x29 +#define SDHCI_POWER_ON 0x01 +#define SDHCI_POWER_180 0x0A +#define SDHCI_POWER_300 0x0C +#define SDHCI_POWER_330 0x0E +/* + * VDD2 - UHS2 or PCIe/NVMe + * VDD2 power on/off and voltage select + */ +#define SDHCI_VDD2_POWER_ON 0x10 +#define SDHCI_VDD2_POWER_120 0x80 +#define SDHCI_VDD2_POWER_180 0xA0 + +#define SDHCI_BLOCK_GAP_CONTROL 0x2A + +#define SDHCI_WAKE_UP_CONTROL 0x2B +#define SDHCI_WAKE_ON_INT 0x01 +#define SDHCI_WAKE_ON_INSERT 0x02 +#define SDHCI_WAKE_ON_REMOVE 0x04 + +#define SDHCI_CLOCK_CONTROL 0x2C +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK 0xFF +#define SDHCI_DIV_MASK_LEN 8 +#define SDHCI_DIV_HI_MASK 0x300 +#define SDHCI_PROG_CLOCK_MODE 0x0020 +#define SDHCI_CLOCK_CARD_EN 0x0004 +#define SDHCI_CLOCK_PLL_EN 0x0008 +#define SDHCI_CLOCK_INT_STABLE 0x0002 +#define SDHCI_CLOCK_INT_EN 0x0001 + +#define SDHCI_TIMEOUT_CONTROL 0x2E + +#define SDHCI_SOFTWARE_RESET 0x2F +#define SDHCI_RESET_ALL 0x01 +#define SDHCI_RESET_CMD 0x02 +#define SDHCI_RESET_DATA 0x04 + +#define SDHCI_INT_STATUS 0x30 +#define SDHCI_INT_ENABLE 0x34 +#define SDHCI_SIGNAL_ENABLE 0x38 +#define SDHCI_INT_RESPONSE 0x00000001 +#define SDHCI_INT_DATA_END 0x00000002 +#define SDHCI_INT_BLK_GAP 0x00000004 +#define SDHCI_INT_DMA_END 0x00000008 +#define SDHCI_INT_SPACE_AVAIL 0x00000010 +#define SDHCI_INT_DATA_AVAIL 0x00000020 +#define SDHCI_INT_CARD_INSERT 0x00000040 +#define SDHCI_INT_CARD_REMOVE 0x00000080 +#define SDHCI_INT_CARD_INT 0x00000100 +#define SDHCI_INT_RETUNE 0x00001000 +#define SDHCI_INT_CQE 0x00004000 +#define SDHCI_INT_ERROR 0x00008000 +#define SDHCI_INT_TIMEOUT 0x00010000 +#define SDHCI_INT_CRC 0x00020000 +#define SDHCI_INT_END_BIT 0x00040000 +#define SDHCI_INT_INDEX 0x00080000 +#define SDHCI_INT_DATA_TIMEOUT 0x00100000 +#define SDHCI_INT_DATA_CRC 0x00200000 +#define SDHCI_INT_DATA_END_BIT 0x00400000 +#define SDHCI_INT_BUS_POWER 0x00800000 +#define SDHCI_INT_AUTO_CMD_ERR 0x01000000 +#define SDHCI_INT_ADMA_ERROR 0x02000000 +#define SDHCI_INT_TUNING_ERROR 0x04000000 + +#define SDHCI_INT_NORMAL_MASK 0x00007FFF +#define SDHCI_INT_ERROR_MASK 0xFFFF8000 + +#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ + SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX | \ + SDHCI_INT_AUTO_CMD_ERR) +#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ + SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ + SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \ + SDHCI_INT_BLK_GAP | SDHCI_INT_TUNING_ERROR) +#define SDHCI_INT_ALL_MASK ((unsigned int)-1) + +#define SDHCI_CQE_INT_ERR_MASK ( \ + SDHCI_INT_ADMA_ERROR | SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | \ + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | \ + SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT) + +#define SDHCI_CQE_INT_MASK (SDHCI_CQE_INT_ERR_MASK | SDHCI_INT_CQE) + +#define SDHCI_AUTO_CMD_STATUS 0x3C +#define SDHCI_AUTO_CMD_TIMEOUT 0x00000002 +#define SDHCI_AUTO_CMD_CRC 0x00000004 +#define SDHCI_AUTO_CMD_END_BIT 0x00000008 +#define SDHCI_AUTO_CMD_INDEX 0x00000010 + +#define SDHCI_HOST_CONTROL2 0x3E +#define SDHCI_CTRL_UHS_MASK 0x0007 +#define SDHCI_CTRL_UHS_SDR12 0x0000 +#define SDHCI_CTRL_UHS_SDR25 0x0001 +#define SDHCI_CTRL_UHS_SDR50 0x0002 +#define SDHCI_CTRL_UHS_SDR104 0x0003 +#define SDHCI_CTRL_UHS_DDR50 0x0004 +#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */ +#define SDHCI_CTRL_VDD_180 0x0008 +#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 +#define SDHCI_CTRL_DRV_TYPE_B 0x0000 +#define SDHCI_CTRL_DRV_TYPE_A 0x0010 +#define SDHCI_CTRL_DRV_TYPE_C 0x0020 +#define SDHCI_CTRL_DRV_TYPE_D 0x0030 +#define SDHCI_CTRL_EXEC_TUNING 0x0040 +#define SDHCI_CTRL_TUNED_CLK 0x0080 +#define SDHCI_CMD23_ENABLE 0x0800 +#define SDHCI_CTRL_V4_MODE 0x1000 +#define SDHCI_CTRL_64BIT_ADDR 0x2000 +#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 + +#define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0) +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8) +#define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8) +#define SDHCI_MAX_BLOCK_MASK 0x00030000 +#define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_8BIT 0x00040000 +#define SDHCI_CAN_DO_ADMA2 0x00080000 +#define SDHCI_CAN_DO_ADMA1 0x00100000 +#define SDHCI_CAN_DO_HISPD 0x00200000 +#define SDHCI_CAN_DO_SDMA 0x00400000 +#define SDHCI_CAN_DO_SUSPEND 0x00800000 +#define SDHCI_CAN_VDD_330 0x01000000 +#define SDHCI_CAN_VDD_300 0x02000000 +#define SDHCI_CAN_VDD_180 0x04000000 +#define SDHCI_CAN_64BIT_V4 0x08000000 +#define SDHCI_CAN_64BIT 0x10000000 + +#define SDHCI_CAPABILITIES_1 0x44 +#define SDHCI_SUPPORT_SDR50 0x00000001 +#define SDHCI_SUPPORT_SDR104 0x00000002 +#define SDHCI_SUPPORT_DDR50 0x00000004 +#define SDHCI_DRIVER_TYPE_A 0x00000010 +#define SDHCI_DRIVER_TYPE_C 0x00000020 +#define SDHCI_DRIVER_TYPE_D 0x00000040 +#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8) +#define SDHCI_USE_SDR50_TUNING 0x00002000 +#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14) +#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16) +#define SDHCI_CAN_DO_ADMA3 0x08000000 +#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ + +#define SDHCI_MAX_CURRENT 0x48 +#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0) +#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0) +#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8) +#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16) +#define SDHCI_MAX_CURRENT_MULTIPLIER 4 + +/* 4C-4F reserved for more max current */ + +#define SDHCI_SET_ACMD12_ERROR 0x50 +#define SDHCI_SET_INT_ERROR 0x52 + +#define SDHCI_ADMA_ERROR 0x54 + +/* 55-57 reserved */ + +#define SDHCI_ADMA_ADDRESS 0x58 +#define SDHCI_ADMA_ADDRESS_HI 0x5C + +/* 60-FB reserved */ + +#define SDHCI_PRESET_FOR_HIGH_SPEED 0x64 +#define SDHCI_PRESET_FOR_SDR12 0x66 +#define SDHCI_PRESET_FOR_SDR25 0x68 +#define SDHCI_PRESET_FOR_SDR50 0x6A +#define SDHCI_PRESET_FOR_SDR104 0x6C +#define SDHCI_PRESET_FOR_DDR50 0x6E +#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ +#define SDHCI_PRESET_DRV_MASK GENMASK(15, 14) +#define SDHCI_PRESET_CLKGEN_SEL BIT(10) +#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0) + +#define SDHCI_SLOT_INT_STATUS 0xFC + +#define SDHCI_HOST_VERSION 0xFE +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 +#define SDHCI_SPEC_100 0 +#define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 +#define SDHCI_SPEC_400 3 +#define SDHCI_SPEC_410 4 +#define SDHCI_SPEC_420 5 + +/* + * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2. + */ +#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) +#define SDHCI_DEFAULT_BOUNDARY_ARG (7) + +/* + * End of controller registers. + */ + +#define MMC_DATA_READ 1 +#define MMC_DATA_WRITE 2 + +#define MMC_CMD_GO_IDLE_STATE 0 +#define MMC_CMD_SEND_OP_COND 1 +#define MMC_CMD_ALL_SEND_CID 2 +#define MMC_CMD_SET_RELATIVE_ADDR 3 +#define MMC_CMD_SET_DSR 4 +#define MMC_CMD_SWITCH 6 +#define MMC_CMD_SELECT_CARD 7 +#define MMC_CMD_SEND_EXT_CSD 8 +#define MMC_CMD_SEND_CSD 9 +#define MMC_CMD_SEND_CID 10 +#define MMC_CMD_STOP_TRANSMISSION 12 +#define MMC_CMD_SEND_STATUS 13 +#define MMC_CMD_SET_BLOCKLEN 16 +#define MMC_CMD_READ_SINGLE_BLOCK 17 +#define MMC_CMD_READ_MULTIPLE_BLOCK 18 +#define MMC_CMD_SEND_TUNING_BLOCK 19 +#define MMC_CMD_SEND_TUNING_BLOCK_HS200 21 +#define MMC_CMD_SET_BLOCK_COUNT 23 +#define MMC_CMD_WRITE_SINGLE_BLOCK 24 +#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 +#define MMC_CMD_ERASE_GROUP_START 35 +#define MMC_CMD_ERASE_GROUP_END 36 +#define MMC_CMD_ERASE 38 +#define MMC_CMD_APP_CMD 55 +#define MMC_CMD_SPI_READ_OCR 58 +#define MMC_CMD_SPI_CRC_ON_OFF 59 +#define MMC_CMD_RES_MAN 62 + +#define MMC_CMD62_ARG1 0xefac62ec +#define MMC_CMD62_ARG2 0xcbaea7 + + +#define SD_CMD_SEND_RELATIVE_ADDR 3 +#define SD_CMD_SWITCH_FUNC 6 +#define SD_CMD_SEND_IF_COND 8 +#define SD_CMD_SWITCH_UHS18V 11 + +#define SD_CMD_APP_SET_BUS_WIDTH 6 +#define SD_CMD_APP_SD_STATUS 13 +#define SD_CMD_ERASE_WR_BLK_START 32 +#define SD_CMD_ERASE_WR_BLK_END 33 +#define SD_CMD_APP_SEND_OP_COND 41 +#define SD_CMD_APP_SEND_SCR 51 + +#define MMC_RSP_PRESENT (1 << 0) +#define MMC_RSP_136 (1 << 1) /* 136 bit response */ +#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ + +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R1b (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \ + MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) +#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + + +#define MMC_STATUS_MASK (~0x0206BF7F) +#define MMC_STATUS_SWITCH_ERROR (1 << 7) +#define MMC_STATUS_RDY_FOR_DATA (1 << 8) +#define MMC_STATUS_CURR_STATE (0xf << 9) +#define MMC_STATUS_ERROR (1 << 19) + +#define MMC_STATE_PRG (7 << 9) +#define MMC_STATE_TRANS (4 << 9) + +#define MMC_BLOCK_SIZE 512 +#define MMC_MAX_BLK_COUNT 65535 + +struct mmc_cmd { + unsigned short cmdidx; + unsigned int resp_type; + unsigned int cmdarg; + unsigned int response[4]; +}; + +struct mmc_data { + union { + char *dest; + const char *src; /* src buffers don't get written to */ + }; + unsigned int flags; + unsigned int blocks; + unsigned int blocksize; +}; + +static inline void sdhci_writel(char *host, unsigned int val, int reg) +{ + writel(val, host + reg); +} + +static inline void sdhci_writew(char *host, unsigned short val, int reg) +{ + writew(val, host + reg); +} + +static inline void sdhci_writeb(char *host, unsigned char val, int reg) +{ + writeb(val, host + reg); +} + +static inline unsigned int sdhci_readl(char *host, int reg) +{ + return readl(host + reg); +} + +static inline unsigned short sdhci_readw(char *host, int reg) +{ + return readw(host + reg); +} + +static inline unsigned char sdhci_readb(char *host, int reg) +{ + return readb(host + reg); +} + +ssize_t +k1_mmc_panic_rtest(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t +k1_mmc_pancic_wrtest(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +int k1_mmc_panic_init_ps(void *data); +#endif diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c index de8cf5d75f34..aef81d1db7c2 100644 --- a/fs/pstore/blk.c +++ b/fs/pstore/blk.c @@ -52,7 +52,7 @@ static long ftrace_size = -1; module_param(ftrace_size, long, 0400); MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes"); -static bool best_effort; +static bool best_effort = 1; module_param(best_effort, bool, 0400); MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)"); @@ -72,6 +72,14 @@ static DEFINE_MUTEX(pstore_blk_lock); static struct file *psblk_file; static struct pstore_device_info *pstore_device_info; +static struct { + struct raw_notifier_head chain; + struct pstore_blk_notifier *pbn; + bool notifier; +} pstore_blk_panic_notifier = { + .chain = RAW_NOTIFIER_INIT(pstore_blk_panic_notifier.chain), +}; + #define check_size(name, alignsize) ({ \ long _##name_ = (name); \ _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \ @@ -94,7 +102,97 @@ static struct pstore_device_info *pstore_device_info; dev->zone.name = _##name_; \ } -static int __register_pstore_device(struct pstore_device_info *dev) +#ifdef CONFIG_MMC_SDHCI_OF_K1X_PANIC +struct bdev_info k1_bdev_info; +#endif + +static int pstore_blk_panic_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + int ret = 0; + struct pstore_blk_notifier *pbn = + container_of(nb, struct pstore_blk_notifier, nb); + + if (pbn) + ret = pbn->notifier_call(action, data); + + return ret; +} + +int register_pstore_blk_panic_notifier(struct pstore_blk_notifier *pbn) +{ + int err = 0; + struct notifier_block *nb; + + mutex_lock(&pstore_blk_lock); + + if (pstore_blk_panic_notifier.notifier) { + pr_info("had register panic\n"); + goto unlock; + } + + nb = &pbn->nb; + nb->notifier_call = pstore_blk_panic_notifier_call; + + err = raw_notifier_chain_register(&pstore_blk_panic_notifier.chain, nb); + if (err) + goto unlock; + + if (pstore_device_info) + err = nb->notifier_call(nb, PSTORE_BLK_BACKEND_PANIC_DRV_REGISTER, + pstore_device_info); + + if (!err) + pstore_blk_panic_notifier.notifier = true; + +unlock: + mutex_unlock(&pstore_blk_lock); + + return err; +} +EXPORT_SYMBOL_GPL(register_pstore_blk_panic_notifier); + +void unregister_pstore_blk_panic_notifier(struct pstore_blk_notifier *pbn) +{ + struct notifier_block *nb = &pbn->nb; + + mutex_lock(&pstore_blk_lock); + + raw_notifier_chain_unregister(&pstore_blk_panic_notifier.chain, nb); + + if (pstore_device_info) + nb->notifier_call(nb, PSTORE_BLK_BACKEND_PANIC_DRV_UNREGISTER, + pstore_device_info); + + pstore_blk_panic_notifier.notifier = false; + + mutex_unlock(&pstore_blk_lock); +} +EXPORT_SYMBOL_GPL(unregister_pstore_blk_panic_notifier); + +static int pstore_blk_panic_notifier_init_call(struct pstore_device_info *pdi) +{ + return raw_notifier_call_chain(&pstore_blk_panic_notifier.chain, + PSTORE_BLK_BACKEND_REGISTER, pdi); +} + +static int pstore_blk_panic_notifier_exit_call(struct pstore_device_info *pdi) +{ + return raw_notifier_call_chain(&pstore_blk_panic_notifier.chain, + PSTORE_BLK_BACKEND_UNREGISTER, pdi); +} + + +/** + * register_pstore_device() - register device to pstore/blk + * + * @dev: non-block device information + * + * Return: + * * 0 - OK + * * Others - something error. + */ +int __register_pstore_device(struct pstore_device_info *dev) { int ret; @@ -288,8 +386,10 @@ static int __init __best_effort_init(void) int ret; /* No best-effort mode requested. */ - if (!best_effort) + if (!best_effort) { + pr_err("best_effort=N\n"); return 0; + } /* Reject an empty blkdev. */ if (!blkdev[0]) { @@ -306,12 +406,29 @@ static int __init __best_effort_init(void) ret = __register_pstore_blk(best_effort_dev, early_boot_devpath(blkdev)); +#ifdef CONFIG_MMC_SDHCI_OF_K1X_PANIC + k1_bdev_info.bdev = blkdev_get_by_path(early_boot_devpath(blkdev), FMODE_READ, NULL, NULL); + if (IS_ERR(k1_bdev_info.bdev)) { + pr_err("fail to access bdev %s errno %ld\n", blkdev, + PTR_ERR(k1_bdev_info.bdev)); + return -EACCES; + } + + k1_bdev_info.nr_sects = bdev_nr_sectors(k1_bdev_info.bdev); + k1_bdev_info.start_sect = k1_bdev_info.bdev->bd_start_sect; +#endif if (ret) kfree(best_effort_dev); - else - pr_info("attached %s (%lu) (no dedicated panic_write!)\n", - blkdev, best_effort_dev->zone.total_size); + else { + if (pstore_blk_panic_notifier_init_call(best_effort_dev) == NOTIFY_OK) + pr_info("attached %s (%lu) (dedicated panic_write!)\n", + blkdev, best_effort_dev->zone.total_size); + else + pr_info("attached %s (%lu) (no dedicated panic_write!)\n", + blkdev, best_effort_dev->zone.total_size); + } + /* No best-effort mode requested. */ return ret; } @@ -325,7 +442,7 @@ static void __exit __best_effort_exit(void) */ if (psblk_file) { struct pstore_device_info *dev = pstore_device_info; - + pstore_blk_panic_notifier_exit_call(dev); __unregister_pstore_device(dev); kfree(dev); fput(psblk_file); diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h index 924ca07aafbd..4817015ab2c6 100644 --- a/include/linux/pstore_blk.h +++ b/include/linux/pstore_blk.h @@ -43,6 +43,14 @@ struct pstore_blk_config { unsigned long ftrace_size; }; +#ifdef CONFIG_MMC_SDHCI_OF_K1X_PANIC +struct bdev_info { + struct block_device *bdev; + dev_t devt; + sector_t nr_sects; + sector_t start_sect; +}; +#endif /** * pstore_blk_get_config - get a copy of the pstore_blk backend configuration * @@ -51,5 +59,23 @@ struct pstore_blk_config { * Failure returns negative error code, and success returns 0. */ int pstore_blk_get_config(struct pstore_blk_config *info); +enum pstore_blk_notifier_type { + PSTORE_BLK_BACKEND_REGISTER = 1, + PSTORE_BLK_BACKEND_PANIC_DRV_REGISTER, + PSTORE_BLK_BACKEND_UNREGISTER, + PSTORE_BLK_BACKEND_PANIC_DRV_UNREGISTER, +}; +typedef int (*pstore_blk_notifier_fn_t)(enum pstore_blk_notifier_type type, + struct pstore_device_info *dev); +struct pstore_blk_notifier { + struct notifier_block nb; + pstore_blk_notifier_fn_t notifier_call; +}; + +extern int register_pstore_device(struct pstore_device_info *dev); +extern void unregister_pstore_device(struct pstore_device_info *dev); + +extern int register_pstore_blk_panic_notifier(struct pstore_blk_notifier *pbn); +extern void unregister_pstore_blk_panic_notifier(struct pstore_blk_notifier *nb); #endif