@@ -9,6 +9,11 @@
#include "t7xx_port_ap_msg.h"
#include "t7xx_port_flash_dump.h"
+static struct t7xx_dump_region_info t7xx_dump_region_infos[] = {
+ [T7XX_MRDUMP_INDEX] = {"mr_dump", T7XX_MRDUMP_SIZE},
+ [T7XX_LKDUMP_INDEX] = {"lk_dump", T7XX_LKDUMP_SIZE},
+};
+
static int t7xx_flash_dump_port_read(struct t7xx_port *port, char *buf, size_t count)
{
struct sk_buff *skb;
@@ -130,6 +135,151 @@ static int t7xx_flash_dump_fb_raw_command(char *cmd, struct t7xx_port *port, cha
return ret;
}
+static int t7xx_flash_dump_fb_cmd_send(struct t7xx_port *port, char *cmd)
+{
+ int len = strlen(cmd);
+ int ret;
+
+ ret = t7xx_flash_dump_port_write(port, cmd, len);
+ if (ret == len)
+ return 0;
+
+ return ret;
+}
+
+static int t7xx_flash_dump_fb_get_core(struct t7xx_port *port)
+{
+ u32 mrd_mb = T7XX_MRDUMP_SIZE / (1024 * 1024);
+ struct t7xx_flash_dump *flash_dump = port->t7xx_dev->flash_dump;
+ char mcmd[T7XX_FB_MCMD_SIZE + 1];
+ size_t offset_dlen = 0;
+ int clen, dlen, ret;
+
+ flash_dump->regions[T7XX_MRDUMP_INDEX].buf =
+ vmalloc(flash_dump->regions[T7XX_MRDUMP_INDEX].info->size);
+ if (!flash_dump->regions[T7XX_MRDUMP_INDEX].buf)
+ return -ENOMEM;
+
+ set_bit(T7XX_MRDUMP_STATUS, &flash_dump->status);
+ ret = t7xx_flash_dump_fb_raw_command(T7XX_FB_CMD_OEM_MRDUMP, port, NULL);
+ if (ret) {
+ dev_err(port->dev, "%s command failed\n", T7XX_FB_CMD_OEM_MRDUMP);
+ goto free_mem;
+ }
+
+ while (flash_dump->regions[T7XX_MRDUMP_INDEX].info->size > offset_dlen) {
+ clen = t7xx_flash_dump_port_read(port, mcmd, sizeof(mcmd) - 1);
+ if (clen <= 0)
+ goto free_mem;
+
+ mcmd[clen] = '\0';
+ if (!strcmp(mcmd, T7XX_FB_CMD_RTS)) {
+ memset(mcmd, 0, sizeof(mcmd));
+ ret = t7xx_flash_dump_fb_cmd_send(port, T7XX_FB_CMD_CTS);
+ if (ret < 0) {
+ dev_err(port->dev, "write for _CTS failed:%zu\n",
+ strlen(T7XX_FB_CMD_CTS));
+ goto free_mem;
+ }
+
+ dlen = t7xx_flash_dump_port_read(port,
+ flash_dump->regions[T7XX_MRDUMP_INDEX].buf
+ + offset_dlen, T7XX_FB_MDATA_SIZE);
+ if (dlen <= 0) {
+ dev_err(port->dev, "read data error(%d)\n", dlen);
+ ret = dlen;
+ goto free_mem;
+ }
+ offset_dlen += dlen;
+
+ ret = t7xx_flash_dump_fb_cmd_send(port, T7XX_FB_CMD_FIN);
+ if (ret < 0) {
+ dev_err(port->dev, "_FIN failed, (Read %05zu:%05zu)\n",
+ strlen(T7XX_FB_CMD_FIN), offset_dlen);
+ goto free_mem;
+ }
+ continue;
+ } else if (!strcmp(mcmd, T7XX_FB_RESP_MRDUMP_DONE)) {
+ dev_dbg(port->dev, "%s! size:%zd\n", T7XX_FB_RESP_MRDUMP_DONE, offset_dlen);
+ clear_bit(T7XX_MRDUMP_STATUS, &flash_dump->status);
+ return 0;
+ }
+ dev_err(port->dev, "getcore protocol error (read len %05d, response %s)\n",
+ clen, mcmd);
+ ret = -EPROTO;
+ goto free_mem;
+ }
+
+ dev_err(port->dev, "mrdump exceeds %uMB size. Discarded!\n", mrd_mb);
+
+free_mem:
+ vfree(flash_dump->regions[T7XX_MRDUMP_INDEX].buf);
+ clear_bit(T7XX_MRDUMP_STATUS, &flash_dump->status);
+ return ret;
+}
+
+static int t7xx_flash_dump_fb_dump_log(struct t7xx_port *port)
+{
+ struct t7xx_flash_dump *flash_dump = port->t7xx_dev->flash_dump;
+ struct t7xx_dump_region *lkdump_region;
+ char rsp[T7XX_FB_RESPONSE_SIZE];
+ int datasize = 0, ret;
+ size_t offset = 0;
+
+ if (flash_dump->status != T7XX_DEVLINK_IDLE) {
+ dev_err(&flash_dump->t7xx_dev->pdev->dev, "Modem is busy!\n");
+ return -EBUSY;
+ }
+
+ set_bit(T7XX_LKDUMP_STATUS, &flash_dump->status);
+ ret = t7xx_flash_dump_fb_raw_command(T7XX_FB_CMD_OEM_LKDUMP, port, rsp);
+ if (ret) {
+ dev_err(port->dev, "%s command returns failure\n", T7XX_FB_CMD_OEM_LKDUMP);
+ goto err_clear_bit;
+ }
+
+ ret = kstrtoint(rsp, 16, &datasize);
+ if (ret) {
+ dev_err(port->dev, "bad value\n");
+ goto err_clear_bit;
+ }
+
+ lkdump_region = &flash_dump->regions[T7XX_LKDUMP_INDEX];
+ if (datasize > lkdump_region->info->size) {
+ dev_err(port->dev, "lkdump size is more than %dKB. Discarded!\n",
+ T7XX_LKDUMP_SIZE / 1024);
+ ret = -EFBIG;
+ goto err_clear_bit;
+ }
+
+ lkdump_region->buf = vmalloc(lkdump_region->info->size);
+ if (!lkdump_region->buf) {
+ ret = -ENOMEM;
+ goto err_clear_bit;
+ }
+
+ while (datasize > 0) {
+ int dlen = t7xx_flash_dump_port_read(port, lkdump_region->buf + offset, datasize);
+
+ if (dlen <= 0) {
+ dev_err(port->dev, "lkdump read error ret = %d\n", dlen);
+ ret = dlen;
+ goto err_clear_bit;
+ }
+
+ datasize -= dlen;
+ offset += dlen;
+ }
+
+ dev_dbg(port->dev, "LKDUMP DONE! size:%zd\n", offset);
+ clear_bit(T7XX_LKDUMP_STATUS, &flash_dump->status);
+ return t7xx_flash_dump_fb_handle_response(port, NULL);
+
+err_clear_bit:
+ clear_bit(T7XX_LKDUMP_STATUS, &flash_dump->status);
+ return ret;
+}
+
static int t7xx_flash_dump_fb_download_command(struct t7xx_port *port, size_t size)
{
char download_command[T7XX_FB_COMMAND_SIZE];
@@ -355,6 +505,67 @@ static const struct devlink_ops devlink_flash_ops = {
.reload_up = t7xx_devlink_reload_up,
};
+static int t7xx_flash_dump_region_snapshot(struct devlink *dl, const struct devlink_region_ops *ops,
+ struct netlink_ext_ack *extack, u8 **data)
+{
+ struct t7xx_flash_dump *flash_dump = devlink_priv(dl);
+ struct t7xx_dump_region *region = ops->priv;
+ struct t7xx_port *port = flash_dump->port;
+ u8 *snapshot_mem;
+
+ if (flash_dump->status != T7XX_DEVLINK_IDLE)
+ return -EBUSY;
+
+ if (!strncmp(ops->name, "mr_dump", strlen("mr_dump"))) {
+ snapshot_mem = vmalloc(region->info->size);
+ memcpy(snapshot_mem, region->buf, region->info->size);
+ *data = snapshot_mem;
+ } else if (!strncmp(ops->name, "lk_dump", strlen("lk_dump"))) {
+ int ret;
+
+ ret = t7xx_flash_dump_fb_dump_log(port);
+ if (ret)
+ return ret;
+
+ *data = region->buf;
+ }
+
+ return 0;
+}
+
+static_assert(ARRAY_SIZE(t7xx_dump_region_infos) ==
+ ARRAY_SIZE(((struct t7xx_flash_dump *)NULL)->regions));
+
+/* To create regions for dump files */
+static int t7xx_flash_dump_create_regions(struct t7xx_flash_dump *flash_dump)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(t7xx_dump_region_infos); i++) {
+ flash_dump->regions[i].info = &t7xx_dump_region_infos[i];
+ flash_dump->regions[i].ops.name = flash_dump->regions[i].info->name;
+ flash_dump->regions[i].ops.snapshot = t7xx_flash_dump_region_snapshot;
+ flash_dump->regions[i].ops.destructor = vfree;
+ flash_dump->regions[i].dlreg = devlink_region_create(flash_dump->ctx,
+ &flash_dump->regions[i].ops,
+ T7XX_MAX_SNAPSHOTS,
+ t7xx_dump_region_infos[i].size
+ );
+ if (IS_ERR(flash_dump->regions[i].dlreg)) {
+ ret = PTR_ERR(flash_dump->regions[i].dlreg);
+ dev_err(flash_dump->port->dev, "create region failed, err %d\n", ret);
+ while (i >= 0)
+ devlink_region_destroy(flash_dump->regions[i--].dlreg);
+
+ return ret;
+ }
+
+ flash_dump->regions[i].ops.priv = &flash_dump->regions[i];
+ }
+
+ return 0;
+}
+
int t7xx_devlink_register(struct t7xx_pci_dev *t7xx_dev)
{
union devlink_param_value value;
@@ -379,6 +590,14 @@ int t7xx_devlink_register(struct t7xx_pci_dev *t7xx_dev)
return 0;
}
+static void t7xx_flash_dump_work(struct work_struct *work)
+{
+ struct t7xx_flash_dump *flash_dump;
+
+ flash_dump = container_of(work, struct t7xx_flash_dump, ws);
+ t7xx_flash_dump_fb_get_core(flash_dump->port);
+}
+
void t7xx_devlink_unregister(struct t7xx_pci_dev *t7xx_dev)
{
struct devlink *dl_ctx = t7xx_dev->flash_dump->ctx;
@@ -399,29 +618,64 @@ void t7xx_devlink_unregister(struct t7xx_pci_dev *t7xx_dev)
static int t7xx_port_flash_dump_init(struct t7xx_port *port)
{
struct t7xx_flash_dump *flash_dump = port->t7xx_dev->flash_dump;
+ struct workqueue_struct *flash_dump_wq;
+ int rc;
+
+ flash_dump_wq = create_workqueue("t7xx_flash_dump");
+ if (!flash_dump_wq) {
+ dev_err(port->dev, "create_workqueue failed\n");
+ return -ENODATA;
+ }
+ INIT_WORK(&flash_dump->ws, t7xx_flash_dump_work);
port->rx_length_th = T7XX_MAX_QUEUE_LENGTH;
flash_dump->mode = T7XX_NORMAL_MODE;
flash_dump->status = T7XX_DEVLINK_IDLE;
+ flash_dump->wq = flash_dump_wq;
flash_dump->port = port;
+ rc = t7xx_flash_dump_create_regions(flash_dump);
+ if (rc) {
+ destroy_workqueue(flash_dump->wq);
+ dev_err(port->dev, "devlink region creation failed, rc %d\n", rc);
+ return -ENOMEM;
+ }
+
return 0;
}
static void t7xx_port_flash_dump_uninit(struct t7xx_port *port)
{
struct t7xx_flash_dump *flash_dump = port->t7xx_dev->flash_dump;
+ int i;
+
+ vfree(flash_dump->regions[T7XX_MRDUMP_INDEX].buf);
flash_dump->mode = T7XX_NORMAL_MODE;
+ destroy_workqueue(flash_dump->wq);
+
+ for (i = 0; i < ARRAY_SIZE(t7xx_dump_region_infos); ++i)
+ devlink_region_destroy(flash_dump->regions[i].dlreg);
skb_queue_purge(&port->rx_skb_list);
}
+static int t7xx_flash_dump_enable_chl(struct t7xx_port *port)
+{
+ struct t7xx_flash_dump *flash_dump = port->t7xx_dev->flash_dump;
+
+ t7xx_port_enable_chl(port);
+ if (flash_dump->mode == T7XX_FB_DUMP_MODE)
+ queue_work(flash_dump->wq, &flash_dump->ws);
+
+ return 0;
+}
+
struct port_ops flash_dump_port_ops = {
.init = &t7xx_port_flash_dump_init,
.recv_skb = &t7xx_port_enqueue_skb,
.uninit = &t7xx_port_flash_dump_uninit,
- .enable_chl = &t7xx_port_enable_chl,
+ .enable_chl = &t7xx_flash_dump_enable_chl,
.disable_chl = &t7xx_port_disable_chl,
};
@@ -12,28 +12,67 @@
#define T7XX_MAX_QUEUE_LENGTH 32
#define T7XX_FB_COMMAND_SIZE 64
#define T7XX_FB_RESPONSE_SIZE 512
+#define T7XX_FB_MCMD_SIZE 64
+#define T7XX_FB_MDATA_SIZE 1024
#define T7XX_FB_RESP_COUNT 30
+#define T7XX_FB_EVENT_SIZE 50
+
+#define T7XX_MAX_SNAPSHOTS 1
+#define T7XX_MRDUMP_SIZE (160 * 1024 * 1024)
+#define T7XX_LKDUMP_SIZE (256 * 1024)
+#define T7XX_TOTAL_REGIONS 2
+
#define T7XX_FLASH_STATUS 0
+#define T7XX_MRDUMP_STATUS 1
+#define T7XX_LKDUMP_STATUS 2
#define T7XX_GET_INFO 3
#define T7XX_DEVLINK_IDLE 0
#define T7XX_NORMAL_MODE 0
#define T7XX_FB_DL_MODE 1
+#define T7XX_FB_DUMP_MODE 2
+#define T7XX_FB_CMD_RTS "_RTS"
+#define T7XX_FB_CMD_CTS "_CTS"
+#define T7XX_FB_CMD_FIN "_FIN"
+#define T7XX_FB_CMD_OEM_MRDUMP "oem mrdump"
+#define T7XX_FB_CMD_OEM_LKDUMP "oem dump_pllk_log"
#define T7XX_FB_CMD_DOWNLOAD "download"
#define T7XX_FB_CMD_FLASH "flash"
#define T7XX_FB_CMD_REBOOT "reboot"
+#define T7XX_FB_RESP_MRDUMP_DONE "MRDUMP08_DONE"
#define T7XX_FB_RESP_OKAY "OKAY"
#define T7XX_FB_RESP_FAIL "FAIL"
#define T7XX_FB_RESP_DATA "DATA"
#define T7XX_FB_RESP_INFO "INFO"
#define T7XX_FB_CMD_GET_VER "get_version"
+/* Internal region indexes */
+enum t7xx_regions {
+ T7XX_MRDUMP_INDEX,
+ T7XX_LKDUMP_INDEX,
+};
+
+struct t7xx_dump_region_info {
+ const char *name;
+ size_t size;
+};
+
+struct t7xx_dump_region {
+ struct t7xx_dump_region_info *info;
+ struct devlink_region_ops ops;
+ struct devlink_region *dlreg;
+ void *buf;
+};
+
struct t7xx_flash_dump {
struct t7xx_pci_dev *t7xx_dev;
struct t7xx_port *port;
struct devlink *ctx;
+ struct t7xx_dump_region regions[T7XX_TOTAL_REGIONS];
+ struct workqueue_struct *wq;
+ struct work_struct ws;
unsigned long status;
u8 mode;
};
@@ -244,6 +244,8 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int
if (lk_event == LK_EVENT_CREATE_POST_DL_PORT)
md->t7xx_dev->flash_dump->mode = T7XX_FB_DL_MODE;
+ else
+ md->t7xx_dev->flash_dump->mode = T7XX_FB_DUMP_MODE;
port->port_conf->ops->enable_chl(port);
t7xx_cldma_start(md_ctrl);