diff --git a/drivers/mtd/devices/tegra_hv_mtd.c b/drivers/mtd/devices/tegra_hv_mtd.c index 14aed12b..db4184ed 100644 --- a/drivers/mtd/devices/tegra_hv_mtd.c +++ b/drivers/mtd/devices/tegra_hv_mtd.c @@ -470,9 +470,96 @@ static ssize_t vmtd_phys_base_show(struct device *dev, } static DEVICE_ATTR(phys_base, 0444, vmtd_phys_base_show, NULL); +static ssize_t manufacturer_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vmtd_dev *vmtddev = dev_get_drvdata(dev); + + return sprintf(buf, "0x%x\n", vmtddev->config.mtd_config.manufacturer_id); +} +static DEVICE_ATTR_RO(manufacturer_id); + +static ssize_t device_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vmtd_dev *vmtddev = dev_get_drvdata(dev); + + return sprintf(buf, "0x%x\n", vmtddev->config.mtd_config.device_id); +} +static DEVICE_ATTR_RO(device_id); + +static ssize_t qspi_device_size_bytes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vmtd_dev *vmtddev = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", (unsigned int)vmtddev->config.mtd_config.qspi_device_size_bytes); +} +static DEVICE_ATTR_RO(qspi_device_size_bytes); + +static ssize_t ecc_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vmtd_dev *vmtddev = dev_get_drvdata(dev); + struct vs_request *vs_req; + int32_t ret; + struct vs_mtd_ecc_response ecc_response; + + vs_req = (struct vs_request *)vmtddev->cmd_frame; + vs_req->type = VS_DATA_REQ; + vs_req->mtddev_req.req_op = VS_MTD_ECC; + vs_req->mtddev_req.mtd_req.offset = 0; + vs_req->mtddev_req.mtd_req.size = 0; + vs_req->mtddev_req.mtd_req.data_offset = 0; + vs_req->req_id = 0; + + ret = vmtd_process_request(vmtddev, vs_req); + if (ret != 0) { + dev_err(vmtddev->device, "Read ECC Failed\n"); + return snprintf(buf, PAGE_SIZE, "Error reading ECC status\n"); + } + + ecc_response = vs_req->mtddev_resp.ecc_resp; + vs_req->mtddev_req.stored_ecc_status = ecc_response.status; + vs_req->mtddev_req.stored_failed_chunk_addr = ecc_response.failed_chunk_addr; + + switch (ecc_response.status) { + case ECC_NO_ERROR: + return sprintf(buf, "0x%x\n", ECC_NO_ERROR); + case ECC_ONE_BIT_CORRECTED: + return sprintf(buf, "0x%x\n", ECC_ONE_BIT_CORRECTED); + case ECC_TWO_BIT_ERROR: + return sprintf(buf, "0x%x\n", ECC_TWO_BIT_ERROR); + case ECC_DISABLED: + return sprintf(buf, "0x%x\n", ECC_DISABLED); + default: + return snprintf(buf, PAGE_SIZE, "ECC Status: Unknown error\n"); + } +} +static DEVICE_ATTR_RO(ecc_status); + +static ssize_t failure_chunk_addr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vmtd_dev *vmtddev = dev_get_drvdata(dev); + struct vs_request *vs_req = (struct vs_request *)vmtddev->cmd_frame; + + if (vs_req->mtddev_req.stored_ecc_status == ECC_NO_ERROR) + return sprintf(buf, "0x0\n"); + else + return snprintf(buf, PAGE_SIZE, "0x%x\n", vs_req->mtddev_req.stored_failed_chunk_addr); + +} +static DEVICE_ATTR_RO(failure_chunk_addr); + static const struct attribute *vmtd_storage_attrs[] = { &dev_attr_phys_dev.attr, &dev_attr_phys_base.attr, + &dev_attr_manufacturer_id.attr, + &dev_attr_device_id.attr, + &dev_attr_qspi_device_size_bytes.attr, + &dev_attr_ecc_status.attr, + &dev_attr_failure_chunk_addr.attr, NULL }; diff --git a/include/tegra_virt_storage_spec.h b/include/tegra_virt_storage_spec.h index 04af5a91..8e20ccac 100644 --- a/include/tegra_virt_storage_spec.h +++ b/include/tegra_virt_storage_spec.h @@ -28,10 +28,52 @@ enum mtd_cmd_op { VS_MTD_WRITE = 2, VS_MTD_ERASE = 3, VS_MTD_IOCTL = 4, + VS_MTD_ECC = 5, VS_MTD_INVAL_REQ = 32, VS_UNKNOWN_MTD_CMD = 0xffffffff, }; +typedef enum { + /** + * @brief no error detected for ECC 0x800 register. + */ + ECC_NO_ERROR = 0, + + /** + * @brief one bit error corrected for ECC 0x800 register. + */ + ECC_ONE_BIT_CORRECTED = 1, + + /** + * @brief two bit error detected for ECC 0x800 register. + */ + ECC_TWO_BIT_ERROR = 2, + + /** + * @brief ECC is disabled 0x800 register. + */ + ECC_DISABLED = 3, +} qspi_ecc_err; + +/** + * @brief QSPI ecc status + * A structure contains information related to ecc + * read values + */ +typedef struct NvQspi_ecc_status { + /** + * @brief read ECC error status. + */ + qspi_ecc_err status; + + /** + * @brief if 1 or 2 bit error, this will + * hold valid failed chunk address. + */ + uint32_t failed_chunk_addr; +} NvQspi_ecc_status; + + /* MTD device request Operation type features supported */ #define VS_MTD_READ_OP_F (1 << VS_MTD_READ) #define VS_MTD_WRITE_OP_F (1 << VS_MTD_WRITE) @@ -118,6 +160,9 @@ struct vs_mtddev_request { struct vs_mtd_request mtd_req; struct vs_ioctl_request ioctl_req; }; + uint32_t stored_ecc_status; /* Field to store ECC status */ + + uint32_t stored_failed_chunk_addr; /* field to store failed chunk address */ }; struct vs_blk_response { @@ -142,10 +187,30 @@ struct vs_blkdev_response { }; }; +struct vs_mtd_ecc_response { + /** Status code returned as part of response message of MTD requests. + * Status code value is "0" for success and "< 0" for failure + * This value of this member is ignored in the MTD request message. + */ + int32_t status; + + /** + * Read ECC error status. + */ + uint32_t ecc_status; + + /** + * If 1 or 2 bit error, this will + * hold valid failed chunk address. + */ + uint32_t failed_chunk_addr; +}; + struct vs_mtddev_response { union { struct vs_mtd_response mtd_resp; struct vs_ioctl_response ioctl_resp; + struct vs_mtd_ecc_response ecc_resp; }; }; @@ -176,6 +241,9 @@ struct vs_mtd_dev_config { device*/ uint32_t req_ops_supported; /* Allowed operations by requests */ uint64_t size; /* Total number of bytes */ + uint32_t manufacturer_id; + uint32_t device_id; + uint32_t qspi_device_size_bytes; }; /* Physical device types */ @@ -209,6 +277,7 @@ struct vs_config_info { uint32_t storage_type; uint32_t priority; uint8_t speed_mode[SPEED_MODE_MAX_LEN]; + }; struct vs_request {