M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
#
# 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
---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 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.
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_TEST
tristate "Universal Flash Storage host controller driver unit-tests"
depends on SCSI_UFSHCD && IOSCHED_TEST
---help---
This adds UFS Host controller unit-test framework.
The UFS unit-tests register as a block device test utility to
the test-iosched and will be initiated when the test-iosched will
be chosen to be the active I/O scheduler.
+5
View File
@@ -0,0 +1,5 @@
# UFSHCD makefile
obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
obj-$(CONFIG_SCSI_UFS_TEST) += ufs_test.o
+303
View File
@@ -0,0 +1,303 @@
/*
* 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 UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \
(GENERAL_UPIU_REQUEST_SIZE))
#define QUERY_OSF_SIZE ((GENERAL_UPIU_REQUEST_SIZE) - \
(sizeof(struct utp_upiu_header)))
#define UFS_QUERY_RESERVED_SCSI_CMD 0xCC
#define UFS_QUERY_CMD_SIZE 10
#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
(byte1 << 8) | (byte0))
/*
* 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,
};
/* UTP QUERY Transaction Specific Fields OpCode */
enum {
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,
};
/* 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,
};
/* 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,
};
/**
* 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 {
u32 dword_0;
u32 dword_1;
u32 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 {
u32 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-6
* @reserved: spec reserved DW-7,8
*/
struct utp_upiu_query {
u8 opcode;
u8 idn;
u8 index;
u8 selector;
u16 reserved_osf;
u16 length;
u32 value;
u32 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
* @qr: fields structure for query request
*/
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 {
u32 residual_transfer_count;
u32 reserved[4];
u16 sense_data_len;
u8 sense_data[18];
};
/**
* struct utp_upiu_rsp - general upiu response structure
* @header: UPIU header structure DW-0 to DW-2
* @sc: fields structure for scsi command
* @qr: fields structure for query request
*/
struct utp_upiu_rsp {
struct utp_upiu_header header;
union {
struct utp_cmd_rsp sc;
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;
u32 input_param1;
u32 input_param2;
u32 input_param3;
u32 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;
u32 output_param1;
u32 output_param2;
u32 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;
};
#endif /* End of Header */
+348
View File
@@ -0,0 +1,348 @@
/* Copyright (c) 2013, 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 <linux/module.h>
#include <linux/blkdev.h>
#include <linux/debugfs.h>
#include <linux/test-iosched.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <../sd.h>
#define MODULE_NAME "ufs_test"
#define TEST_MAX_BIOS_PER_REQ 120
#define LARGE_PRIME_1 1103515367
#define LARGE_PRIME_2 35757
#define DEFAULT_NUM_OF_BIOS 2
#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
enum ufs_test_testcases {
UFS_TEST_WRITE_READ_TEST,
};
struct ufs_test_debug {
struct dentry *write_read_test; /* basic test */
struct dentry *random_test_seed; /* parameters in utils */
};
struct ufs_test_data {
/* Data structure for debugfs dentrys */
struct ufs_test_debug debug;
/*
* Data structure containing individual test information, including
* self-defined specific data
*/
struct test_info test_info;
/* device test */
struct blk_dev_test_type bdt;
/* A wait queue for OPs to complete */
wait_queue_head_t wait_q;
/* a flag for read compleation */
bool read_completed;
/* a flag for write compleation */
bool write_completed;
/*
* To determine the number of r/w bios. When seed = 0, random is
* disabled and 2 BIOs are written.
*/
unsigned int random_test_seed;
};
static struct ufs_test_data *utd;
static bool message_repeat;
static char *ufs_test_get_test_case_str(struct test_data *td)
{
if (!td) {
test_pr_err("%s: NULL td", __func__);
return NULL;
}
switch (td->test_info.testcase) {
case UFS_TEST_WRITE_READ_TEST:
return "UFS write read test";
break;
default:
return "Unknown test";
}
}
static unsigned int ufs_test_pseudo_random_seed(unsigned int *seed_number,
unsigned int min_val, unsigned int max_val)
{
int ret = 0;
if (!seed_number)
return 0;
*seed_number = ((unsigned int) (((unsigned long) *seed_number
* (unsigned long) LARGE_PRIME_1) + LARGE_PRIME_2));
ret = (unsigned int) ((*seed_number) % max_val);
return (ret > min_val ? ret : min_val);
}
static void ufs_test_pseudo_rnd_size(unsigned int *seed,
unsigned int *num_of_bios)
{
*num_of_bios = ufs_test_pseudo_random_seed(seed, 1,
TEST_MAX_BIOS_PER_REQ);
if (!(*num_of_bios))
*num_of_bios = DEFAULT_NUM_OF_BIOS;
}
static void ufs_test_write_read_test_end_io_fn(struct request *rq, int err)
{
struct test_request *test_rq = (struct test_request *)rq->elv.priv[0];
BUG_ON(!test_rq);
test_rq->req_completed = 1;
test_rq->req_result = err;
test_pr_info("%s: request %d completed, err=%d",
__func__, test_rq->req_id, err);
utd->write_completed = true;
wake_up(&utd->wait_q);
}
static struct gendisk *ufs_test_get_rq_disk(void)
{
struct request_queue *req_q = test_iosched_get_req_queue();
struct scsi_device *sd;
struct device *dev;
struct scsi_disk *sdkp;
struct gendisk *gd;
if (!req_q) {
test_pr_info("%s: Could not fetch request_queue", __func__);
gd = NULL;
goto exit;
}
sd = (struct scsi_device *)req_q->queuedata;
dev = &sd->sdev_gendev;
sdkp = scsi_disk_get_from_dev(dev);
if (!sdkp) {
test_pr_info("%s: Could not fatch scsi disk", __func__);
gd = NULL;
goto exit;
}
gd = sdkp->disk;
exit:
return gd;
}
static int ufs_test_run_write_read_test(struct test_data *td)
{
int ret = 0;
unsigned int start_sec;
unsigned int num_bios;
struct request_queue *q = td->req_q;
start_sec = td->start_sector + sizeof(int) * BIO_U32_SIZE
* td->num_of_write_bios;
if (utd->random_test_seed != 0)
ufs_test_pseudo_rnd_size(&utd->random_test_seed, &num_bios);
else
num_bios = DEFAULT_NUM_OF_BIOS;
/* Adding a write request */
test_pr_info("%s: Adding a write requests to Q, first req_id=%d",
__func__, td->wr_rd_next_req_id);
utd->write_completed = false;
ret = test_iosched_add_wr_rd_test_req(0, WRITE, start_sec,
num_bios, TEST_PATTERN_5A,
ufs_test_write_read_test_end_io_fn);
if (ret) {
test_pr_err("%s: failed to add a write request", __func__);
return ret;
}
/* waiting for the write request to finish */
blk_run_queue(q);
wait_event(utd->wait_q, utd->write_completed);
/* Adding a read request*/
test_pr_info("%s: Adding a read request to Q", __func__);
ret = test_iosched_add_wr_rd_test_req(0, READ, start_sec,
num_bios, TEST_PATTERN_5A, NULL);
if (ret) {
test_pr_err("%s: failed to add a read request", __func__);
return ret;
}
blk_run_queue(q);
return ret;
}
static
int ufs_test_write_read_test_open_cb(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
message_repeat = 1;
test_pr_info("%s:UFS test initialized", __func__);
return 0;
}
static ssize_t ufs_test_write_read_test_write_cb(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
int ret = 0;
int i;
int number;
sscanf(buf, "%d", &number);
if (number <= 0)
number = 1;
test_pr_info("%s:the test will run for %d iterations.",
__func__, number);
memset(&utd->test_info, 0, sizeof(struct test_info));
/* Initializing test */
utd->test_info.data = utd;
utd->test_info.get_test_case_str_fn = ufs_test_get_test_case_str;
utd->test_info.testcase = UFS_TEST_WRITE_READ_TEST;
utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;
utd->test_info.run_test_fn = ufs_test_run_write_read_test;
/* Running the test multiple times */
for (i = 0; i < number; ++i) {
ret = test_iosched_start_test(&utd->test_info);
if (ret) {
test_pr_err("%s: Test failed.", __func__);
return ret;
}
}
test_pr_info("%s: Completed all the ufs test iterations.", __func__);
return count;
}
static ssize_t ufs_test_write_read_test_read_cb(struct file *file,
char __user *buffer, size_t count, loff_t *offset)
{
memset((void *) buffer, 0, count);
snprintf(buffer, count, "\nThis is a UFS write-read test for debug.\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else
return 0;
}
const struct file_operations write_read_test_ops = {
.open = ufs_test_write_read_test_open_cb,
.write = ufs_test_write_read_test_write_cb,
.read = ufs_test_write_read_test_read_cb,
};
static void ufs_test_debugfs_cleanup(void)
{
debugfs_remove(utd->debug.write_read_test);
}
static int ufs_test_debugfs_init(void)
{
struct dentry *utils_root, *tests_root;
utils_root = test_iosched_get_debugfs_utils_root();
tests_root = test_iosched_get_debugfs_tests_root();
if (!utils_root || !tests_root) {
test_pr_err("%s: Failed to create debugfs root.", __func__);
return -EINVAL;
}
utd->debug.random_test_seed = debugfs_create_u32("random_test_seed",
S_IRUGO | S_IWUGO, utils_root, &utd->random_test_seed);
if (!utd->debug.random_test_seed) {
test_pr_err("%s: Could not create debugfs random_test_seed.",
__func__);
return -ENOMEM;
}
utd->debug.write_read_test = debugfs_create_file("write_read_test",
S_IRUGO | S_IWUGO, tests_root,
NULL, &write_read_test_ops);
if (!utd->debug.write_read_test) {
debugfs_remove(utd->debug.random_test_seed);
test_pr_err("%s: Could not create debugfs write_read_test.",
__func__);
return -ENOMEM;
}
return 0;
}
static void ufs_test_probe(void)
{
ufs_test_debugfs_init();
}
static void ufs_test_remove(void)
{
ufs_test_debugfs_cleanup();
}
int __init ufs_test_init(void)
{
utd = kzalloc(sizeof(struct ufs_test_data), GFP_KERNEL);
if (!utd) {
test_pr_err("%s: failed to allocate ufs_test_data", __func__);
return -ENODEV;
}
init_waitqueue_head(&utd->wait_q);
utd->bdt.init_fn = ufs_test_probe;
utd->bdt.exit_fn = ufs_test_remove;
INIT_LIST_HEAD(&utd->bdt.list);
test_iosched_register(&utd->bdt);
return 0;
}
EXPORT_SYMBOL_GPL(ufs_test_init);
static void __exit ufs_test_exit(void)
{
test_iosched_unregister(&utd->bdt);
kfree(utd);
}
module_init(ufs_test_init)
;
module_exit(ufs_test_exit)
;
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("UFC test");
+211
View File
@@ -0,0 +1,211 @@
/*
* 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>
#ifdef CONFIG_PM
/**
* ufshcd_pci_suspend - suspend power management function
* @pdev: pointer to PCI device handle
* @state: power state
*
* Returns -ENOSYS
*/
static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
/*
* TODO:
* 1. Call ufshcd_suspend
* 2. Do bus specific power management
*/
return -ENOSYS;
}
/**
* ufshcd_pci_resume - resume power management function
* @pdev: pointer to PCI device handle
*
* Returns -ENOSYS
*/
static int ufshcd_pci_resume(struct pci_dev *pdev)
{
/*
* TODO:
* 1. Call ufshcd_resume.
* 2. Do bus specific wake up
*/
return -ENOSYS;
}
#endif /* CONFIG_PM */
/**
* 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_hba_stop((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);
disable_irq(pdev->irq);
free_irq(pdev->irq, hba);
ufshcd_remove(hba);
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
pci_clear_master(pdev);
pci_disable_device(pdev);
}
/**
* ufshcd_set_dma_mask - Set dma mask based on the controller
* addressing capability
* @pdev: PCI device structure
*
* Returns 0 for success, non-zero for failure
*/
static int ufshcd_set_dma_mask(struct pci_dev *pdev)
{
int err;
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
&& !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
return 0;
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (!err)
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
return err;
}
/**
* 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 = pci_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "pci_enable_device failed\n");
goto out_error;
}
pci_set_master(pdev);
err = pci_request_regions(pdev, UFSHCD);
if (err < 0) {
dev_err(&pdev->dev, "request regions failed\n");
goto out_disable;
}
mmio_base = pci_ioremap_bar(pdev, 0);
if (!mmio_base) {
dev_err(&pdev->dev, "memory map failed\n");
err = -ENOMEM;
goto out_release_regions;
}
err = ufshcd_set_dma_mask(pdev);
if (err) {
dev_err(&pdev->dev, "set dma mask failed\n");
goto out_iounmap;
}
err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq);
if (err) {
dev_err(&pdev->dev, "Initialization failed\n");
goto out_iounmap;
}
pci_set_drvdata(pdev, hba);
return 0;
out_iounmap:
iounmap(mmio_base);
out_release_regions:
pci_release_regions(pdev);
out_disable:
pci_clear_master(pdev);
pci_disable_device(pdev);
out_error:
return err;
}
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,
#ifdef CONFIG_PM
.suspend = ufshcd_pci_suspend,
.resume = ufshcd_pci_resume,
#endif
};
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);
+217
View File
@@ -0,0 +1,217 @@
/*
* 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 "ufshcd.h"
#include <linux/platform_device.h>
#ifdef CONFIG_PM
/**
* ufshcd_pltfrm_suspend - suspend power management function
* @dev: pointer to device handle
*
*
* Returns 0
*/
static int ufshcd_pltfrm_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ufs_hba *hba = platform_get_drvdata(pdev);
/*
* TODO:
* 1. Call ufshcd_suspend
* 2. Do bus specific power management
*/
disable_irq(hba->irq);
return 0;
}
/**
* ufshcd_pltfrm_resume - resume power management function
* @dev: pointer to device handle
*
* Returns 0
*/
static int ufshcd_pltfrm_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ufs_hba *hba = platform_get_drvdata(pdev);
/*
* TODO:
* 1. Call ufshcd_resume.
* 2. Do bus specific wake up
*/
enable_irq(hba->irq);
return 0;
}
#else
#define ufshcd_pltfrm_suspend NULL
#define ufshcd_pltfrm_resume NULL
#endif
/**
* ufshcd_pltfrm_probe - probe routine of the 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)
{
struct ufs_hba *hba;
void __iomem *mmio_base;
struct resource *mem_res;
struct resource *irq_res;
resource_size_t mem_size;
int err;
struct device *dev = &pdev->dev;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev,
"Memory resource not available\n");
err = -ENODEV;
goto out_error;
}
mem_size = resource_size(mem_res);
if (!request_mem_region(mem_res->start, mem_size, "ufshcd")) {
dev_err(&pdev->dev,
"Cannot reserve the memory resource\n");
err = -EBUSY;
goto out_error;
}
mmio_base = ioremap_nocache(mem_res->start, mem_size);
if (!mmio_base) {
dev_err(&pdev->dev, "memory map failed\n");
err = -ENOMEM;
goto out_release_regions;
}
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
dev_err(&pdev->dev, "IRQ resource not available\n");
err = -ENODEV;
goto out_iounmap;
}
err = dma_set_coherent_mask(dev, dev->coherent_dma_mask);
if (err) {
dev_err(&pdev->dev, "set dma mask failed\n");
goto out_iounmap;
}
err = ufshcd_init(&pdev->dev, &hba, mmio_base, irq_res->start);
if (err) {
dev_err(&pdev->dev, "Intialization failed\n");
goto out_iounmap;
}
platform_set_drvdata(pdev, hba);
return 0;
out_iounmap:
iounmap(mmio_base);
out_release_regions:
release_mem_region(mem_res->start, mem_size);
out_error:
return err;
}
/**
* 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 resource *mem_res;
resource_size_t mem_size;
struct ufs_hba *hba = platform_get_drvdata(pdev);
disable_irq(hba->irq);
/* Some buggy controllers raise interrupt after
* the resources are removed. So first we unregister the
* irq handler and then the resources used by driver
*/
free_irq(hba->irq, hba);
ufshcd_remove(hba);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res)
dev_err(&pdev->dev, "ufshcd: Memory resource not available\n");
else {
mem_size = resource_size(mem_res);
release_mem_region(mem_res->start, mem_size);
}
platform_set_drvdata(pdev, NULL);
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,
};
static struct platform_driver ufshcd_pltfrm_driver = {
.probe = ufshcd_pltfrm_probe,
.remove = ufshcd_pltfrm_remove,
.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);
File diff suppressed because it is too large Load Diff
+220
View File
@@ -0,0 +1,220 @@
/*
* 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 <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 <scsi/scsi_device.h>
#include "ufs.h"
#include "ufshci.h"
#define UFSHCD "ufshcd"
#define UFSHCD_DRIVER_VERSION "0.2"
/**
* 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
*/
struct uic_command {
u32 command;
u32 argument1;
u32 argument2;
u32 argument3;
int cmd_active;
int result;
};
/**
* 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
*/
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;
unsigned int lun;
};
/**
* struct ufs_query - keeps the query request information
* @request: request upiu and function
* @descriptor: buffer for sending/receiving descriptor
* @response: response upiu and response
* @mutex: lock to allow one query at a time
*/
struct ufs_query {
struct ufs_query_req *request;
u8 *descriptor;
struct ufs_query_res *response;
struct mutex lock_ufs_query;
};
/**
* 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
* @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
* @irq: Irq number of the controller
* @active_uic_cmd: handle of active UIC command
* @ufshcd_tm_wait_queue: wait queue for task management
* @tm_condition: condition variable for task management
* @ufshcd_state: UFSHCD states
* @int_enable_mask: Interrupt Mask Bits
* @uic_workq: Work queue for UIC completion handling
* @feh_workq: Work queue for fatal controller error handling
* @errors: HBA errors
* @query: query request information
*/
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;
struct ufshcd_lrb *lrb;
unsigned long outstanding_tasks;
unsigned long outstanding_reqs;
u32 capabilities;
int nutrs;
int nutmrs;
u32 ufs_version;
unsigned int irq;
struct uic_command active_uic_cmd;
wait_queue_head_t ufshcd_tm_wait_queue;
unsigned long tm_condition;
u32 ufshcd_state;
u32 int_enable_mask;
/* Work Queues */
struct work_struct uic_workq;
struct work_struct feh_workq;
/* HBA Errors */
u32 errors;
/* Query Request */
struct ufs_query query;
};
int ufshcd_init(struct device *, 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)
{
writel(CONTROLLER_DISABLE, (hba->mmio_base + REG_CONTROLLER_ENABLE));
}
#endif /* End of Header */
+366
View File
@@ -0,0 +1,366 @@
/*
* 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_ERROR_MASK (UIC_ERROR |\
DEVICE_FATAL_ERROR |\
CONTROLLER_FATAL_ERROR |\
SYSTEM_BUS_FATAL_ERROR)
#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
CONTROLLER_FATAL_ERROR |\
SYSTEM_BUS_FATAL_ERROR)
/* 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)
/* 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_PA_INIT 0x2000
/* UECN - Host UIC Error Code Network Layer 40h */
#define UIC_NETWORK_LAYER_ERROR UFS_BIT(31)
#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7
/* UECT - Host UIC Error Code Transport Layer 44h */
#define UIC_TRANSPORT_LAYER_ERROR UFS_BIT(31)
#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F
/* 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
/* UIC Commands */
enum {
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_THRESHOLD_VALUE (0x1F << 8)
#define INT_AGGR_TIMEOUT_VALUE (0x02)
/* Interrupt disable masks */
enum {
/* Interrupt disable mask for UFSHCI v1.0 */
INTERRUPT_DISABLE_MASK_10 = 0xFFFF,
/* Interrupt disable mask for UFSHCI v1.1 */
INTERRUPT_DISABLE_MASK_11 = 0x0,
};
/*
* 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,
};
/**
* 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 {
u32 base_addr;
u32 upper_addr;
u32 reserved;
u32 size;
};
/**
* 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 {
u32 dword_0;
u32 dword_1;
u32 dword_2;
u32 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*/
u32 command_desc_base_addr_lo;
u32 command_desc_base_addr_hi;
/* DW 6 */
u16 response_upiu_length;
u16 response_upiu_offset;
/* DW 7 */
u16 prd_table_length;
u16 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 */
u32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
/* DW 12-19 */
u32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
};
#endif /* End of Header */