Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

92
drivers/scsi/ufs/Kconfig Normal file
View file

@ -0,0 +1,92 @@
#
# Kernel configuration file for the UFS Host Controller
#
# This code is based on drivers/scsi/ufs/Kconfig
# Copyright (C) 2011-2013 Samsung India Software Operations
#
# Authors:
# Santosh Yaraganavi <santosh.sy@samsung.com>
# Vinayak Holikatti <h.vinayak@samsung.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# See the COPYING file in the top-level directory or visit
# <http://www.gnu.org/licenses/gpl-2.0.html>
#
# 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.
#
# This program is provided "AS IS" and "WITH ALL FAULTS" and
# without warranty of any kind. You are solely responsible for
# determining the appropriateness of using and distributing
# the program and assume all risks associated with your exercise
# of rights with respect to the program, including but not limited
# to infringement of third party rights, the risks and costs of
# program errors, damage to or loss of data, programs or equipment,
# and unavailability or interruption of operations. Under no
# circumstances will the contributor of this Program be liable for
# any damages of any kind arising from your use or distribution of
# this program.
config SCSI_UFSHCD
tristate "Universal Flash Storage Controller Driver Core"
depends on SCSI && SCSI_DMA
---help---
This selects the support for UFS devices in Linux, say Y and make
sure that you know the name of your UFS host adapter (the card
inside your computer that "speaks" the UFS protocol, also
called UFS Host Controller), because you will be asked for it.
The module will be called ufshcd.
To compile this driver as a module, choose M here and read
<file:Documentation/scsi/ufs.txt>.
However, do not compile this as a module if your root file system
(the one containing the directory /) is located on a UFS device.
config UFS_DYNAMIC_H8
bool "UFS Dynamic Hibernation (EXPERIMENTAL)"
depends on SCSI_UFSHCD
config SCSI_UFSHCD_PCI
tristate "PCI bus based UFS Controller support"
depends on SCSI_UFSHCD && PCI
---help---
This selects the PCI UFS Host Controller Interface. Select this if
you have UFS Host Controller with PCI Interface.
If you have a controller with this interface, say Y or M here.
config SCSI_SKIP_CACHE_OP
bool "Skip operations for cache coherency"
depends on SCSI_UFSHCD
---help---
This selects support for skipping operations for cache coherency
to reduce I/O overhead if file system layer do same thing.
If unsure, say N.
config SCSI_UFSHCD_PLATFORM
tristate "Platform bus based UFS Controller support"
depends on SCSI_UFSHCD
---help---
This selects the UFS host controller support. Select this if
you have an UFS controller on Platform bus.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config SCSI_UFS_EXYNOS
tristate "EXYNOS UFS Host Controller Driver"
depends on SCSI_UFSHCD && SCSI_UFSHCD_PLATFORM
---help---
This selects the EXYNOS UFS host controller driver.
If you have a controller with this interface, say Y or M here.
If unsure, say N.

View file

@ -0,0 +1,5 @@
# UFSHCD makefile
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_EXYNOS) += ufs-exynos.o

View file

@ -0,0 +1,434 @@
/*
* Exynos FMP UFS driver for FIPS
*
* Copyright (C) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/buffer_head.h>
#include <linux/mtd/mtd.h>
#include <linux/kdev_t.h>
#include <fmpdev_int.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include "ufshcd.h"
#include "../scsi_priv.h"
#define byte2word(b0, b1, b2, b3) \
((unsigned int)(b0) << 24) | ((unsigned int)(b1) << 16) | ((unsigned int)(b2) << 8) | (b3)
#define get_word(x, c) byte2word(((unsigned char *)(x) + 4 * (c))[0], ((unsigned char *)(x) + 4 * (c))[1], \
((unsigned char *)(x) + 4 * (c))[2], ((unsigned char *)(x) + 4 * (c))[3])
#define SF_BLK_OFFSET (5)
#define MAX_SCAN_PART (50)
struct ufs_fmp_work {
struct Scsi_Host *host;
struct scsi_device *sdev;
struct block_device *bdev;
sector_t sector;
dev_t devt;
};
struct ufshcd_sg_entry *prd_table;
struct ufshcd_sg_entry *ucd_prdt_ptr_st;
static int ufs_fmp_init(struct device *dev, uint32_t mode)
{
struct ufs_hba *hba;
struct ufs_fmp_work *work;
struct Scsi_Host *host;
work = dev_get_drvdata(dev);
if (!work) {
dev_err(dev, "Fail to get work from platform device\n");
return -ENODEV;
}
host = work->host;
hba = shost_priv(host);
ucd_prdt_ptr_st = kmalloc(sizeof(struct ufshcd_sg_entry), GFP_KERNEL);
if (!ucd_prdt_ptr_st) {
dev_err(dev, "Fail to alloc prdt ptr for self test\n");
return -ENOMEM;
}
hba->ucd_prdt_ptr_st = ucd_prdt_ptr_st;
return 0;
}
static int ufs_fmp_set_key(struct device *dev, uint32_t mode, uint8_t *key, uint32_t key_len)
{
struct ufs_hba *hba;
struct ufs_fmp_work *work;
struct Scsi_Host *host;
struct ufshcd_sg_entry *prd_table;
work = dev_get_drvdata(dev);
if (!work) {
dev_err(dev, "Fail to get work from platform device\n");
return -ENODEV;
}
host = work->host;
hba = shost_priv(host);
if (!hba->ucd_prdt_ptr_st) {
dev_err(dev, "prdt ptr for fips is not allocated\n");
return -ENOMEM;
}
prd_table = hba->ucd_prdt_ptr_st;
if (mode == CBC_MODE) {
SET_FAS(prd_table, CBC_MODE);
switch (key_len) {
case 32:
prd_table->size = 32;
/* encrypt key */
prd_table->file_enckey0 = get_word(key, 7);
prd_table->file_enckey1 = get_word(key, 6);
prd_table->file_enckey2 = get_word(key, 5);
prd_table->file_enckey3 = get_word(key, 4);
prd_table->file_enckey4 = get_word(key, 3);
prd_table->file_enckey5 = get_word(key, 2);
prd_table->file_enckey6 = get_word(key, 1);
prd_table->file_enckey7 = get_word(key, 0);
break;
case 16:
prd_table->size = 16;
/* encrypt key */
prd_table->file_enckey0 = get_word(key, 3);
prd_table->file_enckey1 = get_word(key, 2);
prd_table->file_enckey2 = get_word(key, 1);
prd_table->file_enckey3 = get_word(key, 0);
prd_table->file_enckey4 = 0;
prd_table->file_enckey5 = 0;
prd_table->file_enckey6 = 0;
prd_table->file_enckey7 = 0;
break;
default:
dev_err(dev, "Invalid key length : %d\n", key_len);
return -EINVAL;
}
} else if (mode == XTS_MODE) {
SET_FAS(prd_table, XTS_MODE);
switch (key_len) {
case 64:
prd_table->size = 32;
/* encrypt key */
prd_table->file_enckey0 = get_word(key, 7);
prd_table->file_enckey1 = get_word(key, 6);
prd_table->file_enckey2 = get_word(key, 5);
prd_table->file_enckey3 = get_word(key, 4);
prd_table->file_enckey4 = get_word(key, 3);
prd_table->file_enckey5 = get_word(key, 2);
prd_table->file_enckey6 = get_word(key, 1);
prd_table->file_enckey7 = get_word(key, 0);
/* tweak key */
prd_table->file_twkey0 = get_word(key, 15);
prd_table->file_twkey1 = get_word(key, 14);
prd_table->file_twkey2 = get_word(key, 13);
prd_table->file_twkey3 = get_word(key, 12);
prd_table->file_twkey4 = get_word(key, 11);
prd_table->file_twkey5 = get_word(key, 10);
prd_table->file_twkey6 = get_word(key, 9);
prd_table->file_twkey7 = get_word(key, 8);
break;
case 32:
prd_table->size = 16;
/* encrypt key */
prd_table->file_enckey0 = get_word(key, 3);
prd_table->file_enckey1 = get_word(key, 2);
prd_table->file_enckey2 = get_word(key, 1);
prd_table->file_enckey3 = get_word(key, 0);
prd_table->file_enckey4 = 0;
prd_table->file_enckey5 = 0;
prd_table->file_enckey6 = 0;
prd_table->file_enckey7 = 0;
/* tweak key */
prd_table->file_twkey0 = get_word(key, 7);
prd_table->file_twkey1 = get_word(key, 6);
prd_table->file_twkey2 = get_word(key, 5);
prd_table->file_twkey3 = get_word(key, 4);
prd_table->file_twkey4 = 0;
prd_table->file_twkey5 = 0;
prd_table->file_twkey6 = 0;
prd_table->file_twkey7 = 0;
break;
default:
dev_err(dev, "Invalid key length : %d\n", key_len);
return -EINVAL;
}
} else if (mode == BYPASS_MODE) {
SET_FAS(prd_table, BYPASS_MODE);
/* enc key */
prd_table->file_enckey0 = 0;
prd_table->file_enckey1 = 0;
prd_table->file_enckey2 = 0;
prd_table->file_enckey3 = 0;
prd_table->file_enckey4 = 0;
prd_table->file_enckey5 = 0;
prd_table->file_enckey6 = 0;
prd_table->file_enckey7 = 0;
/* tweak key */
prd_table->file_twkey0 = 0;
prd_table->file_twkey1 = 0;
prd_table->file_twkey2 = 0;
prd_table->file_twkey3 = 0;
prd_table->file_twkey4 = 0;
prd_table->file_twkey5 = 0;
prd_table->file_twkey6 = 0;
prd_table->file_twkey7 = 0;
} else {
dev_err(dev, "Invalid mode : %d\n", mode);
return -EINVAL;
}
dev_info(dev, "%s: ufs fmp key set is done.\n", __func__);
return 0;
}
static int ufs_fmp_set_iv(struct device *dev, uint32_t mode, uint8_t *iv, uint32_t iv_len)
{
struct ufs_hba *hba;
struct ufs_fmp_work *work;
struct Scsi_Host *host;
struct ufshcd_sg_entry *prd_table;
work = dev_get_drvdata(dev);
if (!work) {
dev_err(dev, "Fail to get work from platform device\n");
return -ENODEV;
}
host = work->host;
hba = shost_priv(host);
if (!hba->ucd_prdt_ptr_st) {
dev_err(dev, "prdt ptr for fips is not allocated\n");
return -ENOMEM;
}
prd_table = hba->ucd_prdt_ptr_st;
if (mode == CBC_MODE || mode == XTS_MODE) {
prd_table->file_iv0 = get_word(iv, 3);
prd_table->file_iv1 = get_word(iv, 2);
prd_table->file_iv2 = get_word(iv, 1);
prd_table->file_iv3 = get_word(iv, 0);
} else if (mode == BYPASS_MODE) {
prd_table->file_iv0 = 0;
prd_table->file_iv1 = 0;
prd_table->file_iv2 = 0;
prd_table->file_iv3 = 0;
} else {
dev_err(dev, "Invalid mode : %d\n", mode);
return -EINVAL;
}
return 0;
}
static dev_t find_devt_for_selftest(void)
{
int i, idx = 0;
uint64_t size;
uint64_t size_list[MAX_SCAN_PART];
dev_t devt_list[MAX_SCAN_PART];
dev_t devt_scan, devt;
struct block_device *bdev;
fmode_t fmode = FMODE_WRITE | FMODE_READ;
for (i = 0; i < MAX_SCAN_PART; i++) {
devt_scan = blk_lookup_devt("sda", i);
bdev = blkdev_get_by_dev(devt_scan, fmode, NULL);
if (IS_ERR(bdev))
continue;
else {
size_list[idx++] = (uint64_t)i_size_read(bdev->bd_inode);
devt_list[idx++] = devt_scan;
}
}
if (!idx)
goto err;
for (i = 0; i < idx; i++) {
if (i == 0) {
size = size_list[i];
devt = devt_list[i];
} else {
if (size < size_list[i])
devt = devt_list[i];
}
}
return devt;
err:
return (dev_t)0;
}
static int ufs_fmp_run(struct device *dev, uint32_t mode, uint8_t *data,
uint32_t len, uint32_t write)
{
int ret = 0;
struct ufs_hba *hba;
struct ufs_fmp_work *work;
struct Scsi_Host *host;
static struct buffer_head *bh;
work = dev_get_drvdata(dev);
if (!work) {
dev_err(dev, "Fail to get work from platform device\n");
return -ENODEV;
}
host = work->host;
hba = shost_priv(host);
hba->self_test_mode = mode;
bh = __getblk(work->bdev, work->sector, FMP_BLK_SIZE);
if (!bh) {
dev_err(dev, "Fail to get block from bdev\n");
return -ENODEV;
}
hba->self_test_bh = bh;
get_bh(bh);
if (write == WRITE_MODE) {
memcpy(bh->b_data, data, len);
set_buffer_dirty(bh);
sync_dirty_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
dev_err(dev, "IO error syncing for FMP fips write\n");
ret = -EIO;
goto out;
}
memset(bh->b_data, 0, FMP_BLK_SIZE);
} else {
lock_buffer(bh);
bh->b_end_io = end_buffer_read_sync;
submit_bh(READ_SYNC, bh);
wait_on_buffer(bh);
if (unlikely(!buffer_uptodate(bh))) {
ret = -EIO;
goto out;
}
memcpy(data, bh->b_data, len);
}
out:
hba->self_test_mode = 0;
hba->self_test_bh = NULL;
put_bh(bh);
return ret;
}
static int ufs_fmp_exit(void)
{
if (ucd_prdt_ptr_st)
kfree(ucd_prdt_ptr_st);
return 0;
}
struct fips_fmp_ops fips_fmp_fops = {
.init = ufs_fmp_init,
.set_key = ufs_fmp_set_key,
.set_iv = ufs_fmp_set_iv,
.run = ufs_fmp_run,
.exit = ufs_fmp_exit,
};
int fips_fmp_init(struct device *dev)
{
struct ufs_fmp_work *work;
struct device_node *dev_node;
struct platform_device *pdev_ufs;
struct device *dev_ufs;
struct ufs_hba *hba;
struct Scsi_Host *host;
struct inode *inode;
struct scsi_device *sdev;
struct super_block *sb;
unsigned long blocksize;
unsigned char blocksize_bits;
sector_t self_test_block;
fmode_t fmode = FMODE_WRITE | FMODE_READ;
work = kmalloc(sizeof(*work), GFP_KERNEL);
if (!work) {
dev_err(dev, "Fail to alloc fmp work buffer\n");
return -ENOMEM;
}
dev_node = of_find_compatible_node(NULL, NULL, "samsung,exynos-ufs");
if (!dev_node) {
dev_err(dev, "Fail to find exynos ufs device node\n");
goto out;
}
pdev_ufs = of_find_device_by_node(dev_node);
if (!pdev_ufs) {
dev_err(dev, "Fail to find exynos ufs pdev\n");
goto out;
}
dev_ufs = &pdev_ufs->dev;
hba = dev_get_drvdata(dev_ufs);
if (!hba) {
dev_err(dev, "Fail to find hba from dev\n");
goto out;
}
host = hba->host;
sdev = to_scsi_device(dev_ufs);
work->host = host;
work->sdev = sdev;
work->devt = find_devt_for_selftest();
if (!work->devt) {
dev_err(dev, "Fail to find devt for self test\n");
return -ENODEV;
}
work->bdev = blkdev_get_by_dev(work->devt, fmode, NULL);
if (IS_ERR(work->bdev)) {
dev_err(dev, "Fail to open block device\n");
return -ENODEV;
}
inode = work->bdev->bd_inode;
sb = inode->i_sb;
blocksize = sb->s_blocksize;
blocksize_bits = sb->s_blocksize_bits;
self_test_block = (i_size_read(inode) - (blocksize * SF_BLK_OFFSET)) >> blocksize_bits;
work->sector = self_test_block;
dev_set_drvdata(dev, work);
return 0;
out:
if (work)
kfree(work);
return -ENODEV;
}
EXPORT_SYMBOL_GPL(fips_fmp_init);

35
drivers/scsi/ufs/mphy.h Normal file
View file

@ -0,0 +1,35 @@
/*
* drivers/scsi/ufs/mphy.h
*
* Copyright (C) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _MPHY_H_
#define _MPHY_H_
#define TX_MIN_ACTIVATE_TIME 0x33
#define RX_HS_G1_SYNC_LENGTH_CAP 0x8b
#define RX_HS_G1_PREP_LENGTH_CAP 0x8c
#define RX_HS_G2_SYNC_LENGTH_CAP 0x94
#define RX_HS_G3_SYNC_LENGTH_CAP 0x95
#define RX_HS_G2_PREP_LENGTH_CAP 0x96
#define RX_HS_G3_PREP_LENGTH_CAP 0x97
#define SYNC_RANGE_FINE (0 << 6)
#define SYNC_RANGE_COARSE (1 << 6)
#define SYNC_LEN(x) ((x) & 0x3f)
#define PREP_LEN(x) ((x) & 0xf)
#define RX_ADV_GRANULARITY_CAP 0x98
#define RX_ADV_FINE_GRAN_STEP(x) ((((x) & 0x3) << 1) | 0x1)
#define RX_MIN_ACTIVATETIME_CAP 0x8f
#define RX_HIBERN8TIME_CAP 0x92
#define RX_ADV_HIBERN8TIME_CAP 0x99
#define RX_ADV_MIN_ACTIVATETIME_CAP 0x9a
#endif /* _MPHY_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,527 @@
/*
* UFS Host Controller driver for Exynos specific extensions
*
* Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _UFS_EXYNOS_H_
#define _UFS_EXYNOS_H_
#include <linux/pm_qos.h>
/*
* Exynos's Vendor specific registers for UFSHCI
*/
#define HCI_TXPRDT_ENTRY_SIZE 0x00
#define HCI_RXPRDT_ENTRY_SIZE 0x04
#define HCI_TO_CNT_DIV_VAL 0x08
#define HCI_1US_TO_CNT_VAL 0x0C
#define CNT_VAL_1US_MASK 0x3ff
#define HCI_INVALID_UPIU_CTRL 0x10
#define HCI_INVALID_UPIU_BADDR 0x14
#define HCI_INVALID_UPIU_UBADDR 0x18
#define HCI_INVALID_UTMR_OFFSET_ADDR 0x1C
#define HCI_INVALID_UTR_OFFSET_ADDR 0x20
#define HCI_INVALID_DIN_OFFSET_ADDR 0x24
#define HCI_DBR_TIMER_CONFIG 0x28
#define HCI_DBR_TIMER_STATUS 0x2C
#define HCI_VENDOR_SPECIFIC_IS 0x38
#define HCI_VENDOR_SPECIFIC_IE 0x3C
#define HCI_UTRL_NEXUS_TYPE 0x40
#define HCI_UTMRL_NEXUS_TYPE 0x44
#define HCI_E2EFC_CTRL 0x48
#define HCI_SW_RST 0x50
#define UFS_LINK_SW_RST (1 << 0)
#define UFS_UNIPRO_SW_RST (1 << 1)
#define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
#define HCI_LINK_VERSION 0x54
#define HCI_IDLE_TIMER_CONFIG 0x58
#define HCI_RX_UPIU_MATCH_ERROR_CODE 0x5C
#define HCI_DATA_REORDER 0x60
#define HCI_MAX_DOUT_DATA_SIZE 0x64
#define HCI_UNIPRO_APB_CLK_CTRL 0x68
#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
#define HCI_GPIO_OUT 0x70
#define HCI_WRITE_DMA_CTRL 0x74
#define HCI_ERROR_EN_PA_LAYER 0x78
#define HCI_ERROR_EN_DL_LAYER 0x7C
#define HCI_ERROR_EN_N_LAYER 0x80
#define HCI_ERROR_EN_T_LAYER 0x84
#define HCI_ERROR_EN_DME_LAYER 0x88
#define HCI_REQ_HOLD_EN 0xAC
#define HCI_CLKSTOP_CTRL 0xB0
#define REFCLK_STOP BIT(2)
#define UNIPRO_MCLK_STOP BIT(1)
#define UNIPRO_PCLK_STOP BIT(0)
#define CLK_STOP_ALL (REFCLK_STOP |\
UNIPRO_MCLK_STOP |\
UNIPRO_PCLK_STOP)
#define HCI_FORCE_HCS 0xB4
#define REFCLK_STOP_EN BIT(7)
#define UNIPRO_PCLK_STOP_EN BIT(6)
#define UNIPRO_MCLK_STOP_EN BIT(5)
#define HCI_CORECLK_STOP_EN BIT(4)
#define CLK_STOP_CTRL_EN_ALL (REFCLK_STOP_EN |\
UNIPRO_PCLK_STOP_EN |\
UNIPRO_MCLK_STOP_EN)
#define HCI_FSM_MONITOR 0xC0
#define HCI_PRDT_HIT_RATIO 0xC4
#define HCI_DMA0_MONITOR_STATE 0xC8
#define HCI_DMA0_MONITOR_CNT 0xCC
#define HCI_DMA1_MONITOR_STATE 0xD0
#define HCI_DMA1_MONITOR_CNT 0xD4
/* Device fatal error */
#define DFES_ERR_EN BIT(31)
#define DFES_DEF_DL_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
#define DFES_DEF_N_ERRS (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
UIC_NETWORK_BAD_DEVICEID_ENC |\
UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
#define DFES_DEF_T_ERRS (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE |\
UIC_TRANSPORT_UNKNOWN_CPORTID |\
UIC_TRANSPORT_NO_CONNECTION_RX |\
UIC_TRANSPORT_BAD_TC)
/* TXPRDT defines */
#define PRDT_PREFECT_EN BIT(31)
#define PRDT_SET_SIZE(x) ((x) & 0x1F)
enum {
UNIP_PA_LYR = 0,
UNIP_DL_LYR,
UNIP_N_LYR,
UNIP_T_LYR,
UNIP_DME_LYR,
};
/*
* UNIPRO registers
*/
#define UNIP_COMP_VERSION 0x000
#define UNIP_COMP_INFO 0x004
#define UNIP_COMP_RESET 0x010
#define UNIP_DME_POWERON_REQ 0x040
#define UNIP_DME_POWERON_CNF_RESULT 0x044
#define UNIP_DME_POWEROFF_REQ 0x048
#define UNIP_DME_POWEROFF_CNF_RESULT 0x04C
#define UNIP_DME_RESET_REQ 0x050
#define UNIP_DME_RESET_REQ_LEVEL 0x054
#define UNIP_DME_ENABLE_REQ 0x058
#define UNIP_DME_ENABLE_CNF_RESULT 0x05C
#define UNIP_DME_ENDPOINTRESET_REQ 0x060
#define UNIP_DME_ENDPOINTRESET_CNF_RESULT 0x064
#define UNIP_DME_LINKSTARTUP_REQ 0x068
#define UNIP_DME_LINKSTARTUP_CNF_RESULT 0x06C
#define UNIP_DME_HIBERN8_ENTER_REQ 0x070
#define UNIP_DME_HIBERN8_ENTER_CNF_RESULT 0x074
#define UNIP_DME_HIBERN8_ENTER_IND_RESULT 0x078
#define UNIP_DME_HIBERN8_EXIT_REQ 0x080
#define UNIP_DME_HIBERN8_EXIT_CNF_RESULT 0x084
#define UNIP_DME_HIBERN8_EXIT_IND_RESULT 0x088
#define UNIP_DME_PWR_REQ 0x090
#define UNIP_DME_PWR_REQ_POWERMODE 0x094
#define UNIP_DME_PWR_REQ_LOCALL2TIMER0 0x098
#define UNIP_DME_PWR_REQ_LOCALL2TIMER1 0x09C
#define UNIP_DME_PWR_REQ_LOCALL2TIMER2 0x0A0
#define UNIP_DME_PWR_REQ_REMOTEL2TIMER0 0x0A4
#define UNIP_DME_PWR_REQ_REMOTEL2TIMER1 0x0A8
#define UNIP_DME_PWR_REQ_REMOTEL2TIMER2 0x0AC
#define UNIP_DME_PWR_CNF_RESULT 0x0B0
#define UNIP_DME_PWR_IND_RESULT 0x0B4
#define UNIP_DME_TEST_MODE_REQ 0x0B8
#define UNIP_DME_TEST_MODE_CNF_RESULT 0x0BC
#define UNIP_DME_ERROR_IND_LAYER 0x0C0
#define UNIP_DME_ERROR_IND_ERRCODE 0x0C4
#define UNIP_DME_PACP_CNFBIT 0x0C8
#define UNIP_DME_DL_FRAME_IND 0x0D0
#define UNIP_DME_INTR_STATUS 0x0E0
#define UNIP_DME_INTR_ENABLE 0x0E4
#define UNIP_DME_GETSET_ADDR 0x100
#define UNIP_DME_GETSET_WDATA 0x104
#define UNIP_DME_GETSET_RDATA 0x108
#define UNIP_DME_GETSET_CONTROL 0x10C
#define UNIP_DME_GETSET_RESULT 0x110
#define UNIP_DME_PEER_GETSET_ADDR 0x120
#define UNIP_DME_PEER_GETSET_WDATA 0x124
#define UNIP_DME_PEER_GETSET_RDATA 0x128
#define UNIP_DME_PEER_GETSET_CONTROL 0x12C
#define UNIP_DME_PEER_GETSET_RESULT 0x130
#define UNIP_DBG_FORCE_DME_CTRL_STATE 0x150
#define UNIP_DBG_AUTO_DME_LINKSTARTUP 0x158
#define UNIP_DBG_PA_CTRLSTATE 0x15C
#define UNIP_DBG_PA_TX_STATE 0x160
#define UNIP_DBG_BREAK_DME_CTRL_STATE 0x164
#define UNIP_DBG_STEP_DME_CTRL_STATE 0x168
#define UNIP_DBG_NEXT_DME_CTRL_STATE 0x16C
/*
* UFS Protector registers
*/
#define UFSPRCTRL 0x000
#define UFSPRSTAT 0x008
#define UFSPRSECURITY 0x010
#define NSSMU BIT(14)
#define UFSPVERSION 0x01C
#define UFSPRENCKEY0 0x020
#define UFSPRENCKEY1 0x024
#define UFSPRENCKEY2 0x028
#define UFSPRENCKEY3 0x02C
#define UFSPRENCKEY4 0x030
#define UFSPRENCKEY5 0x034
#define UFSPRENCKEY6 0x038
#define UFSPRENCKEY7 0x03C
#define UFSPRTWKEY0 0x040
#define UFSPRTWKEY1 0x044
#define UFSPRTWKEY2 0x048
#define UFSPRTWKEY3 0x04C
#define UFSPRTWKEY4 0x050
#define UFSPRTWKEY5 0x054
#define UFSPRTWKEY6 0x058
#define UFSPRTWKEY7 0x05C
#define UFSPWCTRL 0x100
#define UFSPWSTAT 0x108
#define UFSPWSECURITY 0x110
#define UFSPWENCKEY0 0x120
#define UFSPWENCKEY1 0x124
#define UFSPWENCKEY2 0x128
#define UFSPWENCKEY3 0x12C
#define UFSPWENCKEY4 0x130
#define UFSPWENCKEY5 0x134
#define UFSPWENCKEY6 0x138
#define UFSPWENCKEY7 0x13C
#define UFSPWTWKEY0 0x140
#define UFSPWTWKEY1 0x144
#define UFSPWTWKEY2 0x148
#define UFSPWTWKEY3 0x14C
#define UFSPWTWKEY4 0x150
#define UFSPWTWKEY5 0x154
#define UFSPWTWKEY6 0x158
#define UFSPWTWKEY7 0x15C
#define UFSPSBEGIN0 0x200
#define UFSPSEND0 0x204
#define UFSPSLUN0 0x208
#define UFSPSCTRL0 0x20C
#define UFSPSBEGIN1 0x210
#define UFSPSEND1 0x214
#define UFSPSLUN1 0x218
#define UFSPSCTRL1 0x21C
#define UFSPSBEGIN2 0x220
#define UFSPSEND2 0x224
#define UFSPSLUN2 0x228
#define UFSPSCTRL2 0x22C
#define UFSPSBEGIN3 0x230
#define UFSPSEND3 0x234
#define UFSPSLUN3 0x238
#define UFSPSCTRL3 0x23C
#define UFSPSBEGIN4 0x240
#define UFSPSEND4 0x244
#define UFSPSLUN4 0x248
#define UFSPSCTRL4 0x24C
#define UFSPSBEGIN5 0x250
#define UFSPSEND5 0x254
#define UFSPSLUN5 0x258
#define UFSPSCTRL5 0x25C
#define UFSPSBEGIN6 0x260
#define UFSPSEND6 0x264
#define UFSPSLUN6 0x268
#define UFSPSCTRL6 0x26C
#define UFSPSBEGIN7 0x270
#define UFSPSEND7 0x274
#define UFSPSLUN7 0x278
#define UFSPSCTRL7 0x27C
/*
* MIBs for PA debug registers
*/
#define PA_DBG_CLK_PERIOD 0x9514
#define PA_DBG_TXPHY_CFGUPDT 0x9518
#define PA_DBG_RXPHY_CFGUPDT 0x9519
#define PA_DBG_MODE 0x9529
#define PA_DBG_TX_PHY_LATENCY 0x952C
#define PA_DBG_AUTOMODE_THLD 0x9536
#define PA_DBG_OV_TM 0x9540
#define PA_DBG_MIB_CAP_SEL 0x9546
#define PA_DBG_RESUME_HIBERN8 0x9550
#define PA_DBG_TX_PHY_LATENCY_EN 0x9552
#define PA_DBG_IGNORE_INCOMING 0x9559
#define PA_DBG_LINE_RESET_THLD 0x9561
#define PA_DBG_OPTION_SUITE 0x9564
/*
* MIBs for Transport Layer debug registers
*/
#define T_DBG_SKIP_INIT_HIBERN8_EXIT 0xc001
/*
* Exynos MPHY attributes
*/
#define TX_LINERESET_N_VAL 0x0277
#define TX_LINERESET_N(v) (((v) >> 10) & 0xff)
#define TX_LINERESET_P_VAL 0x027D
#define TX_LINERESET_P(v) (((v) >> 12) & 0xff)
#define TX_OV_SLEEP_CNT_TIMER 0x028E
#define TX_OV_H8_ENTER_EN (1 << 7)
#define TX_OV_SLEEP_CNT(v) (((v) >> 5) & 0x7f)
#define TX_HIGH_Z_CNT_11_08 0x028c
#define TX_HIGH_Z_CNT_H(v) (((v) >> 8) & 0xf)
#define TX_HIGH_Z_CNT_07_00 0x028d
#define TX_HIGH_Z_CNT_L(v) ((v) & 0xff)
#define TX_BASE_NVAL_07_00 0x0293
#define TX_BASE_NVAL_L(v) ((v) & 0xff)
#define TX_BASE_NVAL_15_08 0x0294
#define TX_BASE_NVAL_H(v) (((v) >> 8) & 0xff)
#define TX_GRAN_NVAL_07_00 0x0295
#define TX_GRAN_NVAL_L(v) ((v) & 0xff)
#define TX_GRAN_NVAL_10_08 0x0296
#define TX_GRAN_NVAL_H(v) (((v) >> 8) & 0x3)
#define RX_FILLER_ENABLE 0x0316
#define RX_FILLER_EN (1 << 1)
#define RX_LCC_IGNORE 0x0318
#define RX_LINERESET_VAL 0x0317
#define RX_LINERESET(v) (((v) >> 12) & 0xff)
#define RX_SYNC_MASK_LENGTH 0x0321
#define RX_HIBERN8_WAIT_VAL_BIT_20_16 0x0331
#define RX_HIBERN8_WAIT_VAL_BIT_15_08 0x0332
#define RX_HIBERN8_WAIT_VAL_BIT_07_00 0x0333
#define RX_OV_SLEEP_CNT_TIMER 0x0340
#define RX_OV_SLEEP_CNT(v) (((v) >> 6) & 0x1f)
#define RX_OV_STALL_CNT_TIMER 0x0341
#define RX_OV_STALL_CNT(v) (((v) >> 4) & 0xff)
#define RX_BASE_NVAL_07_00 0x0355
#define RX_BASE_NVAL_L(v) ((v) & 0xff)
#define RX_BASE_NVAL_15_08 0x0354
#define RX_BASE_NVAL_H(v) (((v) >> 8) & 0xff)
#define RX_GRAN_NVAL_07_00 0x0353
#define RX_GRAN_NVAL_L(v) ((v) & 0xff)
#define RX_GRAN_NVAL_10_08 0x0352
#define RX_GRAN_NVAL_H(v) (((v) >> 8) & 0x3)
#define CMN_PWM_CMN_CTRL 0x0402
#define PWM_CMN_CTRL_MASK 0x3
#define CMN_REFCLK_PLL_LOCK 0x0406
#define CMN_REFCLK_STREN 0x044C
#define CMN_REFCLK_OUT 0x044E
#define CMN_REFCLK_SEL_PLL 0x044F
/*
* Driver specific definitions
*/
enum {
PHY_CFG_NONE = 0,
PHY_PCS_COMN,
PHY_PCS_RXTX,
PHY_PMA_COMN,
PHY_PMA_TRSV,
UNIPRO_STD_MIB,
UNIPRO_DBG_MIB,
UNIPRO_DBG_APB,
};
enum {
__PMD_PWM_G1_L1,
__PMD_PWM_G1_L2,
__PMD_PWM_G2_L1,
__PMD_PWM_G2_L2,
__PMD_PWM_G3_L1,
__PMD_PWM_G3_L2,
__PMD_PWM_G4_L1,
__PMD_PWM_G4_L2,
__PMD_PWM_G5_L1,
__PMD_PWM_G5_L2,
__PMD_HS_G1_L1,
__PMD_HS_G1_L2,
__PMD_HS_G2_L1,
__PMD_HS_G2_L2,
__PMD_HS_G3_L1,
__PMD_HS_G3_L2,
};
#define PMD_PWM_G1_L1 (1U << __PMD_PWM_G1_L1)
#define PMD_PWM_G1_L2 (1U << __PMD_PWM_G1_L2)
#define PMD_PWM_G2_L1 (1U << __PMD_PWM_G2_L1)
#define PMD_PWM_G2_L2 (1U << __PMD_PWM_G2_L2)
#define PMD_PWM_G3_L1 (1U << __PMD_PWM_G3_L1)
#define PMD_PWM_G3_L2 (1U << __PMD_PWM_G3_L2)
#define PMD_PWM_G4_L1 (1U << __PMD_PWM_G4_L1)
#define PMD_PWM_G4_L2 (1U << __PMD_PWM_G4_L2)
#define PMD_PWM_G5_L1 (1U << __PMD_PWM_G5_L1)
#define PMD_PWM_G5_L2 (1U << __PMD_PWM_G5_L2)
#define PMD_HS_G1_L1 (1U << __PMD_HS_G1_L1)
#define PMD_HS_G1_L2 (1U << __PMD_HS_G1_L2)
#define PMD_HS_G2_L1 (1U << __PMD_HS_G2_L1)
#define PMD_HS_G2_L2 (1U << __PMD_HS_G2_L2)
#define PMD_HS_G3_L1 (1U << __PMD_HS_G3_L1)
#define PMD_HS_G3_L2 (1U << __PMD_HS_G3_L2)
#define PMD_ALL (PMD_HS_G3_L2 - 1)
#define PMD_PWM (PMD_PWM_G4_L2 - 1)
#define PMD_HS (PMD_ALL ^ PMD_PWM)
struct ufs_phy_cfg {
u32 addr;
u32 val;
u32 flg;
u32 lyr;
};
struct exynos_ufs_soc {
struct ufs_phy_cfg *tbl_phy_init;
struct ufs_phy_cfg *tbl_post_phy_init;
struct ufs_phy_cfg *tbl_calib_of_pwm;
struct ufs_phy_cfg *tbl_calib_of_hs_rate_a;
struct ufs_phy_cfg *tbl_calib_of_hs_rate_b;
struct ufs_phy_cfg *tbl_post_calib_of_pwm;
struct ufs_phy_cfg *tbl_post_calib_of_hs_rate_a;
struct ufs_phy_cfg *tbl_post_calib_of_hs_rate_b;
struct ufs_phy_cfg *tbl_pma_restore;
};
struct exynos_ufs_phy {
void __iomem *reg_pma;
void __iomem *reg_pmu;
struct exynos_ufs_soc *soc;
};
struct uic_pwr_mode {
u8 lane;
u8 gear;
u8 mode;
u8 hs_series;
bool termn;
u32 local_l2_timer[3];
u32 remote_l2_timer[3];
};
struct exynos_ufs_tp_mon_table {
u32 threshold;
s32 cluster1_value;
#ifdef CONFIG_ARM_EXYNOS_MP_CPUFREQ
s32 cluster0_value;
#endif
s32 mif_value;
};
struct exynos_ufs_clk_info {
struct list_head list;
struct clk *clk;
const char *name;
u32 freq;
};
struct exynos_ufs_misc_log {
struct list_head clk_list_head;
bool isolation;
};
struct exynos_ufs_sfr_log {
const char* name;
const u32 offset;
#define LOG_STD_HCI_SFR 0xFFFFFFF0
#define LOG_VS_HCI_SFR 0xFFFFFFF1
#define LOG_FMP_SFR 0xFFFFFFF2
#define LOG_UNIPRO_SFR 0xFFFFFFF3
#define LOG_PMA_SFR 0xFFFFFFF4
u32 val;
};
struct exynos_ufs_attr_log {
const u32 offset;
u32 res;
u32 val;
};
struct exynos_ufs_debug {
struct exynos_ufs_sfr_log* sfr;
struct exynos_ufs_attr_log* attr;
struct exynos_ufs_misc_log misc;
};
struct exynos_ufs {
struct device *dev;
struct ufs_hba *hba;
void __iomem *reg_hci;
void __iomem *reg_unipro;
void __iomem *reg_ufsp;
struct clk *clk_hci;
struct clk *pclk;
struct clk *clk_unipro;
struct clk *clk_refclk_1;
struct clk *clk_refclk_2;
struct clk *clk_phy_symb[4];
u32 pclk_rate;
u32 pclk_avail_min;
u32 pclk_avail_max;
u32 mclk_rate;
int avail_ln_rx;
int avail_ln_tx;
struct exynos_ufs_phy phy;
struct notifier_block lpa_nb;
struct uic_pwr_mode req_pmd_parm;
struct uic_pwr_mode act_pmd_parm;
u32 rx_adv_fine_gran_sup_en;
u32 rx_adv_fine_gran_step;
u32 rx_min_actv_time_cap;
u32 rx_hibern8_time_cap;
u32 rx_adv_min_actv_time_cap;
u32 rx_adv_hibern8_time_cap;
u32 pa_granularity;
u32 pa_tactivate;
u32 pa_hibern8time;
u32 wait_cdr_lock;
u32 opts;
#define EXYNOS_UFS_OPTS_SKIP_CONNECTION_ESTAB BIT(0)
#define EXYNOS_UFS_OPTS_USE_SEPERATED_PCLK BIT(1) /* for CAL */
/* Performance */
struct exynos_ufs_tp_mon_table *tp_mon_tbl;
struct delayed_work tp_mon;
struct pm_qos_request pm_qos_cluster1;
struct pm_qos_request pm_qos_cluster0;
struct pm_qos_request pm_qos_mif;
u32 last_threshold;
struct pm_qos_request pm_qos_int;
s32 pm_qos_int_value;
/* Support system power mode */
int idle_ip_index;
/* for miscellaneous control */
u32 misc_flags;
#define EXYNOS_UFS_MISC_TOGGLE_LOG BIT(0)
struct exynos_ufs_debug debug;
};
struct phy_tm_parm {
u32 tx_linereset_p;
u32 tx_linereset_n;
u32 tx_high_z_cnt;
u32 tx_base_n_val;
u32 tx_gran_n_val;
u32 tx_sleep_cnt;
u32 rx_linereset;
u32 rx_hibern8_wait;
u32 rx_base_n_val;
u32 rx_gran_n_val;
u32 rx_sleep_cnt;
u32 rx_stall_cnt;
};
#endif /* _UFS_EXYNOS_H_ */

549
drivers/scsi/ufs/ufs.h Normal file
View file

@ -0,0 +1,549 @@
/*
* Universal Flash Storage Host controller driver
*
* This code is based on drivers/scsi/ufs/ufs.h
* Copyright (C) 2011-2013 Samsung India Software Operations
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
* Vinayak Holikatti <h.vinayak@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* See the COPYING file in the top-level directory or visit
* <http://www.gnu.org/licenses/gpl-2.0.html>
*
* 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.
*
* This program is provided "AS IS" and "WITH ALL FAULTS" and
* without warranty of any kind. You are solely responsible for
* determining the appropriateness of using and distributing
* the program and assume all risks associated with your exercise
* of rights with respect to the program, including but not limited
* to infringement of third party rights, the risks and costs of
* program errors, damage to or loss of data, programs or equipment,
* and unavailability or interruption of operations. Under no
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program.
*/
#ifndef _UFS_H
#define _UFS_H
#include <linux/mutex.h>
#include <linux/types.h>
#define MAX_CDB_SIZE 16
#define GENERAL_UPIU_REQUEST_SIZE 32
#define QUERY_DESC_MAX_SIZE 255
#define QUERY_DESC_MIN_SIZE 2
#define QUERY_DESC_HDR_SIZE 2
#define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
(byte1 << 8) | (byte0))
/*
* UFS device may have standard LUs and LUN id could be from 0x00 to
* 0x7F. Standard LUs use "Peripheral Device Addressing Format".
* UFS device may also have the Well Known LUs (also referred as W-LU)
* which again could be from 0x00 to 0x7F. For W-LUs, device only use
* the "Extended Addressing Format" which means the W-LUNs would be
* from 0xc100 (SCSI_W_LUN_BASE) onwards.
* This means max. LUN number reported from UFS device could be 0xC17F.
*/
#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F
#define UFS_MAX_LUNS (SCSI_W_LUN_BASE + UFS_UPIU_MAX_UNIT_NUM_ID)
#define UFS_UPIU_WLUN_ID (1 << 7)
#define UFS_UPIU_MAX_GENERAL_LUN 8
/* Well known logical unit id in LUN field of UPIU */
enum {
UFS_UPIU_REPORT_LUNS_WLUN = 0x81,
UFS_UPIU_UFS_DEVICE_WLUN = 0xD0,
UFS_UPIU_BOOT_WLUN = 0xB0,
UFS_UPIU_RPMB_WLUN = 0xC4,
};
/*
* UFS Protocol Information Unit related definitions
*/
/* Task management functions */
enum {
UFS_ABORT_TASK = 0x01,
UFS_ABORT_TASK_SET = 0x02,
UFS_CLEAR_TASK_SET = 0x04,
UFS_LOGICAL_RESET = 0x08,
UFS_QUERY_TASK = 0x80,
UFS_QUERY_TASK_SET = 0x81,
};
/* UTP UPIU Transaction Codes Initiator to Target */
enum {
UPIU_TRANSACTION_NOP_OUT = 0x00,
UPIU_TRANSACTION_COMMAND = 0x01,
UPIU_TRANSACTION_DATA_OUT = 0x02,
UPIU_TRANSACTION_TASK_REQ = 0x04,
UPIU_TRANSACTION_QUERY_REQ = 0x16,
};
/* UTP UPIU Transaction Codes Target to Initiator */
enum {
UPIU_TRANSACTION_NOP_IN = 0x20,
UPIU_TRANSACTION_RESPONSE = 0x21,
UPIU_TRANSACTION_DATA_IN = 0x22,
UPIU_TRANSACTION_TASK_RSP = 0x24,
UPIU_TRANSACTION_READY_XFER = 0x31,
UPIU_TRANSACTION_QUERY_RSP = 0x36,
UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
};
/* UPIU Read/Write flags */
enum {
UPIU_CMD_FLAGS_NONE = 0x00,
UPIU_CMD_FLAGS_WRITE = 0x20,
UPIU_CMD_FLAGS_READ = 0x40,
};
/* UPIU Task Attributes */
enum {
UPIU_TASK_ATTR_SIMPLE = 0x00,
UPIU_TASK_ATTR_ORDERED = 0x01,
UPIU_TASK_ATTR_HEADQ = 0x02,
UPIU_TASK_ATTR_ACA = 0x03,
};
/* UPIU Query request function */
enum {
UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
};
/* Flag idn for Query Requests*/
enum flag_idn {
QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
QUERY_FLAG_IDN_BKOPS_EN = 0x04,
};
/* Attribute idn for Query requests */
enum attr_idn {
QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
QUERY_ATTR_IDN_RESERVED = 0x01,
QUERY_ATTR_IDN_POWER_MODE = 0x02,
QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
QUERY_ATTR_IDN_EE_STATUS = 0x0E,
QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
QUERY_ATTR_IDN_CNTX_CONF = 0x10,
QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
};
/* Descriptor idn for Query requests */
enum desc_idn {
QUERY_DESC_IDN_DEVICE = 0x0,
QUERY_DESC_IDN_CONFIGURAION = 0x1,
QUERY_DESC_IDN_UNIT = 0x2,
QUERY_DESC_IDN_RFU_0 = 0x3,
QUERY_DESC_IDN_INTERCONNECT = 0x4,
QUERY_DESC_IDN_STRING = 0x5,
QUERY_DESC_IDN_RFU_1 = 0x6,
QUERY_DESC_IDN_GEOMETRY = 0x7,
QUERY_DESC_IDN_POWER = 0x8,
QUERY_DESC_IDN_HEALTH = 0x9,
QUERY_DESC_IDN_RFU_2 = 0xA,
QUERY_DESC_IDN_MAX,
};
enum desc_header_offset {
QUERY_DESC_LENGTH_OFFSET = 0x00,
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
};
enum ufs_desc_max_size {
QUERY_DESC_DEVICE_MAX_SIZE = 0x40,
QUERY_DESC_CONFIGURAION_MAX_SIZE = 0x90,
QUERY_DESC_UNIT_MAX_SIZE = 0x23,
QUERY_DESC_INTERCONNECT_MAX_SIZE = 0x06,
/*
* Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes
* of descriptor header.
*/
QUERY_DESC_STRING_MAX_SIZE = 0xFE,
QUERY_DESC_GEOMETRY_MAZ_SIZE = 0x44,
QUERY_DESC_POWER_MAX_SIZE = 0x62,
QUERY_DESC_HEALTH_MAX_SIZE = 0x37,
QUERY_DESC_RFU_MAX_SIZE = 0x00,
};
/* Unit descriptor parameters offsets in bytes*/
enum unit_desc_param {
UNIT_DESC_PARAM_LEN = 0x0,
UNIT_DESC_PARAM_TYPE = 0x1,
UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
UNIT_DESC_PARAM_LU_ENABLE = 0x3,
UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
UNIT_DESC_PARAM_MEM_TYPE = 0x8,
UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9,
UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
UNIT_DESC_PARAM_ERASE_BLK_SIZE = 0x13,
UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20,
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
};
/*
* Logical Unit Write Protect
* 00h: LU not write protected
* 01h: LU write protected when fPowerOnWPEn =1
* 02h: LU permanently write protected when fPermanentWPEn =1
*/
enum ufs_lu_wp_type {
UFS_LU_NO_WP = 0x00,
UFS_LU_POWER_ON_WP = 0x01,
UFS_LU_PERM_WP = 0x02,
};
/* bActiveICCLevel parameter current units */
enum {
UFSHCD_NANO_AMP = 0,
UFSHCD_MICRO_AMP = 1,
UFSHCD_MILI_AMP = 2,
UFSHCD_AMP = 3,
};
#define POWER_DESC_MAX_SIZE 0x62
#define POWER_DESC_MAX_ACTV_ICC_LVLS 16
/* Attribute bActiveICCLevel parameter bit masks definitions */
#define ATTR_ICC_LVL_UNIT_OFFSET 14
#define ATTR_ICC_LVL_UNIT_MASK (0x3 << ATTR_ICC_LVL_UNIT_OFFSET)
#define ATTR_ICC_LVL_VALUE_MASK 0x3FF
/* Power descriptor parameters offsets in bytes */
enum power_desc_param_offset {
PWR_DESC_LEN = 0x0,
PWR_DESC_TYPE = 0x1,
PWR_DESC_ACTIVE_LVLS_VCC_0 = 0x2,
PWR_DESC_ACTIVE_LVLS_VCCQ_0 = 0x22,
PWR_DESC_ACTIVE_LVLS_VCCQ2_0 = 0x42,
};
/* Exception event mask values */
enum {
MASK_EE_STATUS = 0xFFFF,
MASK_EE_URGENT_BKOPS = (1 << 2),
};
/* Background operation status */
enum bkops_status {
BKOPS_STATUS_NO_OP = 0x0,
BKOPS_STATUS_NON_CRITICAL = 0x1,
BKOPS_STATUS_PERF_IMPACT = 0x2,
BKOPS_STATUS_CRITICAL = 0x3,
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
};
/* UTP QUERY Transaction Specific Fields OpCode */
enum query_opcode {
UPIU_QUERY_OPCODE_NOP = 0x0,
UPIU_QUERY_OPCODE_READ_DESC = 0x1,
UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
};
/* Query response result code */
enum {
QUERY_RESULT_SUCCESS = 0x00,
QUERY_RESULT_NOT_READABLE = 0xF6,
QUERY_RESULT_NOT_WRITEABLE = 0xF7,
QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
QUERY_RESULT_INVALID_LENGTH = 0xF9,
QUERY_RESULT_INVALID_VALUE = 0xFA,
QUERY_RESULT_INVALID_SELECTOR = 0xFB,
QUERY_RESULT_INVALID_INDEX = 0xFC,
QUERY_RESULT_INVALID_IDN = 0xFD,
QUERY_RESULT_INVALID_OPCODE = 0xFE,
QUERY_RESULT_GENERAL_FAILURE = 0xFF,
};
/* 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,
};
enum health_device_desc_param {
HEALTH_DEVICE_DESC_PARAM_LEN = 0x0,
HEALTH_DEVICE_DESC_PARAM_IDN =0x1,
HEALTH_DEVICE_DESC_PARAM_INFO =0x2,
HEALTH_DEVICE_DESC_PARAM_LIFETIMEA =0x3,
HEALTH_DEVICE_DESC_PARAM_LIFETIMEB =0x4,
HEALTH_DEVICE_DESC_PARAM_RESERVED =0x5,
};
/* UTP Transfer Request Command Type (CT) */
enum {
UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
UPIU_COMMAND_SET_TYPE_UFS = 0x1,
UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
};
/* UTP Transfer Request Command Offset */
#define UPIU_COMMAND_TYPE_OFFSET 28
/* Offset of the response code in the UPIU header */
#define UPIU_RSP_CODE_OFFSET 8
enum {
MASK_SCSI_STATUS = 0xFF,
MASK_TASK_RESPONSE = 0xFF00,
MASK_RSP_UPIU_RESULT = 0xFFFF,
MASK_QUERY_DATA_SEG_LEN = 0xFFFF,
MASK_RSP_UPIU_DATA_SEG_LEN = 0xFFFF,
MASK_RSP_EXCEPTION_EVENT = 0x10000,
};
/* Task management service response */
enum {
UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00,
UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08,
UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
};
/* UFS device power modes */
enum ufs_dev_pwr_mode {
UFS_ACTIVE_PWR_MODE = 1,
UFS_SLEEP_PWR_MODE = 2,
UFS_POWERDOWN_PWR_MODE = 3,
};
/**
* struct utp_upiu_header - UPIU header structure
* @dword_0: UPIU header DW-0
* @dword_1: UPIU header DW-1
* @dword_2: UPIU header DW-2
*/
struct utp_upiu_header {
__be32 dword_0;
__be32 dword_1;
__be32 dword_2;
};
/**
* struct utp_upiu_cmd - Command UPIU structure
* @data_transfer_len: Data Transfer Length DW-3
* @cdb: Command Descriptor Block CDB DW-4 to DW-7
*/
struct utp_upiu_cmd {
__be32 exp_data_transfer_len;
u8 cdb[MAX_CDB_SIZE];
};
/**
* struct utp_upiu_query - upiu request buffer structure for
* query request.
* @opcode: command to perform B-0
* @idn: a value that indicates the particular type of data B-1
* @index: Index to further identify data B-2
* @selector: Index to further identify data B-3
* @reserved_osf: spec reserved field B-4,5
* @length: number of descriptor bytes to read/write B-6,7
* @value: Attribute value to be written DW-5
* @reserved: spec reserved DW-6,7
*/
struct utp_upiu_query {
u8 opcode;
u8 idn;
u8 index;
u8 selector;
__be16 reserved_osf;
__be16 length;
__be32 value;
__be32 reserved[2];
};
/**
* struct utp_upiu_req - general upiu request structure
* @header:UPIU header structure DW-0 to DW-2
* @sc: fields structure for scsi command DW-3 to DW-7
* @qr: fields structure for query request DW-3 to DW-7
*/
struct utp_upiu_req {
struct utp_upiu_header header;
union {
struct utp_upiu_cmd sc;
struct utp_upiu_query qr;
};
};
/**
* struct utp_cmd_rsp - Response UPIU structure
* @residual_transfer_count: Residual transfer count DW-3
* @reserved: Reserved double words DW-4 to DW-7
* @sense_data_len: Sense data length DW-8 U16
* @sense_data: Sense data field DW-8 to DW-12
*/
struct utp_cmd_rsp {
__be32 residual_transfer_count;
__be32 reserved[4];
__be16 sense_data_len;
u8 sense_data[18];
};
/**
* struct utp_upiu_rsp - general upiu response structure
* @header: UPIU header structure DW-0 to DW-2
* @sr: fields structure for scsi command DW-3 to DW-12
* @qr: fields structure for query request DW-3 to DW-7
*/
struct utp_upiu_rsp {
struct utp_upiu_header header;
union {
struct utp_cmd_rsp sr;
struct utp_upiu_query qr;
};
};
/**
* struct utp_upiu_task_req - Task request UPIU structure
* @header - UPIU header structure DW0 to DW-2
* @input_param1: Input parameter 1 DW-3
* @input_param2: Input parameter 2 DW-4
* @input_param3: Input parameter 3 DW-5
* @reserved: Reserved double words DW-6 to DW-7
*/
struct utp_upiu_task_req {
struct utp_upiu_header header;
__be32 input_param1;
__be32 input_param2;
__be32 input_param3;
__be32 reserved[2];
};
/**
* struct utp_upiu_task_rsp - Task Management Response UPIU structure
* @header: UPIU header structure DW0-DW-2
* @output_param1: Ouput parameter 1 DW3
* @output_param2: Output parameter 2 DW4
* @reserved: Reserved double words DW-5 to DW-7
*/
struct utp_upiu_task_rsp {
struct utp_upiu_header header;
__be32 output_param1;
__be32 output_param2;
__be32 reserved[3];
};
/**
* struct ufs_query_req - parameters for building a query request
* @query_func: UPIU header query function
* @upiu_req: the query request data
*/
struct ufs_query_req {
u8 query_func;
struct utp_upiu_query upiu_req;
};
/**
* struct ufs_query_resp - UPIU QUERY
* @response: device response code
* @upiu_res: query response data
*/
struct ufs_query_res {
u8 response;
struct utp_upiu_query upiu_res;
};
#define UFS_VREG_VCC_MIN_UV 2700000 /* uV */
#define UFS_VREG_VCC_MAX_UV 3600000 /* uV */
#define UFS_VREG_VCC_1P8_MIN_UV 1700000 /* uV */
#define UFS_VREG_VCC_1P8_MAX_UV 1950000 /* uV */
#define UFS_VREG_VCCQ_MIN_UV 1100000 /* uV */
#define UFS_VREG_VCCQ_MAX_UV 1300000 /* uV */
#define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */
#define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */
/*
* VCCQ & VCCQ2 current requirement when UFS device is in sleep state
* and link is in Hibern8 state.
*/
#define UFS_VREG_LPM_LOAD_UA 1000 /* uA */
struct ufs_vreg {
struct regulator *reg;
const char *name;
bool enabled;
int min_uV;
int max_uV;
int min_uA;
int max_uA;
};
struct ufs_vreg_info {
struct ufs_vreg *vcc;
struct ufs_vreg *vccq;
struct ufs_vreg *vccq2;
struct ufs_vreg *vdd_hba;
};
struct ufs_dev_info {
bool f_power_on_wp_en;
/* Keeps information if any of the LU is power on write protected */
bool is_lu_power_on_wp;
};
#endif /* End of Header */

View file

@ -0,0 +1,138 @@
/*
* 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
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
};
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 serial_num_buf[6]; /* samsung spec : use only 6 bytes of serial number string descriptor */
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;
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));
/* Null terminate the model string */
card_data->model[MAX_MODEL_LEN] = '\0';
/* make unique id with MANUFACTURE DATE & SERIAL NUMBER */
serial_num_index = desc_buf[DEVICE_DESC_PARAM_SN];
memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE);
/*Samsung device use UTF16*/
if (hba->manufacturer_id == 0x1CE)
ascii_type = UTF16_STD;
else
ascii_type = ASCII_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';
memset(serial_num_buf, 0, sizeof(serial_num_buf));
memcpy(serial_num_buf, str_desc_buf + QUERY_DESC_HDR_SIZE, sizeof(serial_num_buf));
memset(hba->unique_number, 0, sizeof(hba->unique_number));
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], serial_num_buf[0], serial_num_buf[1], serial_num_buf[2], serial_num_buf[3], serial_num_buf[4], serial_num_buf[5]);
/* Null terminate the unique number string */
hba->unique_number[UFS_UNIQUE_NUMBER_LEN - 1] = '\0';
printk("%s: UNIQUE NUMBER = %s , Lifetime: 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);
}

View file

@ -0,0 +1,73 @@
/* 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 0xffff
#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
* @wmanufacturerid: card details
* @model: card model
*/
struct ufs_card_info {
u16 wmanufacturerid;
u8 lifetime;
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.wmanufacturerid = (_vendor),\
.card.model = (_model), \
.quirk = (_quirk), \
}
/*
* If UFS device is having issue in processing LCC (Line Control
* Command) coming from UFS host controller then enable this quirk.
* When this quirk is enabled, host controller driver should disable
* the LCC transmission on UFS host controller (by clearing
* TX_LCC_ENABLE attribute of host to 0).
*/
#define UFS_DEVICE_QUIRK_BROKEN_LCC UFS_BIT(0)
#define UFS_DEVICE_QUIRK_BROKEN_LINEREST UFS_BIT(1)
struct ufs_hba;
void ufs_advertise_fixup_device(struct ufs_hba *hba);
#endif /* UFS_QUIRKS_H_ */

View file

@ -0,0 +1,198 @@
/*
* Universal Flash Storage Host controller PCI glue driver
*
* This code is based on drivers/scsi/ufs/ufshcd-pci.c
* Copyright (C) 2011-2013 Samsung India Software Operations
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
* Vinayak Holikatti <h.vinayak@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* See the COPYING file in the top-level directory or visit
* <http://www.gnu.org/licenses/gpl-2.0.html>
*
* 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.
*
* This program is provided "AS IS" and "WITH ALL FAULTS" and
* without warranty of any kind. You are solely responsible for
* determining the appropriateness of using and distributing
* the program and assume all risks associated with your exercise
* of rights with respect to the program, including but not limited
* to infringement of third party rights, the risks and costs of
* program errors, damage to or loss of data, programs or equipment,
* and unavailability or interruption of operations. Under no
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program.
*/
#include "ufshcd.h"
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#ifdef CONFIG_PM
/**
* ufshcd_pci_suspend - suspend power management function
* @pdev: pointer to PCI device handle
* @state: power state
*
* Returns 0 if successful
* Returns non-zero otherwise
*/
static int ufshcd_pci_suspend(struct device *dev)
{
return ufshcd_system_suspend(dev_get_drvdata(dev));
}
/**
* ufshcd_pci_resume - resume power management function
* @pdev: pointer to PCI device handle
*
* Returns 0 if successful
* Returns non-zero otherwise
*/
static int ufshcd_pci_resume(struct device *dev)
{
return ufshcd_system_resume(dev_get_drvdata(dev));
}
#else
#define ufshcd_pci_suspend NULL
#define ufshcd_pci_resume NULL
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
static int ufshcd_pci_runtime_suspend(struct device *dev)
{
return ufshcd_runtime_suspend(dev_get_drvdata(dev));
}
static int ufshcd_pci_runtime_resume(struct device *dev)
{
return ufshcd_runtime_resume(dev_get_drvdata(dev));
}
static int ufshcd_pci_runtime_idle(struct device *dev)
{
return ufshcd_runtime_idle(dev_get_drvdata(dev));
}
#else /* !CONFIG_PM_RUNTIME */
#define ufshcd_pci_runtime_suspend NULL
#define ufshcd_pci_runtime_resume NULL
#define ufshcd_pci_runtime_idle NULL
#endif /* CONFIG_PM_RUNTIME */
/**
* ufshcd_pci_shutdown - main function to put the controller in reset state
* @pdev: pointer to PCI device handle
*/
static void ufshcd_pci_shutdown(struct pci_dev *pdev)
{
ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev));
}
/**
* ufshcd_pci_remove - de-allocate PCI/SCSI host and host memory space
* data structure memory
* @pdev - pointer to PCI handle
*/
static void ufshcd_pci_remove(struct pci_dev *pdev)
{
struct ufs_hba *hba = pci_get_drvdata(pdev);
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
disable_irq(pdev->irq);
ufshcd_remove(hba);
pci_set_drvdata(pdev, NULL);
}
/**
* ufshcd_pci_probe - probe routine of the driver
* @pdev: pointer to PCI device handle
* @id: PCI device id
*
* Returns 0 on success, non-zero value on failure
*/
static int
ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct ufs_hba *hba;
void __iomem *mmio_base;
int err;
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "pcim_enable_device failed\n");
return err;
}
pci_set_master(pdev);
err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD);
if (err < 0) {
dev_err(&pdev->dev, "request and iomap failed\n");
return err;
}
mmio_base = pcim_iomap_table(pdev)[0];
err = ufshcd_alloc_host(&pdev->dev, &hba);
if (err) {
dev_err(&pdev->dev, "Allocation failed\n");
return err;
}
INIT_LIST_HEAD(&hba->clk_list_head);
err = ufshcd_init(hba, mmio_base, pdev->irq);
if (err) {
dev_err(&pdev->dev, "Initialization failed\n");
return err;
}
pci_set_drvdata(pdev, hba);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_allow(&pdev->dev);
return 0;
}
static const struct dev_pm_ops ufshcd_pci_pm_ops = {
.suspend = ufshcd_pci_suspend,
.resume = ufshcd_pci_resume,
.runtime_suspend = ufshcd_pci_runtime_suspend,
.runtime_resume = ufshcd_pci_runtime_resume,
.runtime_idle = ufshcd_pci_runtime_idle,
};
static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
{ PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
static struct pci_driver ufshcd_pci_driver = {
.name = UFSHCD,
.id_table = ufshcd_pci_tbl,
.probe = ufshcd_pci_probe,
.remove = ufshcd_pci_remove,
.shutdown = ufshcd_pci_shutdown,
.driver = {
.pm = &ufshcd_pci_pm_ops
},
};
module_pci_driver(ufshcd_pci_driver);
MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
MODULE_DESCRIPTION("UFS host controller PCI glue driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(UFSHCD_DRIVER_VERSION);

View file

@ -0,0 +1,491 @@
/*
* Universal Flash Storage Host controller Platform bus based glue driver
*
* This code is based on drivers/scsi/ufs/ufshcd-pltfrm.c
* Copyright (C) 2011-2013 Samsung India Software Operations
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
* Vinayak Holikatti <h.vinayak@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* See the COPYING file in the top-level directory or visit
* <http://www.gnu.org/licenses/gpl-2.0.html>
*
* 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.
*
* This program is provided "AS IS" and "WITH ALL FAULTS" and
* without warranty of any kind. You are solely responsible for
* determining the appropriateness of using and distributing
* the program and assume all risks associated with your exercise
* of rights with respect to the program, including but not limited
* to infringement of third party rights, the risks and costs of
* program errors, damage to or loss of data, programs or equipment,
* and unavailability or interruption of operations. Under no
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program.
*/
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include "ufshcd.h"
#include "ufshcd-pltfrm.h"
static const struct of_device_id ufs_of_match[];
static struct ufs_hba_variant *get_variant(struct device *dev)
{
if (dev->of_node) {
const struct of_device_id *match;
match = of_match_node(ufs_of_match, dev->of_node);
if (match)
return (struct ufs_hba_variant *)match->data;
}
return NULL;
}
static int ufshcd_parse_clock_info(struct ufs_hba *hba)
{
int ret = 0;
int cnt;
int i;
struct device *dev = hba->dev;
struct device_node *np = dev->of_node;
char *name;
u32 *clkfreq = NULL;
struct ufs_clk_info *clki;
int len = 0;
size_t sz = 0;
if (!np)
goto out;
INIT_LIST_HEAD(&hba->clk_list_head);
cnt = of_property_count_strings(np, "clock-names");
if (!cnt || (cnt == -EINVAL)) {
dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
__func__);
} else if (cnt < 0) {
dev_err(dev, "%s: count clock strings failed, err %d\n",
__func__, cnt);
ret = cnt;
}
if (cnt <= 0)
goto out;
if (!of_get_property(np, "freq-table-hz", &len)) {
dev_info(dev, "freq-table-hz property not specified\n");
goto out;
}
if (len <= 0)
goto out;
sz = len / sizeof(*clkfreq);
if (sz != 2 * cnt) {
dev_err(dev, "%s len mismatch\n", "freq-table-hz");
ret = -EINVAL;
goto out;
}
clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq),
GFP_KERNEL);
if (!clkfreq) {
ret = -ENOMEM;
goto out;
}
ret = of_property_read_u32_array(np, "freq-table-hz",
clkfreq, sz);
if (ret && (ret != -EINVAL)) {
dev_err(dev, "%s: error reading array %d\n",
"freq-table-hz", ret);
return ret;
}
for (i = 0; i < sz; i += 2) {
ret = of_property_read_string_index(np,
"clock-names", i/2, (const char **)&name);
if (ret)
goto out;
clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
if (!clki) {
ret = -ENOMEM;
goto out;
}
clki->min_freq = clkfreq[i];
clki->max_freq = clkfreq[i+1];
clki->name = kstrdup(name, GFP_KERNEL);
dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz",
clki->min_freq, clki->max_freq, clki->name);
list_add_tail(&clki->list, &hba->clk_list_head);
}
out:
return ret;
}
#define MAX_PROP_SIZE 32
static int ufshcd_populate_vreg(struct device *dev, const char *name,
struct ufs_vreg **out_vreg)
{
int ret = 0;
char prop_name[MAX_PROP_SIZE];
struct ufs_vreg *vreg = NULL;
struct device_node *np = dev->of_node;
if (!np) {
dev_err(dev, "%s: non DT initialization\n", __func__);
goto out;
}
snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
if (!of_parse_phandle(np, prop_name, 0)) {
dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
__func__, prop_name);
goto out;
}
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
if (!vreg)
return -ENOMEM;
vreg->name = kstrdup(name, GFP_KERNEL);
/* if fixed regulator no need further initialization */
snprintf(prop_name, MAX_PROP_SIZE, "%s-fixed-regulator", name);
if (of_property_read_bool(np, prop_name))
goto out;
snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
ret = of_property_read_u32(np, prop_name, &vreg->max_uA);
if (ret) {
dev_err(dev, "%s: unable to find %s err %d\n",
__func__, prop_name, ret);
goto out_free;
}
vreg->min_uA = 0;
if (!strcmp(name, "vcc")) {
if (of_property_read_bool(np, "vcc-supply-1p8")) {
vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
} else {
vreg->min_uV = UFS_VREG_VCC_MIN_UV;
vreg->max_uV = UFS_VREG_VCC_MAX_UV;
}
} else if (!strcmp(name, "vccq")) {
vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
} else if (!strcmp(name, "vccq2")) {
vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
}
goto out;
out_free:
devm_kfree(dev, vreg);
vreg = NULL;
out:
if (!ret)
*out_vreg = vreg;
return ret;
}
/**
* ufshcd_parse_regulator_info - get regulator info from device tree
* @hba: per adapter instance
*
* Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
* If any of the supplies are not defined it is assumed that they are always-on
* and hence return zero. If the property is defined but parsing is failed
* then return corresponding error.
*/
static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
{
int err;
struct device *dev = hba->dev;
struct ufs_vreg_info *info = &hba->vreg_info;
err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba);
if (err)
goto out;
err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
if (err)
goto out;
err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
if (err)
goto out;
err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
out:
return err;
}
static int ufshcd_parse_pm_lvl_policy(struct ufs_hba *hba)
{
struct device *dev = hba->dev;
struct device_node *np = dev->of_node;
u32 lvl_def[] = {UFS_PM_LVL_2, UFS_PM_LVL_5};
u32 lvl[2], i;
for (i = 0; i < ARRAY_SIZE(lvl); i++) {
if (of_property_read_u32_index(np, "pm_lvl_states", i, lvl +i)) {
dev_info(hba->dev,
"UFS power management: set default level%d index %d\n",
lvl_def[i], i);
lvl[i] = lvl_def[i];
}
if (lvl[i] < UFS_PM_LVL_0 || lvl[i] >= UFS_PM_LVL_MAX) {
dev_warn(hba->dev,
"UFS power management: out of range level%d index %d\n",
lvl[i], i);
lvl[i] = lvl_def[i];
}
}
hba->rpm_lvl = lvl[0];
hba->spm_lvl = lvl[1];
return 0;
}
static int ufshcd_parse_caps_info(struct ufs_hba *hba)
{
struct device *dev = hba->dev;
struct device_node *np = dev->of_node;
if (of_find_property(np, "ufs-cap-clk-gating", NULL))
hba->caps |= UFSHCD_CAP_CLK_GATING;
if (of_find_property(np, "ufs-cap-hibern8-with-clk-gating", NULL))
hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
if (of_find_property(np, "ufs-cap-clk-scaling", NULL))
hba->caps |= UFSHCD_CAP_CLK_SCALING;
if (of_find_property(np, "ufs-cap-auto-bkops-suspend", NULL))
hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
if (of_find_property(np, "ufs-cap-fake-clk-gating", NULL))
hba->caps |= UFSHCD_CAP_FAKE_CLK_GATING;
return 0;
}
#ifdef CONFIG_PM
/**
* ufshcd_pltfrm_suspend - suspend power management function
* @dev: pointer to device handle
*
* Returns 0 if successful
* Returns non-zero otherwise
*/
static int ufshcd_pltfrm_suspend(struct device *dev)
{
return ufshcd_system_suspend(dev_get_drvdata(dev));
}
/**
* ufshcd_pltfrm_resume - resume power management function
* @dev: pointer to device handle
*
* Returns 0 if successful
* Returns non-zero otherwise
*/
static int ufshcd_pltfrm_resume(struct device *dev)
{
return ufshcd_system_resume(dev_get_drvdata(dev));
}
#else
#define ufshcd_pltfrm_suspend NULL
#define ufshcd_pltfrm_resume NULL
#endif
#ifdef CONFIG_PM_RUNTIME
static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
{
return ufshcd_runtime_suspend(dev_get_drvdata(dev));
}
static int ufshcd_pltfrm_runtime_resume(struct device *dev)
{
return ufshcd_runtime_resume(dev_get_drvdata(dev));
}
static int ufshcd_pltfrm_runtime_idle(struct device *dev)
{
return ufshcd_runtime_idle(dev_get_drvdata(dev));
}
#else /* !CONFIG_PM_RUNTIME */
#define ufshcd_pltfrm_runtime_suspend NULL
#define ufshcd_pltfrm_runtime_resume NULL
#define ufshcd_pltfrm_runtime_idle NULL
#endif /* CONFIG_PM_RUNTIME */
static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
{
ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
}
/**
* ufshcd_pltfrm_init - initialize common routine of the driver
* @pdev: pointer to Platform device handle
*
* Returns 0 on success, non-zero value on failure
*/
int ufshcd_pltfrm_init(struct platform_device *pdev,
const struct ufs_hba_variant *var)
{
struct ufs_hba *hba;
void __iomem *mmio_base;
struct resource *mem_res;
int irq, err;
struct device *dev = &pdev->dev;
const struct ufs_hba_variant *_var;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmio_base = devm_ioremap_resource(dev, mem_res);
if (IS_ERR(*(void **)&mmio_base)) {
err = PTR_ERR(*(void **)&mmio_base);
goto out;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "IRQ resource not available\n");
err = -ENODEV;
goto out;
}
err = ufshcd_alloc_host(dev, &hba);
if (err) {
dev_err(&pdev->dev, "Allocation failed\n");
goto out;
}
_var = (var != NULL) ? var : get_variant(&pdev->dev);
if (_var) {
hba->vops = _var->ops;
hba->quirks= _var->quirks;
}
err = ufshcd_parse_clock_info(hba);
if (err) {
dev_err(&pdev->dev, "%s: clock parse failed %d\n",
__func__, err);
goto out;
}
err = ufshcd_parse_regulator_info(hba);
if (err) {
dev_err(&pdev->dev, "%s: regulator init failed %d\n",
__func__, err);
goto out;
}
ufshcd_parse_pm_lvl_policy(hba);
ufshcd_parse_caps_info(hba);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err(dev, "Intialization failed\n");
goto out_disable_rpm;
}
platform_set_drvdata(pdev, hba);
return 0;
out_disable_rpm:
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
out:
return err;
}
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
/**
* ufshcd_pltfrm_exit - exit common routine for platform driver
* @pdev: pointer to platform device handle
*/
void ufshcd_pltfrm_exit(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
}
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_exit);
/**
* ufshcd_pltfrm_probe - probe routine of the platform driver
* @pdev: pointer to Platform device handle
*
* Returns 0 on success, non-zero value on failure
*/
static int ufshcd_pltfrm_probe(struct platform_device *pdev)
{
return ufshcd_pltfrm_init(pdev, NULL);
}
/**
* ufshcd_pltfrm_remove - remove platform driver routine
* @pdev: pointer to platform device handle
*
* Returns 0 on success, non-zero value on failure
*/
static int ufshcd_pltfrm_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
disable_irq(hba->irq);
ufshcd_remove(hba);
return 0;
}
static const struct of_device_id ufs_of_match[] = {
{ .compatible = "jedec,ufs-1.1"},
{},
};
static const struct dev_pm_ops ufshcd_dev_pm_ops = {
.suspend = ufshcd_pltfrm_suspend,
.resume = ufshcd_pltfrm_resume,
.runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle,
};
static struct platform_driver ufshcd_pltfrm_driver = {
.probe = ufshcd_pltfrm_probe,
.remove = ufshcd_pltfrm_remove,
.shutdown = ufshcd_pltfrm_shutdown,
.driver = {
.name = "ufshcd",
.owner = THIS_MODULE,
.pm = &ufshcd_dev_pm_ops,
.of_match_table = ufs_of_match,
},
};
module_platform_driver(ufshcd_pltfrm_driver);
MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
MODULE_DESCRIPTION("UFS host controller Pltform bus based glue driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(UFSHCD_DRIVER_VERSION);

View file

@ -0,0 +1,19 @@
/*
* Universal Flash Storage Host controller Platform bus based glue driver
*
* Copyright (C) 2013, Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* at your option) any later version.
*/
#ifndef _UFSHCD_PLTFRM_H_
#define _UFSHCD_PLTFRM_H_
extern int ufshcd_pltfrm_init(struct platform_device *pdev,
const struct ufs_hba_variant *var);
extern void ufshcd_pltfrm_exit(struct platform_device *pdev);
#endif /* _UFSHCD_PLTFRM_H_ */

6407
drivers/scsi/ufs/ufshcd.c Normal file

File diff suppressed because it is too large Load diff

689
drivers/scsi/ufs/ufshcd.h Normal file
View file

@ -0,0 +1,689 @@
/*
* Universal Flash Storage Host controller driver
*
* This code is based on drivers/scsi/ufs/ufshcd.h
* Copyright (C) 2011-2013 Samsung India Software Operations
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
* Vinayak Holikatti <h.vinayak@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* See the COPYING file in the top-level directory or visit
* <http://www.gnu.org/licenses/gpl-2.0.html>
*
* 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.
*
* This program is provided "AS IS" and "WITH ALL FAULTS" and
* without warranty of any kind. You are solely responsible for
* determining the appropriateness of using and distributing
* the program and assume all risks associated with your exercise
* of rights with respect to the program, including but not limited
* to infringement of third party rights, the risks and costs of
* program errors, damage to or loss of data, programs or equipment,
* and unavailability or interruption of operations. Under no
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program.
*/
#ifndef _UFSHCD_H
#define _UFSHCD_H
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/bitops.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#include <linux/jiffies.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
#include "ufs.h"
#include "ufshci.h"
#define UFSHCD "ufshcd"
#define UFSHCD_DRIVER_VERSION "0.2"
struct ufs_hba;
#define UFS_UNIQUE_NUMBER_LEN 17 /* manufacturer date 4 bytes + serial number 12 bytes + null */
struct ufs_hba;
enum dev_cmd_type {
DEV_CMD_TYPE_NOP = 0x0,
DEV_CMD_TYPE_QUERY = 0x1,
};
/**
* struct uic_command - UIC command structure
* @command: UIC command
* @argument1: UIC command argument 1
* @argument2: UIC command argument 2
* @argument3: UIC command argument 3
* @cmd_active: Indicate if UIC command is outstanding
* @result: UIC command result
* @done: UIC command completion
*/
struct uic_command {
u32 command;
u32 argument1;
u32 argument2;
u32 argument3;
int cmd_active;
int result;
struct completion done;
};
/* Used to differentiate the power management options */
enum ufs_pm_op {
UFS_RUNTIME_PM,
UFS_SYSTEM_PM,
UFS_SHUTDOWN_PM,
};
#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM)
#define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM)
#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
/* Host <-> Device UniPro Link state */
enum uic_link_state {
UIC_LINK_OFF_STATE = 0, /* Link powered down or disabled */
UIC_LINK_ACTIVE_STATE = 1, /* Link is in Fast/Slow/Sleep state */
UIC_LINK_HIBERN8_STATE = 2, /* Link is in Hibernate state */
UIC_LINK_TRANS_ACTIVE_STATE = 3,
UIC_LINK_TRANS_HIBERN8_STATE = 4,
};
#define ufshcd_is_link_off(hba) ((hba)->uic_link_state == UIC_LINK_OFF_STATE)
#define ufshcd_is_link_active(hba) ((hba)->uic_link_state == \
UIC_LINK_ACTIVE_STATE)
#define ufshcd_is_link_hibern8(hba) ((hba)->uic_link_state == \
UIC_LINK_HIBERN8_STATE)
#define ufshcd_set_link_off(hba) ((hba)->uic_link_state = UIC_LINK_OFF_STATE)
#define ufshcd_set_link_active(hba) ((hba)->uic_link_state = \
UIC_LINK_ACTIVE_STATE)
#define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \
UIC_LINK_HIBERN8_STATE)
#define ufshcd_set_link_trans_active(hba) ((hba)->uic_link_state = \
UIC_LINK_TRANS_ACTIVE_STATE)
#define ufshcd_set_link_trans_hibern8(hba) ((hba)->uic_link_state = \
UIC_LINK_TRANS_HIBERN8_STATE)
#define ufshcd_in_link_transition(hba) ( \
(hba)->uic_link_state == UIC_LINK_TRANS_ACTIVE_STATE || \
(hba)->uic_link_state == UIC_LINK_TRANS_HIBERN8_STATE)
/*
* UFS Power management levels.
* Each level is in increasing order of power savings.
*/
enum ufs_pm_level {
UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
UFS_PM_LVL_1, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_2, /* UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE */
UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE */
UFS_PM_LVL_MAX
};
struct ufs_pm_lvl_states {
enum ufs_dev_pwr_mode dev_state;
enum uic_link_state link_state;
};
/**
* struct ufshcd_lrb - local reference block
* @utr_descriptor_ptr: UTRD address of the command
* @ucd_req_ptr: UCD address of the command
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
* @cmd: pointer to SCSI command
* @sense_buffer: pointer to sense buffer address of the SCSI command
* @sense_bufflen: Length of the sense buffer
* @scsi_status: SCSI status of the command
* @command_type: SCSI, UFS, Query.
* @task_tag: Task tag of the command
* @lun: LUN of the command
* @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation)
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
struct utp_upiu_req *ucd_req_ptr;
struct utp_upiu_rsp *ucd_rsp_ptr;
struct ufshcd_sg_entry *ucd_prdt_ptr;
struct scsi_cmnd *cmd;
u8 *sense_buffer;
unsigned int sense_bufflen;
int scsi_status;
int command_type;
int task_tag;
u8 lun; /* UPIU LUN id field is only 8-bit wide */
bool intr_cmd;
};
/**
* struct ufs_query - holds relevent data structures for query request
* @request: request upiu and function
* @descriptor: buffer for sending/receiving descriptor
* @response: response upiu and response
*/
struct ufs_query {
struct ufs_query_req request;
u8 *descriptor;
struct ufs_query_res response;
};
/**
* struct ufs_dev_cmd - all assosiated fields with device management commands
* @type: device management command type - Query, NOP OUT
* @lock: lock to allow one command at a time
* @complete: internal commands completion
* @tag_wq: wait queue until free command slot is available
*/
struct ufs_dev_cmd {
enum dev_cmd_type type;
struct mutex lock;
struct completion *complete;
wait_queue_head_t tag_wq;
struct ufs_query query;
};
/**
* ufs_hba_variant: host specific data
*/
struct ufs_hba_variant {
const struct ufs_hba_variant_ops *ops;
u32 quirks;
void *vs_data;
};
/**
* struct ufs_clk_info - UFS clock related info
* @list: list headed by hba->clk_list_head
* @clk: clock node
* @name: clock name
* @max_freq: maximum frequency supported by the clock
* @min_freq: min frequency that can be used for clock scaling
* @curr_freq: indicates the current frequency that it is set to
* @enabled: variable to check against multiple enable/disable
*/
struct ufs_clk_info {
struct list_head list;
struct clk *clk;
const char *name;
u32 max_freq;
u32 min_freq;
u32 curr_freq;
bool enabled;
};
#define PRE_CHANGE 0
#define POST_CHANGE 1
struct ufs_pa_layer_attr {
u32 gear_rx;
u32 gear_tx;
u32 lane_rx;
u32 lane_tx;
u32 pwr_rx;
u32 pwr_tx;
u32 hs_rate;
};
struct ufs_pwr_mode_info {
bool is_valid;
struct ufs_pa_layer_attr info;
};
/**
* struct ufs_hba_variant_ops - variant specific callbacks
* @name: variant name
* @init: called when the driver is initialized
* @exit: called to cleanup everything done in init
* @clk_scale_notify: notifies that clks are scaled up/down
* @setup_clocks: called before touching any of the controller registers
* @setup_regulators: called before accessing the host controller
* @hce_enable_notify: called before and after HCE enable bit is set to allow
* variant specific Uni-Pro initialization.
* @link_startup_notify: called before and after Link startup is carried out
* to allow variant specific Uni-Pro initialization.
* @pwr_change_notify: called before and after a power mode change
* is carried out to allow vendor spesific capabilities
* to be set.
* @suspend: called during host controller PM callback
* @resume: called during host controller PM callback
*/
struct ufs_hba_variant_ops {
const char *name;
int (*init)(struct ufs_hba *);
void (*exit)(struct ufs_hba *);
void (*clk_scale_notify)(struct ufs_hba *);
int (*setup_clocks)(struct ufs_hba *, bool);
int (*setup_regulators)(struct ufs_hba *, bool);
void (*host_reset)(struct ufs_hba *);
int (*hce_enable_notify)(struct ufs_hba *, bool);
int (*link_startup_notify)(struct ufs_hba *, bool);
int (*pwr_change_notify)(struct ufs_hba *,
bool, struct ufs_pa_layer_attr *,
struct ufs_pa_layer_attr *);
void (*set_nexus_t_xfer_req)(struct ufs_hba *,
int, struct scsi_cmnd *);
void (*set_nexus_t_task_mgmt)(struct ufs_hba *, int, u8);
void (*hibern8_notify)(struct ufs_hba *, u8, bool);
void (*clock_control_notify)(struct ufs_hba *, bool, bool);
void (*get_debug_info)(struct ufs_hba *);
int (*suspend)(struct ufs_hba *, enum ufs_pm_op);
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
};
/* clock gating state */
enum clk_gating_state {
CLKS_OFF,
CLKS_ON,
REQ_CLKS_OFF,
REQ_CLKS_ON,
__CLKS_ON,
};
/**
* struct ufs_clk_gating - UFS clock gating related info
* @gate_work: worker to turn off clocks after some delay as specified in
* delay_ms
* @ungate_work: worker to turn on clocks that will be used in case of
* interrupt context
* @state: the current clocks state
* @delay_ms: gating delay in ms
* @is_suspended: clk gating is suspended when set to 1 which can be used
* during suspend/resume
* @delay_attr: sysfs attribute to control delay_attr
* @active_reqs: number of requests that are pending and should be waited for
* completion before gating clocks.
*/
struct ufs_clk_gating {
struct delayed_work gate_work;
struct work_struct ungate_work;
enum clk_gating_state state;
unsigned long delay_ms;
bool is_suspended;
struct device_attribute delay_attr;
int active_reqs;
};
struct ufs_clk_scaling {
ktime_t busy_start_t;
bool is_busy_started;
unsigned long tot_busy_t;
unsigned long window_start_t;
};
/**
* struct ufs_init_prefetch - contains data that is pre-fetched once during
* initialization
* @icc_level: icc level which was read during initialization
*/
struct ufs_init_prefetch {
u32 icc_level;
};
/**
* struct ufs_debug - monitors ufs driver's behaviors
*/
struct ufs_debug {
struct device_attribute attrs;
unsigned long flag;
#define UFSHCD_DEBUG_LEVEL1 (1 << 0)
#define UFSHCD_DEBUG_LEVEL2 (1 << 1)
};
/**
* struct ufs_hba - per adapter private structure
* @mmio_base: UFSHCI base register address
* @ucdl_base_addr: UFS Command Descriptor base address
* @utrdl_base_addr: UTP Transfer Request Descriptor base address
* @utmrdl_base_addr: UTP Task Management Descriptor base address
* @ucdl_dma_addr: UFS Command Descriptor DMA address
* @utrdl_dma_addr: UTRDL DMA address
* @utmrdl_dma_addr: UTMRDL DMA address
* @host: Scsi_Host instance of the driver
* @dev: device handle
* @lrb: local reference block
* @lrb_in_use: lrb in use
* @outstanding_tasks: Bits representing outstanding task requests
* @outstanding_reqs: Bits representing outstanding transfer requests
* @capabilities: UFS Controller Capabilities
* @nutrs: Transfer Request Queue depth supported by controller
* @nutmrs: Task Management Queue depth supported by controller
* @ufs_version: UFS Version to which controller complies
* @vops: pointer to variant specific operations
* @priv: pointer to variant specific private data
* @irq: Irq number of the controller
* @active_uic_cmd: handle of active UIC command
* @uic_cmd_mutex: mutex for uic command
* @tm_wq: wait queue for task management
* @tm_tag_wq: wait queue for free task management slots
* @tm_slots_in_use: bit map of task management request slots in use
* @pwr_done: completion for power mode change
* @tm_condition: condition variable for task management
* @ufshcd_state: UFSHCD states
* @eh_flags: Error handling flags
* @intr_mask: Interrupt Mask Bits
* @ee_ctrl_mask: Exception event control mask
* @is_powered: flag to check if HBA is powered
* @is_init_prefetch: flag to check if data was pre-fetched in initialization
* @init_prefetch_data: data pre-fetched during initialization
* @eh_work: Worker to handle UFS errors that require s/w attention
* @eeh_work: Worker to handle exception events
* @errors: HBA errors
* @uic_error: UFS interconnect layer error status
* @saved_err: sticky error mask
* @saved_uic_err: sticky UIC error mask
* @dev_cmd: ufs device management command information
* @auto_bkops_enabled: to track whether bkops is enabled in device
* @vreg_info: UFS device voltage regulator information
* @clk_list_head: UFS host controller clocks list node head
* @pwr_info: holds current power mode
* @max_pwr_info: keeps the device max valid pwm
*/
struct ufs_hba {
void __iomem *mmio_base;
/* Virtual memory reference */
struct utp_transfer_cmd_desc *ucdl_base_addr;
struct utp_transfer_req_desc *utrdl_base_addr;
struct utp_task_req_desc *utmrdl_base_addr;
/* DMA memory reference */
dma_addr_t ucdl_dma_addr;
dma_addr_t utrdl_dma_addr;
dma_addr_t utmrdl_dma_addr;
struct Scsi_Host *host;
struct device *dev;
/*
* This field is to keep a reference to "scsi_device" corresponding to
* "UFS device" W-LU.
*/
struct scsi_device *sdev_ufs_device;
struct scsi_device *sdev_rpmb;
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
/* Desired UFS power management level during runtime PM */
enum ufs_pm_level rpm_lvl;
/* Desired UFS power management level during system PM */
enum ufs_pm_level spm_lvl;
int pm_op_in_progress;
struct ufshcd_lrb *lrb;
volatile unsigned long lrb_in_use;
unsigned long outstanding_tasks;
unsigned long outstanding_reqs;
u32 capabilities;
int nutrs;
int nutmrs;
u32 ufs_version;
const struct ufs_hba_variant_ops *vops;
void *priv;
unsigned int irq;
bool is_irq_enabled;
wait_queue_head_t tm_wq;
wait_queue_head_t tm_tag_wq;
unsigned long tm_condition;
unsigned long tm_slots_in_use;
struct uic_command *active_uic_cmd;
struct mutex uic_cmd_mutex;
struct completion *uic_async_done;
u32 ufshcd_state;
u32 eh_flags;
u32 intr_mask;
u32 transferred_sector;
u16 ee_ctrl_mask;
bool is_powered;
bool is_init_prefetch;
struct ufs_init_prefetch init_prefetch_data;
/* Work Queues */
struct workqueue_struct *ufshcd_workq;
struct work_struct eh_work;
struct work_struct eeh_work;
/* Performance */
u32 tp_per_period;
/* HBA Errors */
u32 errors;
u32 uic_error;
u32 saved_err;
u32 saved_uic_err;
u32 tcx_replay_timer_expired_cnt;
u32 fcx_protection_timer_expired_cnt;
/* Device management request data */
struct ufs_dev_cmd dev_cmd;
/* Keeps information of the UFS device connected to this host */
struct ufs_dev_info dev_info;
bool auto_bkops_enabled;
struct ufs_vreg_info vreg_info;
struct list_head clk_list_head;
bool wlun_dev_clr_ua;
struct ufs_pa_layer_attr pwr_info;
struct ufs_pwr_mode_info max_pwr_info;
struct ufs_clk_gating clk_gating;
/* Control to enable/disable host capabilities */
u32 caps;
/* Allow dynamic clk gating */
#define UFSHCD_CAP_CLK_GATING (1 << 0)
/* Allow hiberb8 with clk gating */
#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1)
/* Allow dynamic clk scaling */
#define UFSHCD_CAP_CLK_SCALING (1 << 2)
/* Allow auto bkops to enabled during runtime suspend */
#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
/* Allow only hibern8 without clk gating */
#define UFSHCD_CAP_FAKE_CLK_GATING (1 << 4)
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
bool is_sys_suspended;
u32 quirks;
struct buffer_head *self_test_bh;
uint32_t self_test_mode;
struct ufshcd_sg_entry *ucd_prdt_ptr_st;
/* UFSHCI doesn't support DWORD size in UTRD */
#define UFSHCI_QUIRK_BROKEN_DWORD_UTRD BIT(0)
#define UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR BIT(1)
#define UFSHCI_QUIRK_USE_OF_HCE BIT(2)
#define UFSHCI_QUIRK_SKIP_INTR_AGGR BIT(3)
/* Device deviations from standard UFS device spec. */
unsigned int dev_quirks;
struct device_attribute unique_number_attr;
struct device_attribute manufacturer_id_attr;
char unique_number[UFS_UNIQUE_NUMBER_LEN];
u16 manufacturer_id;
u8 lifetime;
struct ufs_debug debug;
};
/* Returns true if clocks can be gated. Otherwise false */
static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_CLK_GATING;
}
static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
}
#if defined(CONFIG_PM_DEVFREQ)
static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_CLK_SCALING;
}
#endif
static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
}
static inline bool ufshcd_can_fake_clkgating(struct ufs_hba *hba)
{
return hba->caps & UFSHCD_CAP_FAKE_CLK_GATING;
}
#define ufshcd_writel(hba, val, reg) \
writel((val), (hba)->mmio_base + (reg))
#define ufshcd_readl(hba, reg) \
readl((hba)->mmio_base + (reg))
/**
* ufshcd_rmwl - read modify write into a register
* @hba - per adapter instance
* @mask - mask to apply on read value
* @val - actual value to write
* @reg - register address
*/
static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
{
u32 tmp;
tmp = ufshcd_readl(hba, reg);
tmp &= ~mask;
tmp |= (val & mask);
ufshcd_writel(hba, tmp, reg);
}
int ufshcd_alloc_host(struct device *, struct ufs_hba **);
int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int);
void ufshcd_remove(struct ufs_hba *);
/**
* ufshcd_hba_stop - Send controller to reset state
* @hba: per adapter instance
*/
static inline void ufshcd_hba_stop(struct ufs_hba *hba)
{
ufshcd_writel(hba, CONTROLLER_DISABLE, REG_CONTROLLER_ENABLE);
}
static inline void check_upiu_size(void)
{
BUILD_BUG_ON(ALIGNED_UPIU_SIZE <
GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);
}
extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
extern int ufshcd_runtime_resume(struct ufs_hba *hba);
extern int ufshcd_runtime_idle(struct ufs_hba *hba);
extern int ufshcd_system_suspend(struct ufs_hba *hba);
extern int ufshcd_system_resume(struct ufs_hba *hba);
extern int ufshcd_shutdown(struct ufs_hba *hba);
extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer);
extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
u32 *mib_val, u8 peer);
extern int ufshcd_config_pwr_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *desired_pwr_mode);
extern void scsi_softirq_done(struct request *rq);
/* UIC command interfaces for DME primitives */
#define DME_LOCAL 0
#define DME_PEER 1
#define ATTR_SET_NOR 0 /* NORMAL */
#define ATTR_SET_ST 1 /* STATIC */
static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
u32 mib_val)
{
return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
mib_val, DME_LOCAL);
}
static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
u32 mib_val)
{
return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
mib_val, DME_LOCAL);
}
static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
u32 mib_val)
{
return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
mib_val, DME_PEER);
}
static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 attr_sel,
u32 mib_val)
{
return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
mib_val, DME_PEER);
}
static inline int ufshcd_dme_get(struct ufs_hba *hba,
u32 attr_sel, u32 *mib_val)
{
return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
}
static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
u32 attr_sel, u32 *mib_val)
{
return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
}
int ufshcd_hold(struct ufs_hba *hba, bool async);
void ufshcd_release(struct ufs_hba *hba);
int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
int ufshcd_read_health_desc(struct ufs_hba *hba, u8 *buf, u32 size);
#define ASCII_STD true
#define UTF16_STD false
int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
u32 size, bool ascii);
#endif /* End of Header */

460
drivers/scsi/ufs/ufshci.h Normal file
View file

@ -0,0 +1,460 @@
/*
* Universal Flash Storage Host controller driver
*
* This code is based on drivers/scsi/ufs/ufshci.h
* Copyright (C) 2011-2013 Samsung India Software Operations
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
* Vinayak Holikatti <h.vinayak@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* See the COPYING file in the top-level directory or visit
* <http://www.gnu.org/licenses/gpl-2.0.html>
*
* 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.
*
* This program is provided "AS IS" and "WITH ALL FAULTS" and
* without warranty of any kind. You are solely responsible for
* determining the appropriateness of using and distributing
* the program and assume all risks associated with your exercise
* of rights with respect to the program, including but not limited
* to infringement of third party rights, the risks and costs of
* program errors, damage to or loss of data, programs or equipment,
* and unavailability or interruption of operations. Under no
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program.
*/
#ifndef _UFSHCI_H
#define _UFSHCI_H
enum {
TASK_REQ_UPIU_SIZE_DWORDS = 8,
TASK_RSP_UPIU_SIZE_DWORDS = 8,
ALIGNED_UPIU_SIZE = 512,
};
/* UFSHCI Registers */
enum {
REG_CONTROLLER_CAPABILITIES = 0x00,
REG_UFS_VERSION = 0x08,
REG_CONTROLLER_DEV_ID = 0x10,
REG_CONTROLLER_PROD_ID = 0x14,
REG_INTERRUPT_STATUS = 0x20,
REG_INTERRUPT_ENABLE = 0x24,
REG_CONTROLLER_STATUS = 0x30,
REG_CONTROLLER_ENABLE = 0x34,
REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38,
REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C,
REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40,
REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44,
REG_UIC_ERROR_CODE_DME = 0x48,
REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C,
REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50,
REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54,
REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58,
REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C,
REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60,
REG_UTP_TASK_REQ_LIST_BASE_L = 0x70,
REG_UTP_TASK_REQ_LIST_BASE_H = 0x74,
REG_UTP_TASK_REQ_DOOR_BELL = 0x78,
REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C,
REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80,
REG_UIC_COMMAND = 0x90,
REG_UIC_COMMAND_ARG_1 = 0x94,
REG_UIC_COMMAND_ARG_2 = 0x98,
REG_UIC_COMMAND_ARG_3 = 0x9C,
};
/* Controller capability masks */
enum {
MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
MASK_64_ADDRESSING_SUPPORT = 0x01000000,
MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
};
/* UFS Version 08h */
#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0)
#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16)
/* Controller UFSHCI version */
enum {
UFSHCI_VERSION_10 = 0x00010000,
UFSHCI_VERSION_11 = 0x00010100,
};
/*
* HCDDID - Host Controller Identification Descriptor
* - Device ID and Device Class 10h
*/
#define DEVICE_CLASS UFS_MASK(0xFFFF, 0)
#define DEVICE_ID UFS_MASK(0xFF, 24)
/*
* HCPMID - Host Controller Identification Descriptor
* - Product/Manufacturer ID 14h
*/
#define MANUFACTURE_ID_MASK UFS_MASK(0xFFFF, 0)
#define PRODUCT_ID_MASK UFS_MASK(0xFFFF, 16)
#define UFS_BIT(x) (1L << (x))
#define UTP_TRANSFER_REQ_COMPL UFS_BIT(0)
#define UIC_DME_END_PT_RESET UFS_BIT(1)
#define UIC_ERROR UFS_BIT(2)
#define UIC_TEST_MODE UFS_BIT(3)
#define UIC_POWER_MODE UFS_BIT(4)
#define UIC_HIBERNATE_EXIT UFS_BIT(5)
#define UIC_HIBERNATE_ENTER UFS_BIT(6)
#define UIC_LINK_LOST UFS_BIT(7)
#define UIC_LINK_STARTUP UFS_BIT(8)
#define UTP_TASK_REQ_COMPL UFS_BIT(9)
#define UIC_COMMAND_COMPL UFS_BIT(10)
#define DEVICE_FATAL_ERROR UFS_BIT(11)
#define CONTROLLER_FATAL_ERROR UFS_BIT(16)
#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17)
#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\
UIC_HIBERNATE_EXIT |\
UIC_POWER_MODE)
#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)
#define UFSHCD_ERROR_MASK (UIC_ERROR |\
DEVICE_FATAL_ERROR |\
CONTROLLER_FATAL_ERROR |\
SYSTEM_BUS_FATAL_ERROR |\
UIC_LINK_LOST)
#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
CONTROLLER_FATAL_ERROR |\
SYSTEM_BUS_FATAL_ERROR |\
UIC_LINK_LOST)
/* HCS - Host Controller Status 30h */
#define DEVICE_PRESENT UFS_BIT(0)
#define UTP_TRANSFER_REQ_LIST_READY UFS_BIT(1)
#define UTP_TASK_REQ_LIST_READY UFS_BIT(2)
#define UIC_COMMAND_READY UFS_BIT(3)
#define HOST_ERROR_INDICATOR UFS_BIT(4)
#define DEVICE_ERROR_INDICATOR UFS_BIT(5)
#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8)
enum {
PWR_OK = 0x0,
PWR_LOCAL = 0x01,
PWR_REMOTE = 0x02,
PWR_BUSY = 0x03,
PWR_ERROR_CAP = 0x04,
PWR_FATAL_ERROR = 0x05,
};
/* HCE - Host Controller Enable 34h */
#define CONTROLLER_ENABLE UFS_BIT(0)
#define CONTROLLER_DISABLE 0x0
/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
#define UIC_PHY_ADAPTER_LAYER_ERROR UFS_BIT(31)
#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F
/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
#define UIC_DATA_LINK_LAYER_ERROR UFS_BIT(31)
#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK 0x7FFF
#define UIC_DATA_LINK_LAYER_ERROR_TCX_REP_TIMER_EXP UFS_BIT(1)
#define UIC_DATA_LINK_LAYER_ERROR_AFCX_REQ_TIMER_EXP UFS_BIT(2)
#define UIC_DATA_LINK_LAYER_ERROR_FCX_PRO_TIMER_EXP UFS_BIT(3)
#define UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF UFS_BIT(5)
#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT UFS_BIT(13)
/* UECN - Host UIC Error Code Network Layer 40h */
#define UIC_NETWORK_LAYER_ERROR UFS_BIT(31)
#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7
#define UIC_NETWORK_UNSUPPORTED_HEADER_TYPE BIT(0)
#define UIC_NETWORK_BAD_DEVICEID_ENC BIT(1)
#define UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING BIT(2)
/* UECT - Host UIC Error Code Transport Layer 44h */
#define UIC_TRANSPORT_LAYER_ERROR UFS_BIT(31)
#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F
#define UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE BIT(0)
#define UIC_TRANSPORT_UNKNOWN_CPORTID BIT(1)
#define UIC_TRANSPORT_NO_CONNECTION_RX BIT(2)
#define UIC_TRANSPORT_CONTROLLED_SEGMENT_DROPPING BIT(3)
#define UIC_TRANSPORT_BAD_TC BIT(4)
#define UIC_TRANSPORT_E2E_CREDIT_OVERFOW BIT(5)
#define UIC_TRANSPORT_SAFETY_VALUE_DROPPING BIT(6)
/* UECDME - Host UIC Error Code DME 48h */
#define UIC_DME_ERROR UFS_BIT(31)
#define UIC_DME_ERROR_CODE_MASK 0x1
#define INT_AGGR_TIMEOUT_VAL_MASK 0xFF
#define INT_AGGR_COUNTER_THRESHOLD_MASK UFS_MASK(0x1F, 8)
#define INT_AGGR_COUNTER_AND_TIMER_RESET UFS_BIT(16)
#define INT_AGGR_STATUS_BIT UFS_BIT(20)
#define INT_AGGR_PARAM_WRITE UFS_BIT(24)
#define INT_AGGR_ENABLE UFS_BIT(31)
/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT UFS_BIT(0)
/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
#define UTP_TASK_REQ_LIST_RUN_STOP_BIT UFS_BIT(0)
/* UICCMD - UIC Command */
#define COMMAND_OPCODE_MASK 0xFF
#define GEN_SELECTOR_INDEX_MASK 0xFFFF
#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16)
#define RESET_LEVEL 0xFF
#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16)
#define CONFIG_RESULT_CODE_MASK 0xFF
#define GENERIC_ERROR_CODE_MASK 0xFF
#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\
((sel) & 0xFFFF))
#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)
#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16)
#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF)
/*
* UFS Protector configuration
*/
#define UFS_BYPASS_SECTOR_BEGIN 0x0
#define UFS_ENCRYPTION_SECTOR_BEGIN 0x0000FFFF
#define UFS_FILE_ENCRYPTION_SECTOR_BEGIN 0xFFFF0000
#define UFSHCI_SECTOR_SIZE 0x1000
#define MIN_SECTOR_SIZE 0x200
/* UIC Commands */
enum uic_cmd_dme {
UIC_CMD_DME_GET = 0x01,
UIC_CMD_DME_SET = 0x02,
UIC_CMD_DME_PEER_GET = 0x03,
UIC_CMD_DME_PEER_SET = 0x04,
UIC_CMD_DME_POWERON = 0x10,
UIC_CMD_DME_POWEROFF = 0x11,
UIC_CMD_DME_ENABLE = 0x12,
UIC_CMD_DME_RESET = 0x14,
UIC_CMD_DME_END_PT_RST = 0x15,
UIC_CMD_DME_LINK_STARTUP = 0x16,
UIC_CMD_DME_HIBER_ENTER = 0x17,
UIC_CMD_DME_HIBER_EXIT = 0x18,
UIC_CMD_DME_TEST_MODE = 0x1A,
};
/* UIC Config result code / Generic error code */
enum {
UIC_CMD_RESULT_SUCCESS = 0x00,
UIC_CMD_RESULT_INVALID_ATTR = 0x01,
UIC_CMD_RESULT_FAILURE = 0x01,
UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
UIC_CMD_RESULT_BAD_INDEX = 0x05,
UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
UIC_CMD_RESULT_BUSY = 0x09,
UIC_CMD_RESULT_DME_FAILURE = 0x0A,
};
#define MASK_UIC_COMMAND_RESULT 0xFF
#define INT_AGGR_COUNTER_THLD_VAL(c) (((c) & 0x1F) << 8)
#define INT_AGGR_TIMEOUT_VAL(t) (((t) & 0xFF) << 0)
/* Interrupt disable masks */
enum {
/* Interrupt disable mask for UFSHCI v1.0 */
INTERRUPT_MASK_ALL_VER_10 = 0x30FFF,
INTERRUPT_MASK_RW_VER_10 = 0x30000,
/* Interrupt disable mask for UFSHCI v1.1 */
INTERRUPT_MASK_ALL_VER_11 = 0x31FFF,
};
/*
* Request Descriptor Definitions
*/
/* Transfer request command type */
enum {
UTP_CMD_TYPE_SCSI = 0x0,
UTP_CMD_TYPE_UFS = 0x1,
UTP_CMD_TYPE_DEV_MANAGE = 0x2,
};
enum {
UTP_SCSI_COMMAND = 0x00000000,
UTP_NATIVE_UFS_COMMAND = 0x10000000,
UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
UTP_REQ_DESC_INT_CMD = 0x01000000,
};
/* UTP Transfer Request Data Direction (DD) */
enum {
UTP_NO_DATA_TRANSFER = 0x00000000,
UTP_HOST_TO_DEVICE = 0x02000000,
UTP_DEVICE_TO_HOST = 0x04000000,
};
/* Overall command status values */
enum {
OCS_SUCCESS = 0x0,
OCS_INVALID_CMD_TABLE_ATTR = 0x1,
OCS_INVALID_PRDT_ATTR = 0x2,
OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
OCS_PEER_COMM_FAILURE = 0x5,
OCS_ABORTED = 0x6,
OCS_FATAL_ERROR = 0x7,
OCS_INVALID_COMMAND_STATUS = 0x0F,
MASK_OCS = 0x0F,
};
/* The maximum length of the data byte count field in the PRDT is 256KB */
#define PRDT_DATA_BYTE_COUNT_MAX (256 * 1024)
/* The granularity of the data byte count field in the PRDT is 32-bit */
#define PRDT_DATA_BYTE_COUNT_PAD 4
/* FMP bypass/encrypt mode */
#define CLEAR 0
#define AES_CBC 1
#define AES_XTS 2
/**
* struct ufshcd_sg_entry - UFSHCI PRD Entry
* @base_addr: Lower 32bit physical address DW-0
* @upper_addr: Upper 32bit physical address DW-1
* @reserved: Reserved for future use DW-2
* @size: size of physical segment DW-3
*/
struct ufshcd_sg_entry {
__le32 base_addr;
__le32 upper_addr;
__le32 reserved;
__le32 size;
#define FKL BIT(26)
#define DKL BIT(27)
#define SET_FAS(d, v) \
((d)->size = ((d)->size & 0xcfffffff) | v << 28)
#define SET_DAS(d, v) \
((d)->size = ((d)->size & 0x3fffffff) | v << 30)
#if defined(CONFIG_UFS_FMP_DM_CRYPT) || defined(CONFIG_UFS_FMP_ECRYPT_FS)
__le32 file_iv0;
__le32 file_iv1;
__le32 file_iv2;
__le32 file_iv3;
__le32 file_enckey0;
__le32 file_enckey1;
__le32 file_enckey2;
__le32 file_enckey3;
__le32 file_enckey4;
__le32 file_enckey5;
__le32 file_enckey6;
__le32 file_enckey7;
__le32 file_twkey0;
__le32 file_twkey1;
__le32 file_twkey2;
__le32 file_twkey3;
__le32 file_twkey4;
__le32 file_twkey5;
__le32 file_twkey6;
__le32 file_twkey7;
__le32 disk_iv0;
__le32 disk_iv1;
__le32 disk_iv2;
__le32 disk_iv3;
__le32 reserved0;
__le32 reserved1;
__le32 reserved2;
__le32 reserved3;
#endif
};
/**
* struct utp_transfer_cmd_desc - UFS Command Descriptor structure
* @command_upiu: Command UPIU Frame address
* @response_upiu: Response UPIU Frame address
* @prd_table: Physical Region Descriptor
*/
struct utp_transfer_cmd_desc {
u8 command_upiu[ALIGNED_UPIU_SIZE];
u8 response_upiu[ALIGNED_UPIU_SIZE];
struct ufshcd_sg_entry prd_table[SG_ALL];
};
/**
* struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
* @dword0: Descriptor Header DW0
* @dword1: Descriptor Header DW1
* @dword2: Descriptor Header DW2
* @dword3: Descriptor Header DW3
*/
struct request_desc_header {
__le32 dword_0;
__le32 dword_1;
__le32 dword_2;
__le32 dword_3;
};
/**
* struct utp_transfer_req_desc - UTRD structure
* @header: UTRD header DW-0 to DW-3
* @command_desc_base_addr_lo: UCD base address low DW-4
* @command_desc_base_addr_hi: UCD base address high DW-5
* @response_upiu_length: response UPIU length DW-6
* @response_upiu_offset: response UPIU offset DW-6
* @prd_table_length: Physical region descriptor length DW-7
* @prd_table_offset: Physical region descriptor offset DW-7
*/
struct utp_transfer_req_desc {
/* DW 0-3 */
struct request_desc_header header;
/* DW 4-5*/
__le32 command_desc_base_addr_lo;
__le32 command_desc_base_addr_hi;
/* DW 6 */
__le16 response_upiu_length;
__le16 response_upiu_offset;
/* DW 7 */
__le16 prd_table_length;
__le16 prd_table_offset;
};
/**
* struct utp_task_req_desc - UTMRD structure
* @header: UTMRD header DW-0 to DW-3
* @task_req_upiu: Pointer to task request UPIU DW-4 to DW-11
* @task_rsp_upiu: Pointer to task response UPIU DW12 to DW-19
*/
struct utp_task_req_desc {
/* DW 0-3 */
struct request_desc_header header;
/* DW 4-11 */
__le32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
/* DW 12-19 */
__le32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
};
#endif /* End of Header */

231
drivers/scsi/ufs/unipro.h Normal file
View file

@ -0,0 +1,231 @@
/*
* drivers/scsi/ufs/unipro.h
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _UNIPRO_H_
#define _UNIPRO_H_
/*
* M-TX Configuration Attributes
*/
#define TX_MODE 0x0021
#define TX_HSRATE_SERIES 0x0022
#define TX_HSGEAR 0x0023
#define TX_PWMGEAR 0x0024
#define TX_AMPLITUDE 0x0025
#define TX_HS_SLEWRATE 0x0026
#define TX_SYNC_SOURCE 0x0027
#define TX_HS_SYNC_LENGTH 0x0028
#define TX_HS_PREPARE_LENGTH 0x0029
#define TX_LS_PREPARE_LENGTH 0x002A
#define TX_HIBERN8_CONTROL 0x002B
#define TX_LCC_ENABLE 0x002C
#define TX_PWM_BURST_CLOSURE_EXTENSION 0x002D
#define TX_BYPASS_8B10B_ENABLE 0x002E
#define TX_DRIVER_POLARITY 0x002F
#define TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE 0x0030
#define TX_LS_TERMINATED_LINE_DRIVE_ENABLE 0x0031
#define TX_LCC_SEQUENCER 0x0032
#define TX_MIN_ACTIVATETIME 0x0033
#define TX_PWM_G6_G7_SYNC_LENGTH 0x0034
/*
* M-RX Configuration Attributes
*/
#define RX_MODE 0x00A1
#define RX_HSRATE_SERIES 0x00A2
#define RX_HSGEAR 0x00A3
#define RX_PWMGEAR 0x00A4
#define RX_LS_TERMINATED_ENABLE 0x00A5
#define RX_HS_UNTERMINATED_ENABLE 0x00A6
#define RX_ENTER_HIBERN8 0x00A7
#define RX_BYPASS_8B10B_ENABLE 0x00A8
#define RX_TERMINATION_FORCE_ENABLE 0x0089
#define is_mphy_tx_attr(attr) (attr < RX_MODE)
/*
* PHY Adpater attributes
*/
#define PA_ACTIVETXDATALANES 0x1560
#define PA_ACTIVERXDATALANES 0x1580
#define PA_TXTRAILINGCLOCKS 0x1564
#define PA_PHY_TYPE 0x1500
#define PA_AVAILTXDATALANES 0x1520
#define PA_AVAILRXDATALANES 0x1540
#define PA_MINRXTRAILINGCLOCKS 0x1543
#define PA_TXPWRSTATUS 0x1567
#define PA_RXPWRSTATUS 0x1582
#define PA_TXFORCECLOCK 0x1562
#define PA_TXPWRMODE 0x1563
#define PA_LEGACYDPHYESCDL 0x1570
#define PA_MAXTXSPEEDFAST 0x1521
#define PA_MAXTXSPEEDSLOW 0x1522
#define PA_MAXRXSPEEDFAST 0x1541
#define PA_MAXRXSPEEDSLOW 0x1542
#define PA_TXLINKSTARTUPHS 0x1544
#define PA_TXSPEEDFAST 0x1565
#define PA_TXSPEEDSLOW 0x1566
#define PA_REMOTEVERINFO 0x15A0
#define PA_TXGEAR 0x1568
#define PA_TXTERMINATION 0x1569
#define PA_HSSERIES 0x156A
#define PA_PWRMODE 0x1571
#define PA_RXGEAR 0x1583
#define PA_RXTERMINATION 0x1584
#define PA_MAXRXPWMGEAR 0x1586
#define PA_MAXRXHSGEAR 0x1587
#define PA_RXHSUNTERMCAP 0x15A5
#define PA_RXLSTERMCAP 0x15A6
#define PA_PACPREQTIMEOUT 0x1590
#define PA_PACPREQEOBTIMEOUT 0x1591
#define PA_HIBERN8TIME 0x15A7
#define PA_LOCALVERINFO 0x15A9
#define PA_GRANULARITY 0x15AA
#define PA_TACTIVATE 0x15A8
#define PA_PACPFRAMECOUNT 0x15C0
#define PA_PACPERRORCOUNT 0x15C1
#define PA_PHYTESTCONTROL 0x15C2
#define PA_PWRMODEUSERDATA0 0x15B0
#define PA_PWRMODEUSERDATA1 0x15B1
#define PA_PWRMODEUSERDATA2 0x15B2
#define PA_PWRMODEUSERDATA3 0x15B3
#define PA_PWRMODEUSERDATA4 0x15B4
#define PA_PWRMODEUSERDATA5 0x15B5
#define PA_PWRMODEUSERDATA6 0x15B6
#define PA_PWRMODEUSERDATA7 0x15B7
#define PA_PWRMODEUSERDATA8 0x15B8
#define PA_PWRMODEUSERDATA9 0x15B9
#define PA_PWRMODEUSERDATA10 0x15BA
#define PA_PWRMODEUSERDATA11 0x15BB
#define PA_CONNECTEDTXDATALANES 0x1561
#define PA_CONNECTEDRXDATALANES 0x1581
#define PA_LOGICALLANEMAP 0x15A1
#define PA_SLEEPNOCONFIGTIME 0x15A2
#define PA_STALLNOCONFIGTIME 0x15A3
#define PA_SAVECONFIGTIME 0x15A4
/* PA power modes */
enum {
FAST_MODE = 1,
SLOW_MODE = 2,
FASTAUTO_MODE = 4,
SLOWAUTO_MODE = 5,
UNCHANGED = 7,
};
#define IS_PWR_MODE_HS(m) (((m) == FAST_MODE) || ((m) == FASTAUTO_MODE))
#define IS_PWR_MODE_PWM(m) (((m) == SLOW_MODE) || ((m) == SLOWAUTO_MODE))
/* PA TX/RX Frequency Series */
enum {
PA_HS_MODE_A = 1,
PA_HS_MODE_B = 2,
};
enum ufs_pwm_gear_tag {
UFS_PWM_DONT_CHANGE, /* Don't change Gear */
UFS_PWM_G1, /* PWM Gear 1 (default for reset) */
UFS_PWM_G2, /* PWM Gear 2 */
UFS_PWM_G3, /* PWM Gear 3 */
UFS_PWM_G4, /* PWM Gear 4 */
UFS_PWM_G5, /* PWM Gear 5 */
UFS_PWM_G6, /* PWM Gear 6 */
UFS_PWM_G7, /* PWM Gear 7 */
};
enum ufs_hs_gear_tag {
UFS_HS_DONT_CHANGE, /* Don't change Gear */
UFS_HS_G1, /* HS Gear 1 (default for reset) */
UFS_HS_G2, /* HS Gear 2 */
UFS_HS_G3, /* HS Gear 3 */
};
/*
* Data Link Layer Attributes
*/
#define DL_TC0TXFCTHRESHOLD 0x2040
#define DL_FC0PROTTIMEOUTVAL 0x2041
#define DL_TC0REPLAYTIMEOUTVAL 0x2042
#define DL_AFC0REQTIMEOUTVAL 0x2043
#define DL_AFC0CREDITTHRESHOLD 0x2044
#define DL_TC0OUTACKTHRESHOLD 0x2045
#define DL_TC1TXFCTHRESHOLD 0x2060
#define DL_FC1PROTTIMEOUTVAL 0x2061
#define DL_TC1REPLAYTIMEOUTVAL 0x2062
#define DL_AFC1REQTIMEOUTVAL 0x2063
#define DL_AFC1CREDITTHRESHOLD 0x2064
#define DL_TC1OUTACKTHRESHOLD 0x2065
#define DL_TXPREEMPTIONCAP 0x2000
#define DL_TC0TXMAXSDUSIZE 0x2001
#define DL_TC0RXINITCREDITVAL 0x2002
#define DL_TC0TXBUFFERSIZE 0x2005
#define DL_PEERTC0PRESENT 0x2046
#define DL_PEERTC0RXINITCREVAL 0x2047
#define DL_TC1TXMAXSDUSIZE 0x2003
#define DL_TC1RXINITCREDITVAL 0x2004
#define DL_TC1TXBUFFERSIZE 0x2006
#define DL_PEERTC1PRESENT 0x2066
#define DL_PEERTC1RXINITCREVAL 0x2067
/* Default value of L2 Timer */
#define FC0PROTTIMEOUTVAL 8191
#define TC0REPLAYTIMEOUTVAL 65535
#define AFC0REQTIMEOUTVAL 32767
/*
* Network Layer Attributes
*/
#define N_DEVICEID 0x3000
#define N_DEVICEID_VALID 0x3001
#define N_TC0TXMAXSDUSIZE 0x3020
#define N_TC1TXMAXSDUSIZE 0x3021
/*
* Transport Layer Attributes
*/
#define T_NUMCPORTS 0x4000
#define T_NUMTESTFEATURES 0x4001
#define T_CONNECTIONSTATE 0x4020
#define T_PEERDEVICEID 0x4021
#define T_PEERCPORTID 0x4022
#define T_TRAFFICCLASS 0x4023
#define T_PROTOCOLID 0x4024
#define T_CPORTFLAGS 0x4025
#define T_TXTOKENVALUE 0x4026
#define T_RXTOKENVALUE 0x4027
#define T_LOCALBUFFERSPACE 0x4028
#define T_PEERBUFFERSPACE 0x4029
#define T_CREDITSTOSEND 0x402A
#define T_CPORTMODE 0x402B
#define T_TC0TXMAXSDUSIZE 0x4060
#define T_TC1TXMAXSDUSIZE 0x4061
/* CPort setting */
#define E2EFC_ON (1 << 0)
#define E2EFC_OFF (0 << 0)
#define CSD_N_ON (0 << 1)
#define CSD_N_OFF (1 << 1)
#define CSV_N_ON (0 << 2)
#define CSV_N_OFF (1 << 2)
#define CPORT_DEF_FLAGS (CSV_N_OFF | CSD_N_OFF | E2EFC_OFF)
/* CPort connection state */
enum {
CPORT_IDLE = 0,
CPORT_CONNECTED,
};
/* Boolean attribute values */
enum {
FALSE = 0,
TRUE,
};
#endif /* _UNIPRO_H_ */