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:
lijuan
2024-11-25 15:53:05 +08:00
committed by 张猛
parent 305b279f1b
commit c0e6859eb0
6 changed files with 1182 additions and 7 deletions

View File

@@ -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

View File

@@ -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

View 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");

View 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

View File

@@ -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 {
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);

View File

@@ -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