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)