From 61df89e001cbb30e16b63e2d260cfd5126ae7cc9 Mon Sep 17 00:00:00 2001 From: Raviv Shvili Date: Tue, 1 Apr 2014 22:20:15 +0300 Subject: [PATCH] scsi: ufs: seperate device and host quirks Currently we use the host quirks mechanism in order to handle both device and host controller quirks. In order to support many UFS devices we should separate handling the device quirks from the host controller's. Change-Id: I6c23be42ff78689f9aad4d5c0c9f0a678bfb5c14 Signed-off-by: Raviv Shvili [venkatg@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Venkat Gopalakrishnan --- drivers/scsi/ufs/Makefile | 2 +- drivers/scsi/ufs/ufs.h | 30 ++++++++++++++ drivers/scsi/ufs/ufs_quirks.c | 78 +++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs_quirks.h | 62 ++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.c | 2 + drivers/scsi/ufs/ufshcd.h | 4 ++ 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 drivers/scsi/ufs/ufs_quirks.c create mode 100644 drivers/scsi/ufs/ufs_quirks.h diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index 9cddfaaa60a3..876877e602f5 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -1,6 +1,6 @@ # UFSHCD makefile obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o -obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o +obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o ufs_quirks.o obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o obj-$(CONFIG_SCSI_UFS_TEST) += ufs_test.o diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index a597a34b689f..741e1bbd365e 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -178,6 +178,36 @@ enum unit_desc_param { UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, }; +/* Device descriptor parameters offsets in bytes*/ +enum device_desc_param { + DEVICE_DESC_PARAM_LEN = 0x0, + DEVICE_DESC_PARAM_TYPE = 0x1, + DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2, + DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3, + DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4, + DEVICE_DESC_PARAM_PRTCL = 0x5, + DEVICE_DESC_PARAM_NUM_LU = 0x6, + DEVICE_DESC_PARAM_NUM_WLU = 0x7, + DEVICE_DESC_PARAM_BOOT_ENBL = 0x8, + DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9, + DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA, + DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB, + DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC, + DEVICE_DESC_PARAM_SEC_LU = 0xD, + DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE, + DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF, + DEVICE_DESC_PARAM_SPEC_VER = 0x10, + DEVICE_DESC_PARAM_MANF_DATE = 0x12, + DEVICE_DESC_PARAM_MANF_NAME = 0x14, + DEVICE_DESC_PARAM_PRDCT_NAME = 0x15, + DEVICE_DESC_PARAM_SN = 0x16, + DEVICE_DESC_PARAM_OEM_ID = 0x17, + DEVICE_DESC_PARAM_MANF_ID = 0x18, + DEVICE_DESC_PARAM_UD_OFFSET = 0x1A, + DEVICE_DESC_PARAM_UD_LEN = 0x1B, + DEVICE_DESC_PARAM_RTT_CAP = 0x1C, + DEVICE_DESC_PARAM_FRQ_RTC = 0x1D, +}; /* * Logical Unit Write Protect * 00h: LU not write protected diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c new file mode 100644 index 000000000000..e1e8f371a145 --- /dev/null +++ b/drivers/scsi/ufs/ufs_quirks.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ufshcd.h" +#include "ufs_quirks.h" + + +static struct ufs_card_fix ufs_fixups[] = { + /* UFS cards deviations table */ + END_FIX +}; + +int ufs_get_device_info(struct ufs_hba *hba, struct ufs_card_info *card_data) +{ + int err; + u8 model_index; + u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE]; + u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE]; + + err = ufshcd_read_device_desc(hba, desc_buf, + QUERY_DESC_DEVICE_MAX_SIZE); + if (err) + goto out; + + card_data->vendor = desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]; + model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; + + memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE); + err = ufshcd_read_string_desc(hba, model_index, str_desc_buf, + QUERY_DESC_STRING_MAX_SIZE, ASCII_STD); + if (err) + goto out; + + strlcpy(card_data->model, str_desc_buf, MAX_MODEL_LEN + 1); +out: + return err; +} + +void ufs_advertise_fixup_device(struct ufs_hba *hba) +{ + int err; + struct ufs_card_fix *f; + struct ufs_card_info card_data; + + card_data.vendor = 0; + card_data.model = kmalloc(MAX_MODEL_LEN + 1, GFP_KERNEL); + if (!card_data.model) + goto out; + + /* get device data*/ + err = ufs_get_device_info(hba, &card_data); + if (err) { + dev_err(hba->dev, "%s: Failed getting device info\n", __func__); + goto out; + } + + for (f = ufs_fixups; f->quirk; f++) { + /* if same vendor */ + if (((f->card.vendor == card_data.vendor) || + (f->card.vendor == UFS_ANY_VENDOR)) && + /* and same model */ + (STR_PRFX_EQUAL(f->card.model, card_data.model) || + !strcmp(f->card.model, UFS_ANY_MODEL))) + /* update quirks */ + hba->dev_quirks |= f->quirk; + } +out: + kfree(card_data.model); +} diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h new file mode 100644 index 000000000000..836387ea6d9b --- /dev/null +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _UFS_QUIRKS_H_ +#define _UFS_QUIRKS_H_ + +/* return true if s1 is a prefix of s2 */ +#define STR_PRFX_EQUAL(s1, s2) !strncmp(s1, s2, strlen(s1)) + +#define UFS_ANY_VENDOR -1 +#define UFS_ANY_MODEL "ANY_MODEL" + +#define MAX_MODEL_LEN 16 + +#define UFS_VENDOR_TOSHIBA 0x98 +/* UFS TOSHIBA MODELS */ +#define UFS_MODEL_TOSHIBA_32GB "THGLF2G8D4KBADR" +#define UFS_MODEL_TOSHIBA_64GB "THGLF2G9D8KBADG" + +/** + * ufs_card_info - ufs device details + * @vendor: card details + * @model: card model + */ +struct ufs_card_info { + unsigned int vendor; + char *model; +}; + +/** + * ufs_card_fix - ufs device quirk info + * @card: ufs card details + * @quirk: device quirk + */ +struct ufs_card_fix { + struct ufs_card_info card; + unsigned int quirk; +}; + +#define END_FIX { { 0 } , 0 } + +/* add specific device quirk */ +#define UFS_FIX(_vendor, _model, _quirk) \ + { \ + .card.vendor = (_vendor),\ + .card.model = (_model), \ + .quirk = (_quirk), \ + } + +struct ufs_hba; +void ufs_advertise_fixup_device(struct ufs_hba *hba); +#endif /* UFS_QUIRKS_H_ */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3481023af37c..ca60459acf37 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4770,6 +4770,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ret) goto out; + ufs_advertise_fixup_device(hba); + if (!hba->is_init_prefetch) { ret = ufshcd_get_device_ref_clk(hba); if (ret) { diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 51912dc59f76..91d52feb7806 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -68,6 +68,7 @@ #include "ufs.h" #include "ufshci.h" +#include "ufs_quirks.h" #define UFSHCD "ufshcd" #define UFSHCD_DRIVER_VERSION "0.2" @@ -531,6 +532,9 @@ struct ufs_hba { unsigned int quirks; /* Deviations from standard UFSHCI spec. */ + /* Device deviations from standard UFS device spec. */ + unsigned int dev_quirks; + /* Interrupt aggregation support is broken */ #define UFSHCD_QUIRK_BROKEN_INTR_AGGR (1<<0)