1/* 2 * Universal Flash Storage Host controller PCI glue driver 3 * 4 * This code is based on drivers/scsi/ufs/ufshcd-pci.c 5 * Copyright (C) 2011-2013 Samsung India Software Operations 6 * 7 * Authors: 8 * Santosh Yaraganavi <santosh.sy@samsung.com> 9 * Vinayak Holikatti <h.vinayak@samsung.com> 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 2 14 * of the License, or (at your option) any later version. 15 * See the COPYING file in the top-level directory or visit 16 * <http://www.gnu.org/licenses/gpl-2.0.html> 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * This program is provided "AS IS" and "WITH ALL FAULTS" and 24 * without warranty of any kind. You are solely responsible for 25 * determining the appropriateness of using and distributing 26 * the program and assume all risks associated with your exercise 27 * of rights with respect to the program, including but not limited 28 * to infringement of third party rights, the risks and costs of 29 * program errors, damage to or loss of data, programs or equipment, 30 * and unavailability or interruption of operations. Under no 31 * circumstances will the contributor of this Program be liable for 32 * any damages of any kind arising from your use or distribution of 33 * this program. 34 */ 35 36#include "ufshcd.h" 37#include <linux/pci.h> 38#include <linux/pm_runtime.h> 39 40#ifdef CONFIG_PM 41/** 42 * ufshcd_pci_suspend - suspend power management function 43 * @pdev: pointer to PCI device handle 44 * @state: power state 45 * 46 * Returns 0 if successful 47 * Returns non-zero otherwise 48 */ 49static int ufshcd_pci_suspend(struct device *dev) 50{ 51 return ufshcd_system_suspend(dev_get_drvdata(dev)); 52} 53 54/** 55 * ufshcd_pci_resume - resume power management function 56 * @pdev: pointer to PCI device handle 57 * 58 * Returns 0 if successful 59 * Returns non-zero otherwise 60 */ 61static int ufshcd_pci_resume(struct device *dev) 62{ 63 return ufshcd_system_resume(dev_get_drvdata(dev)); 64} 65 66static int ufshcd_pci_runtime_suspend(struct device *dev) 67{ 68 return ufshcd_runtime_suspend(dev_get_drvdata(dev)); 69} 70static int ufshcd_pci_runtime_resume(struct device *dev) 71{ 72 return ufshcd_runtime_resume(dev_get_drvdata(dev)); 73} 74static int ufshcd_pci_runtime_idle(struct device *dev) 75{ 76 return ufshcd_runtime_idle(dev_get_drvdata(dev)); 77} 78#else /* !CONFIG_PM */ 79#define ufshcd_pci_suspend NULL 80#define ufshcd_pci_resume NULL 81#define ufshcd_pci_runtime_suspend NULL 82#define ufshcd_pci_runtime_resume NULL 83#define ufshcd_pci_runtime_idle NULL 84#endif /* CONFIG_PM */ 85 86/** 87 * ufshcd_pci_shutdown - main function to put the controller in reset state 88 * @pdev: pointer to PCI device handle 89 */ 90static void ufshcd_pci_shutdown(struct pci_dev *pdev) 91{ 92 ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev)); 93} 94 95/** 96 * ufshcd_pci_remove - de-allocate PCI/SCSI host and host memory space 97 * data structure memory 98 * @pdev - pointer to PCI handle 99 */ 100static void ufshcd_pci_remove(struct pci_dev *pdev) 101{ 102 struct ufs_hba *hba = pci_get_drvdata(pdev); 103 104 pm_runtime_forbid(&pdev->dev); 105 pm_runtime_get_noresume(&pdev->dev); 106 ufshcd_remove(hba); 107} 108 109/** 110 * ufshcd_pci_probe - probe routine of the driver 111 * @pdev: pointer to PCI device handle 112 * @id: PCI device id 113 * 114 * Returns 0 on success, non-zero value on failure 115 */ 116static int 117ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 118{ 119 struct ufs_hba *hba; 120 void __iomem *mmio_base; 121 int err; 122 123 err = pcim_enable_device(pdev); 124 if (err) { 125 dev_err(&pdev->dev, "pcim_enable_device failed\n"); 126 return err; 127 } 128 129 pci_set_master(pdev); 130 131 err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD); 132 if (err < 0) { 133 dev_err(&pdev->dev, "request and iomap failed\n"); 134 return err; 135 } 136 137 mmio_base = pcim_iomap_table(pdev)[0]; 138 139 err = ufshcd_alloc_host(&pdev->dev, &hba); 140 if (err) { 141 dev_err(&pdev->dev, "Allocation failed\n"); 142 return err; 143 } 144 145 INIT_LIST_HEAD(&hba->clk_list_head); 146 147 err = ufshcd_init(hba, mmio_base, pdev->irq); 148 if (err) { 149 dev_err(&pdev->dev, "Initialization failed\n"); 150 return err; 151 } 152 153 pci_set_drvdata(pdev, hba); 154 pm_runtime_put_noidle(&pdev->dev); 155 pm_runtime_allow(&pdev->dev); 156 157 return 0; 158} 159 160static const struct dev_pm_ops ufshcd_pci_pm_ops = { 161 .suspend = ufshcd_pci_suspend, 162 .resume = ufshcd_pci_resume, 163 .runtime_suspend = ufshcd_pci_runtime_suspend, 164 .runtime_resume = ufshcd_pci_runtime_resume, 165 .runtime_idle = ufshcd_pci_runtime_idle, 166}; 167 168static const struct pci_device_id ufshcd_pci_tbl[] = { 169 { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 170 { } /* terminate list */ 171}; 172 173MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl); 174 175static struct pci_driver ufshcd_pci_driver = { 176 .name = UFSHCD, 177 .id_table = ufshcd_pci_tbl, 178 .probe = ufshcd_pci_probe, 179 .remove = ufshcd_pci_remove, 180 .shutdown = ufshcd_pci_shutdown, 181 .driver = { 182 .pm = &ufshcd_pci_pm_ops 183 }, 184}; 185 186module_pci_driver(ufshcd_pci_driver); 187 188MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); 189MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); 190MODULE_DESCRIPTION("UFS host controller PCI glue driver"); 191MODULE_LICENSE("GPL"); 192MODULE_VERSION(UFSHCD_DRIVER_VERSION); 193