blob: fcb87cafd009da9f929ca0d55055aeedd61c872e [file] [log] [blame]
/*
* Copyright (c) 2013-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"
#ifndef UFS_VENDOR_ID_SAMSUNG
#define UFS_VENDOR_ID_SAMSUNG 0x1ce
#endif
#define SERIAL_NUM_SIZE 6
#define TOSHIBA_SERIAL_NUM_SIZE 10
static struct ufs_card_fix ufs_fixups[] = {
/* UFS cards deviations table */
UFS_FIX(UFS_VENDOR_ID_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_QUIRK_BROKEN_LINEREST),
END_FIX
};
/*UN policy
* 16 digits : mandate + serial number(6byte, hex raw data)
* 18 digits : manid + mandate + serial number(sec, hynix : 6byte hex,
* toshiba : 10byte + 00, ascii)
*/
void ufs_set_sec_unique_number(struct ufs_hba *hba, u8 *str_desc_buf, u8 *desc_buf)
{
u8 manid;
u8 snum_buf[UFS_UN_MAX_DIGITS];
manid = hba->manufacturer_id & 0xFF;
memset(hba->unique_number, 0, sizeof(hba->unique_number));
memset(snum_buf, 0, sizeof(snum_buf));
#if defined(CONFIG_UFS_UN_18DIGITS)
memcpy(snum_buf, str_desc_buf + QUERY_DESC_HDR_SIZE, SERIAL_NUM_SIZE);
sprintf(hba->unique_number, "%02x%02x%02x%02x%02x%02x%02x%02x%02x",
manid,
desc_buf[DEVICE_DESC_PARAM_MANF_DATE], desc_buf[DEVICE_DESC_PARAM_MANF_DATE+1],
snum_buf[0], snum_buf[1], snum_buf[2], snum_buf[3], snum_buf[4], snum_buf[5]);
/* Null terminate the unique number string */
hba->unique_number[UFS_UN_18_DIGITS] = '\0';
#else
/*default is 16 DIGITS UN*/
memcpy(snum_buf, str_desc_buf + QUERY_DESC_HDR_SIZE, SERIAL_NUM_SIZE);
sprintf(hba->unique_number, "%02x%02x%02x%02x%02x%02x%02x%02x",
desc_buf[DEVICE_DESC_PARAM_MANF_DATE], desc_buf[DEVICE_DESC_PARAM_MANF_DATE+1],
snum_buf[0], snum_buf[1], snum_buf[2], snum_buf[3], snum_buf[4], snum_buf[5]);
/* Null terminate the unique number string */
hba->unique_number[UFS_UN_16_DIGITS] = '\0';
#endif
}
int ufs_get_device_info(struct ufs_hba *hba, struct ufs_card_info *card_data)
{
int err;
u8 model_index;
u8 serial_num_index;
u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1];
u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE];
u8 health_buf[QUERY_DESC_HEALTH_MAX_SIZE];
bool ascii_type;
err = ufshcd_read_device_desc(hba, desc_buf,
QUERY_DESC_DEVICE_MAX_SIZE);
if (err)
goto out;
err = ufshcd_read_health_desc(hba, health_buf,
QUERY_DESC_HEALTH_MAX_SIZE);
if (err)
printk("%s: DEVICE_HEALTH desc read fail, err = %d\n", __FUNCTION__, err);
/* getting Life Time at Device Health DESC*/
card_data->lifetime = health_buf[HEALTH_DEVICE_DESC_PARAM_LIFETIMEA];
/*
* getting vendor (manufacturerID) and Bank Index in big endian
* format
*/
card_data->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 |
desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1];
hba->manufacturer_id = card_data->wmanufacturerid;
hba->lifetime = card_data->lifetime;
/*product name*/
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;
str_desc_buf[QUERY_DESC_STRING_MAX_SIZE] = '\0';
strlcpy(card_data->model, (str_desc_buf + QUERY_DESC_HDR_SIZE),
min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET],
MAX_MODEL_LEN));
card_data->model[MAX_MODEL_LEN] = '\0';
/*serial number*/
serial_num_index = desc_buf[DEVICE_DESC_PARAM_SN];
memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE);
/*spec is unicode but sec use hex data*/
ascii_type = UTF16_STD;
err = ufshcd_read_string_desc(hba, serial_num_index, str_desc_buf,
QUERY_DESC_STRING_MAX_SIZE, ascii_type);
if (err)
goto out;
str_desc_buf[QUERY_DESC_STRING_MAX_SIZE] = '\0';
ufs_set_sec_unique_number(hba, str_desc_buf, desc_buf);
printk("%s: UNIQUE NUMBER = %s , LT: 0x%02x \n",
__FUNCTION__, hba->unique_number, health_buf[3]<<4|health_buf[4]);
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.wmanufacturerid = 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 wmanufacturerid */
if (((f->card.wmanufacturerid == card_data.wmanufacturerid) ||
(f->card.wmanufacturerid == 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);
}