|
@@ -2,45 +2,35 @@
|
|
|
* Universal Flash Storage Host controller driver
|
|
|
*
|
|
|
* This code is based on drivers/scsi/ufs/ufshcd.c
|
|
|
- * Copyright (C) 2011-2012 Samsung India Software Operations
|
|
|
+ * Copyright (C) 2011-2013 Samsung India Software Operations
|
|
|
*
|
|
|
- * Santosh Yaraganavi <santosh.sy@samsung.com>
|
|
|
- * Vinayak Holikatti <h.vinayak@samsung.com>
|
|
|
+ * 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.
|
|
|
*
|
|
|
- * NO WARRANTY
|
|
|
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
|
|
|
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
|
|
|
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
|
|
|
- * solely responsible for determining the appropriateness of using and
|
|
|
- * distributing the Program and assumes all risks associated with its
|
|
|
- * exercise of rights under this Agreement, including but not limited to
|
|
|
- * the risks and costs of program errors, damage to or loss of data,
|
|
|
- * programs or equipment, and unavailability or interruption of operations.
|
|
|
-
|
|
|
- * DISCLAIMER OF LIABILITY
|
|
|
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
|
|
|
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
|
|
|
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
|
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
|
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
|
|
|
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
|
|
|
-
|
|
|
- * You should have received a copy of the GNU General Public License
|
|
|
- * along with this program; if not, write to the Free Software
|
|
|
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
|
- * USA.
|
|
|
+ * 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/module.h>
|
|
@@ -129,13 +119,15 @@ struct uic_command {
|
|
|
* @utrdl_dma_addr: UTRDL DMA address
|
|
|
* @utmrdl_dma_addr: UTMRDL DMA address
|
|
|
* @host: Scsi_Host instance of the driver
|
|
|
- * @pdev: PCI device handle
|
|
|
+ * @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
|
|
@@ -159,7 +151,7 @@ struct ufs_hba {
|
|
|
dma_addr_t utmrdl_dma_addr;
|
|
|
|
|
|
struct Scsi_Host *host;
|
|
|
- struct pci_dev *pdev;
|
|
|
+ struct device *dev;
|
|
|
|
|
|
struct ufshcd_lrb *lrb;
|
|
|
|
|
@@ -170,6 +162,7 @@ struct ufs_hba {
|
|
|
int nutrs;
|
|
|
int nutmrs;
|
|
|
u32 ufs_version;
|
|
|
+ unsigned int irq;
|
|
|
|
|
|
struct uic_command active_uic_cmd;
|
|
|
wait_queue_head_t ufshcd_tm_wait_queue;
|
|
@@ -335,21 +328,21 @@ static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
|
|
|
|
|
|
if (hba->utmrdl_base_addr) {
|
|
|
utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
|
|
|
- dma_free_coherent(&hba->pdev->dev, utmrdl_size,
|
|
|
+ dma_free_coherent(hba->dev, utmrdl_size,
|
|
|
hba->utmrdl_base_addr, hba->utmrdl_dma_addr);
|
|
|
}
|
|
|
|
|
|
if (hba->utrdl_base_addr) {
|
|
|
utrdl_size =
|
|
|
(sizeof(struct utp_transfer_req_desc) * hba->nutrs);
|
|
|
- dma_free_coherent(&hba->pdev->dev, utrdl_size,
|
|
|
+ dma_free_coherent(hba->dev, utrdl_size,
|
|
|
hba->utrdl_base_addr, hba->utrdl_dma_addr);
|
|
|
}
|
|
|
|
|
|
if (hba->ucdl_base_addr) {
|
|
|
ucdl_size =
|
|
|
(sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
|
|
|
- dma_free_coherent(&hba->pdev->dev, ucdl_size,
|
|
|
+ dma_free_coherent(hba->dev, ucdl_size,
|
|
|
hba->ucdl_base_addr, hba->ucdl_dma_addr);
|
|
|
}
|
|
|
}
|
|
@@ -724,7 +717,7 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
|
|
|
|
|
|
/* Allocate memory for UTP command descriptors */
|
|
|
ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
|
|
|
- hba->ucdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
|
|
|
+ hba->ucdl_base_addr = dma_alloc_coherent(hba->dev,
|
|
|
ucdl_size,
|
|
|
&hba->ucdl_dma_addr,
|
|
|
GFP_KERNEL);
|
|
@@ -737,7 +730,7 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
|
|
|
*/
|
|
|
if (!hba->ucdl_base_addr ||
|
|
|
WARN_ON(hba->ucdl_dma_addr & (PAGE_SIZE - 1))) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Command Descriptor Memory allocation failed\n");
|
|
|
goto out;
|
|
|
}
|
|
@@ -747,13 +740,13 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
|
|
|
* UFSHCI requires 1024 byte alignment of UTRD
|
|
|
*/
|
|
|
utrdl_size = (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
|
|
|
- hba->utrdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
|
|
|
+ hba->utrdl_base_addr = dma_alloc_coherent(hba->dev,
|
|
|
utrdl_size,
|
|
|
&hba->utrdl_dma_addr,
|
|
|
GFP_KERNEL);
|
|
|
if (!hba->utrdl_base_addr ||
|
|
|
WARN_ON(hba->utrdl_dma_addr & (PAGE_SIZE - 1))) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Transfer Descriptor Memory allocation failed\n");
|
|
|
goto out;
|
|
|
}
|
|
@@ -763,13 +756,13 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
|
|
|
* UFSHCI requires 1024 byte alignment of UTMRD
|
|
|
*/
|
|
|
utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
|
|
|
- hba->utmrdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
|
|
|
+ hba->utmrdl_base_addr = dma_alloc_coherent(hba->dev,
|
|
|
utmrdl_size,
|
|
|
&hba->utmrdl_dma_addr,
|
|
|
GFP_KERNEL);
|
|
|
if (!hba->utmrdl_base_addr ||
|
|
|
WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Task Management Descriptor Memory allocation failed\n");
|
|
|
goto out;
|
|
|
}
|
|
@@ -777,7 +770,7 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
|
|
|
/* Allocate memory for local reference block */
|
|
|
hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL);
|
|
|
if (!hba->lrb) {
|
|
|
- dev_err(&hba->pdev->dev, "LRB Memory allocation failed\n");
|
|
|
+ dev_err(hba->dev, "LRB Memory allocation failed\n");
|
|
|
goto out;
|
|
|
}
|
|
|
return 0;
|
|
@@ -867,7 +860,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
|
|
|
/* check if controller is ready to accept UIC commands */
|
|
|
if (((readl(hba->mmio_base + REG_CONTROLLER_STATUS)) &
|
|
|
UIC_COMMAND_READY) == 0x0) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Controller not ready"
|
|
|
" to accept UIC commands\n");
|
|
|
return -EIO;
|
|
@@ -912,7 +905,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
|
|
|
/* check if device present */
|
|
|
reg = readl((hba->mmio_base + REG_CONTROLLER_STATUS));
|
|
|
if (!ufshcd_is_device_present(reg)) {
|
|
|
- dev_err(&hba->pdev->dev, "cc: Device not present\n");
|
|
|
+ dev_err(hba->dev, "cc: Device not present\n");
|
|
|
err = -ENXIO;
|
|
|
goto out;
|
|
|
}
|
|
@@ -924,7 +917,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
|
|
|
if (!(ufshcd_get_lists_status(reg))) {
|
|
|
ufshcd_enable_run_stop_reg(hba);
|
|
|
} else {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Host controller not ready to process requests");
|
|
|
err = -EIO;
|
|
|
goto out;
|
|
@@ -1005,7 +998,7 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
|
|
|
if (retry) {
|
|
|
retry--;
|
|
|
} else {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Controller enable failed\n");
|
|
|
return -EIO;
|
|
|
}
|
|
@@ -1084,7 +1077,7 @@ static int ufshcd_do_reset(struct ufs_hba *hba)
|
|
|
|
|
|
/* start the initialization process */
|
|
|
if (ufshcd_initialize_hba(hba)) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Reset: Controller initialization failed\n");
|
|
|
return FAILED;
|
|
|
}
|
|
@@ -1167,7 +1160,7 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
|
|
|
task_result = SUCCESS;
|
|
|
} else {
|
|
|
task_result = FAILED;
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"trc: Invalid ocs = %x\n", ocs_value);
|
|
|
}
|
|
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
|
@@ -1281,7 +1274,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
|
|
|
/* check if the returned transfer response is valid */
|
|
|
result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
|
|
|
if (result) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Invalid response = %x\n", result);
|
|
|
break;
|
|
|
}
|
|
@@ -1310,7 +1303,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
|
|
|
case OCS_FATAL_ERROR:
|
|
|
default:
|
|
|
result |= DID_ERROR << 16;
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"OCS error from controller = %x\n", ocs);
|
|
|
break;
|
|
|
} /* end of switch */
|
|
@@ -1374,7 +1367,7 @@ static void ufshcd_uic_cc_handler (struct work_struct *work)
|
|
|
!(ufshcd_get_uic_cmd_result(hba))) {
|
|
|
|
|
|
if (ufshcd_make_hba_operational(hba))
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"cc: hba not operational state\n");
|
|
|
return;
|
|
|
}
|
|
@@ -1509,7 +1502,7 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
|
|
|
free_slot = ufshcd_get_tm_free_slot(hba);
|
|
|
if (free_slot >= hba->nutmrs) {
|
|
|
spin_unlock_irqrestore(host->host_lock, flags);
|
|
|
- dev_err(&hba->pdev->dev, "Task management queue full\n");
|
|
|
+ dev_err(hba->dev, "Task management queue full\n");
|
|
|
err = FAILED;
|
|
|
goto out;
|
|
|
}
|
|
@@ -1552,7 +1545,7 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
|
|
|
&hba->tm_condition) != 0),
|
|
|
60 * HZ);
|
|
|
if (!err) {
|
|
|
- dev_err(&hba->pdev->dev,
|
|
|
+ dev_err(hba->dev,
|
|
|
"Task management command timed-out\n");
|
|
|
err = FAILED;
|
|
|
goto out;
|
|
@@ -1688,23 +1681,22 @@ static struct scsi_host_template ufshcd_driver_template = {
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * ufshcd_shutdown - main function to put the controller in reset state
|
|
|
+ * ufshcd_pci_shutdown - main function to put the controller in reset state
|
|
|
* @pdev: pointer to PCI device handle
|
|
|
*/
|
|
|
-static void ufshcd_shutdown(struct pci_dev *pdev)
|
|
|
+static void ufshcd_pci_shutdown(struct pci_dev *pdev)
|
|
|
{
|
|
|
ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_PM
|
|
|
/**
|
|
|
* ufshcd_suspend - suspend power management function
|
|
|
- * @pdev: pointer to PCI device handle
|
|
|
+ * @hba: per adapter instance
|
|
|
* @state: power state
|
|
|
*
|
|
|
* Returns -ENOSYS
|
|
|
*/
|
|
|
-static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
+int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state)
|
|
|
{
|
|
|
/*
|
|
|
* TODO:
|
|
@@ -1717,14 +1709,15 @@ static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
|
|
|
return -ENOSYS;
|
|
|
}
|
|
|
+EXPORT_SYMBOL_GPL(ufshcd_suspend);
|
|
|
|
|
|
/**
|
|
|
* ufshcd_resume - resume power management function
|
|
|
- * @pdev: pointer to PCI device handle
|
|
|
+ * @hba: per adapter instance
|
|
|
*
|
|
|
* Returns -ENOSYS
|
|
|
*/
|
|
|
-static int ufshcd_resume(struct pci_dev *pdev)
|
|
|
+int ufshcd_resume(struct ufs_hba *hba)
|
|
|
{
|
|
|
/*
|
|
|
* TODO:
|
|
@@ -1737,6 +1730,43 @@ static int ufshcd_resume(struct pci_dev *pdev)
|
|
|
|
|
|
return -ENOSYS;
|
|
|
}
|
|
|
+EXPORT_SYMBOL_GPL(ufshcd_resume);
|
|
|
+
|
|
|
+#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 */
|
|
|
|
|
|
/**
|
|
@@ -1748,27 +1778,39 @@ static void ufshcd_hba_free(struct ufs_hba *hba)
|
|
|
{
|
|
|
iounmap(hba->mmio_base);
|
|
|
ufshcd_free_hba_memory(hba);
|
|
|
- pci_release_regions(hba->pdev);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * ufshcd_remove - de-allocate PCI/SCSI host and host memory space
|
|
|
+ * ufshcd_remove - de-allocate SCSI host and host memory space
|
|
|
* data structure memory
|
|
|
- * @pdev - pointer to PCI handle
|
|
|
+ * @hba - per adapter instance
|
|
|
*/
|
|
|
-static void ufshcd_remove(struct pci_dev *pdev)
|
|
|
+void ufshcd_remove(struct ufs_hba *hba)
|
|
|
{
|
|
|
- struct ufs_hba *hba = pci_get_drvdata(pdev);
|
|
|
-
|
|
|
/* disable interrupts */
|
|
|
ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
|
|
|
- free_irq(pdev->irq, hba);
|
|
|
|
|
|
ufshcd_hba_stop(hba);
|
|
|
ufshcd_hba_free(hba);
|
|
|
|
|
|
scsi_remove_host(hba->host);
|
|
|
scsi_host_put(hba->host);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ufshcd_remove);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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);
|
|
@@ -1781,74 +1823,60 @@ static void ufshcd_remove(struct pci_dev *pdev)
|
|
|
*
|
|
|
* Returns 0 for success, non-zero for failure
|
|
|
*/
|
|
|
-static int ufshcd_set_dma_mask(struct ufs_hba *hba)
|
|
|
+static int ufshcd_set_dma_mask(struct pci_dev *pdev)
|
|
|
{
|
|
|
int err;
|
|
|
- u64 dma_mask;
|
|
|
-
|
|
|
- /*
|
|
|
- * If controller supports 64 bit addressing mode, then set the DMA
|
|
|
- * mask to 64-bit, else set the DMA mask to 32-bit
|
|
|
- */
|
|
|
- if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT)
|
|
|
- dma_mask = DMA_BIT_MASK(64);
|
|
|
- else
|
|
|
- dma_mask = DMA_BIT_MASK(32);
|
|
|
-
|
|
|
- err = pci_set_dma_mask(hba->pdev, dma_mask);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
- err = pci_set_consistent_dma_mask(hba->pdev, dma_mask);
|
|
|
|
|
|
+ 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_probe - probe routine of the driver
|
|
|
- * @pdev: pointer to PCI device handle
|
|
|
- * @id: PCI device id
|
|
|
- *
|
|
|
+ * ufshcd_init - Driver initialization routine
|
|
|
+ * @dev: pointer to device handle
|
|
|
+ * @hba_handle: driver private handle
|
|
|
+ * @mmio_base: base register address
|
|
|
+ * @irq: Interrupt line of device
|
|
|
* Returns 0 on success, non-zero value on failure
|
|
|
*/
|
|
|
-static int ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
+int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
|
|
|
+ void __iomem *mmio_base, unsigned int irq)
|
|
|
{
|
|
|
struct Scsi_Host *host;
|
|
|
struct ufs_hba *hba;
|
|
|
int err;
|
|
|
|
|
|
- err = pci_enable_device(pdev);
|
|
|
- if (err) {
|
|
|
- dev_err(&pdev->dev, "pci_enable_device failed\n");
|
|
|
+ if (!dev) {
|
|
|
+ dev_err(dev,
|
|
|
+ "Invalid memory reference for dev is NULL\n");
|
|
|
+ err = -ENODEV;
|
|
|
goto out_error;
|
|
|
}
|
|
|
|
|
|
- pci_set_master(pdev);
|
|
|
+ if (!mmio_base) {
|
|
|
+ dev_err(dev,
|
|
|
+ "Invalid memory reference for mmio_base is NULL\n");
|
|
|
+ err = -ENODEV;
|
|
|
+ goto out_error;
|
|
|
+ }
|
|
|
|
|
|
host = scsi_host_alloc(&ufshcd_driver_template,
|
|
|
sizeof(struct ufs_hba));
|
|
|
if (!host) {
|
|
|
- dev_err(&pdev->dev, "scsi_host_alloc failed\n");
|
|
|
+ dev_err(dev, "scsi_host_alloc failed\n");
|
|
|
err = -ENOMEM;
|
|
|
- goto out_disable;
|
|
|
+ goto out_error;
|
|
|
}
|
|
|
hba = shost_priv(host);
|
|
|
-
|
|
|
- err = pci_request_regions(pdev, UFSHCD);
|
|
|
- if (err < 0) {
|
|
|
- dev_err(&pdev->dev, "request regions failed\n");
|
|
|
- goto out_host_put;
|
|
|
- }
|
|
|
-
|
|
|
- hba->mmio_base = pci_ioremap_bar(pdev, 0);
|
|
|
- if (!hba->mmio_base) {
|
|
|
- dev_err(&pdev->dev, "memory map failed\n");
|
|
|
- err = -ENOMEM;
|
|
|
- goto out_release_regions;
|
|
|
- }
|
|
|
-
|
|
|
hba->host = host;
|
|
|
- hba->pdev = pdev;
|
|
|
+ hba->dev = dev;
|
|
|
+ hba->mmio_base = mmio_base;
|
|
|
+ hba->irq = irq;
|
|
|
|
|
|
/* Read capabilities registers */
|
|
|
ufshcd_hba_capabilities(hba);
|
|
@@ -1856,17 +1884,11 @@ static int ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
/* Get UFS version supported by the controller */
|
|
|
hba->ufs_version = ufshcd_get_ufs_version(hba);
|
|
|
|
|
|
- err = ufshcd_set_dma_mask(hba);
|
|
|
- if (err) {
|
|
|
- dev_err(&pdev->dev, "set dma mask failed\n");
|
|
|
- goto out_iounmap;
|
|
|
- }
|
|
|
-
|
|
|
/* Allocate memory for host memory space */
|
|
|
err = ufshcd_memory_alloc(hba);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev, "Memory allocation failed\n");
|
|
|
- goto out_iounmap;
|
|
|
+ dev_err(hba->dev, "Memory allocation failed\n");
|
|
|
+ goto out_disable;
|
|
|
}
|
|
|
|
|
|
/* Configure LRB */
|
|
@@ -1888,46 +1910,102 @@ static int ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
|
|
|
|
|
|
/* IRQ registration */
|
|
|
- err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
|
|
|
+ err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev, "request irq failed\n");
|
|
|
+ dev_err(hba->dev, "request irq failed\n");
|
|
|
goto out_lrb_free;
|
|
|
}
|
|
|
|
|
|
/* Enable SCSI tag mapping */
|
|
|
err = scsi_init_shared_tag_map(host, host->can_queue);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev, "init shared queue failed\n");
|
|
|
+ dev_err(hba->dev, "init shared queue failed\n");
|
|
|
goto out_free_irq;
|
|
|
}
|
|
|
|
|
|
- pci_set_drvdata(pdev, hba);
|
|
|
-
|
|
|
- err = scsi_add_host(host, &pdev->dev);
|
|
|
+ err = scsi_add_host(host, hba->dev);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev, "scsi_add_host failed\n");
|
|
|
+ dev_err(hba->dev, "scsi_add_host failed\n");
|
|
|
goto out_free_irq;
|
|
|
}
|
|
|
|
|
|
/* Initialization routine */
|
|
|
err = ufshcd_initialize_hba(hba);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev, "Initialization failed\n");
|
|
|
- goto out_free_irq;
|
|
|
+ dev_err(hba->dev, "Initialization failed\n");
|
|
|
+ goto out_remove_scsi_host;
|
|
|
}
|
|
|
+ *hba_handle = hba;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+out_remove_scsi_host:
|
|
|
+ scsi_remove_host(hba->host);
|
|
|
out_free_irq:
|
|
|
- free_irq(pdev->irq, hba);
|
|
|
+ free_irq(irq, hba);
|
|
|
out_lrb_free:
|
|
|
ufshcd_free_hba_memory(hba);
|
|
|
+out_disable:
|
|
|
+ scsi_host_put(host);
|
|
|
+out_error:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(ufshcd_init);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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(hba->mmio_base);
|
|
|
+ iounmap(mmio_base);
|
|
|
out_release_regions:
|
|
|
pci_release_regions(pdev);
|
|
|
-out_host_put:
|
|
|
- scsi_host_put(host);
|
|
|
out_disable:
|
|
|
pci_clear_master(pdev);
|
|
|
pci_disable_device(pdev);
|
|
@@ -1945,19 +2023,19 @@ MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
|
|
|
static struct pci_driver ufshcd_pci_driver = {
|
|
|
.name = UFSHCD,
|
|
|
.id_table = ufshcd_pci_tbl,
|
|
|
- .probe = ufshcd_probe,
|
|
|
- .remove = ufshcd_remove,
|
|
|
- .shutdown = ufshcd_shutdown,
|
|
|
+ .probe = ufshcd_pci_probe,
|
|
|
+ .remove = ufshcd_pci_remove,
|
|
|
+ .shutdown = ufshcd_pci_shutdown,
|
|
|
#ifdef CONFIG_PM
|
|
|
- .suspend = ufshcd_suspend,
|
|
|
- .resume = ufshcd_resume,
|
|
|
+ .suspend = ufshcd_pci_suspend,
|
|
|
+ .resume = ufshcd_pci_resume,
|
|
|
#endif
|
|
|
};
|
|
|
|
|
|
module_pci_driver(ufshcd_pci_driver);
|
|
|
|
|
|
-MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>, "
|
|
|
- "Vinayak Holikatti <h.vinayak@samsung.com>");
|
|
|
+MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
|
|
|
+MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
|
|
|
MODULE_DESCRIPTION("Generic UFS host controller driver");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
MODULE_VERSION(UFSHCD_DRIVER_VERSION);
|