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 <juan.li@spacemit.com>
This commit is contained in:
@@ -277,6 +277,18 @@ config MMC_SDHCI_OF_K1X
|
|||||||
|
|
||||||
If unsure, say N.
|
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
|
config MMC_SDHCI_CADENCE
|
||||||
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
|
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
|||||||
@@ -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_SPARX5) += sdhci-of-sparx5.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_OF_K1PRO) += sdhci-of-k1pro.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) += 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_BCM_KONA) += sdhci-bcm-kona.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||||
|
|||||||
599
drivers/mmc/host/sdhci-k1-panic.c
Normal file
599
drivers/mmc/host/sdhci-k1-panic.c
Normal file
@@ -0,0 +1,599 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Driver for Spacemit Mobile Storage Host Controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 Spacemit
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/pstore_blk.h>
|
||||||
|
#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");
|
||||||
420
drivers/mmc/host/sdhci-k1-panic.h
Normal file
420
drivers/mmc/host/sdhci-k1-panic.h
Normal file
@@ -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
|
||||||
131
fs/pstore/blk.c
131
fs/pstore/blk.c
@@ -52,7 +52,7 @@ static long ftrace_size = -1;
|
|||||||
module_param(ftrace_size, long, 0400);
|
module_param(ftrace_size, long, 0400);
|
||||||
MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes");
|
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_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)");
|
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 file *psblk_file;
|
||||||
static struct pstore_device_info *pstore_device_info;
|
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) ({ \
|
#define check_size(name, alignsize) ({ \
|
||||||
long _##name_ = (name); \
|
long _##name_ = (name); \
|
||||||
_##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
|
_##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
|
||||||
@@ -94,7 +102,97 @@ static struct pstore_device_info *pstore_device_info;
|
|||||||
dev->zone.name = _##name_; \
|
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;
|
int ret;
|
||||||
|
|
||||||
@@ -288,8 +386,10 @@ static int __init __best_effort_init(void)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* No best-effort mode requested. */
|
/* No best-effort mode requested. */
|
||||||
if (!best_effort)
|
if (!best_effort) {
|
||||||
|
pr_err("best_effort=N\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reject an empty blkdev. */
|
/* Reject an empty blkdev. */
|
||||||
if (!blkdev[0]) {
|
if (!blkdev[0]) {
|
||||||
@@ -306,12 +406,29 @@ static int __init __best_effort_init(void)
|
|||||||
|
|
||||||
ret = __register_pstore_blk(best_effort_dev,
|
ret = __register_pstore_blk(best_effort_dev,
|
||||||
early_boot_devpath(blkdev));
|
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)
|
if (ret)
|
||||||
kfree(best_effort_dev);
|
kfree(best_effort_dev);
|
||||||
else
|
else {
|
||||||
pr_info("attached %s (%lu) (no dedicated panic_write!)\n",
|
if (pstore_blk_panic_notifier_init_call(best_effort_dev) == NOTIFY_OK)
|
||||||
blkdev, best_effort_dev->zone.total_size);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +442,7 @@ static void __exit __best_effort_exit(void)
|
|||||||
*/
|
*/
|
||||||
if (psblk_file) {
|
if (psblk_file) {
|
||||||
struct pstore_device_info *dev = pstore_device_info;
|
struct pstore_device_info *dev = pstore_device_info;
|
||||||
|
pstore_blk_panic_notifier_exit_call(dev);
|
||||||
__unregister_pstore_device(dev);
|
__unregister_pstore_device(dev);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
fput(psblk_file);
|
fput(psblk_file);
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ struct pstore_blk_config {
|
|||||||
unsigned long ftrace_size;
|
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
|
* 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.
|
* Failure returns negative error code, and success returns 0.
|
||||||
*/
|
*/
|
||||||
int pstore_blk_get_config(struct pstore_blk_config *info);
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user