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#else 66#define ufshcd_pci_suspend NULL 67#define ufshcd_pci_resume NULL 68#endif /* CONFIG_PM */ 69 70#ifdef CONFIG_PM_RUNTIME 71static int ufshcd_pci_runtime_suspend(struct device *dev) 72{ 73 return ufshcd_runtime_suspend(dev_get_drvdata(dev)); 74} 75static int ufshcd_pci_runtime_resume(struct device *dev) 76{ 77 return ufshcd_runtime_resume(dev_get_drvdata(dev)); 78} 79static int ufshcd_pci_runtime_idle(struct device *dev) 80{ 81 return ufshcd_runtime_idle(dev_get_drvdata(dev)); 82} 83#else /* !CONFIG_PM_RUNTIME */ 84#define ufshcd_pci_runtime_suspend NULL 85#define ufshcd_pci_runtime_resume NULL 86#define ufshcd_pci_runtime_idle NULL 87#endif /* CONFIG_PM_RUNTIME */ 88 89/** 90 * ufshcd_pci_shutdown - main function to put the controller in reset state 91 * @pdev: pointer to PCI device handle 92 */ 93static void ufshcd_pci_shutdown(struct pci_dev *pdev) 94{ 95 ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev)); 96} 97 98/** 99 * ufshcd_pci_remove - de-allocate PCI/SCSI host and host memory space 100 * data structure memory 101 * @pdev - pointer to PCI handle 102 */ 103static void ufshcd_pci_remove(struct pci_dev *pdev) 104{ 105 struct ufs_hba *hba = pci_get_drvdata(pdev); 106 107 pm_runtime_forbid(&pdev->dev); 108 pm_runtime_get_noresume(&pdev->dev); 109 ufshcd_remove(hba); 110} 111 112/** 113 * ufshcd_pci_probe - probe routine of the driver 114 * @pdev: pointer to PCI device handle 115 * @id: PCI device id 116 * 117 * Returns 0 on success, non-zero value on failure 118 */ 119static int 120ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 121{ 122 struct ufs_hba *hba; 123 void __iomem *mmio_base; 124 int err; 125 126 err = pcim_enable_device(pdev); 127 if (err) { 128 dev_err(&pdev->dev, "pcim_enable_device failed\n"); 129 return err; 130 } 131 132 pci_set_master(pdev); 133 134 err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD); 135 if (err < 0) { 136 dev_err(&pdev->dev, "request and iomap failed\n"); 137 return err; 138 } 139 140 mmio_base = pcim_iomap_table(pdev)[0]; 141 142 err = ufshcd_alloc_host(&pdev->dev, &hba); 143 if (err) { 144 dev_err(&pdev->dev, "Allocation failed\n"); 145 return err; 146 } 147 148 INIT_LIST_HEAD(&hba->clk_list_head); 149 150 err = ufshcd_init(hba, mmio_base, pdev->irq); 151 if (err) { 152 dev_err(&pdev->dev, "Initialization failed\n"); 153 return err; 154 } 155 156 pci_set_drvdata(pdev, hba); 157 pm_runtime_put_noidle(&pdev->dev); 158 pm_runtime_allow(&pdev->dev); 159 160 return 0; 161} 162 163static const struct dev_pm_ops ufshcd_pci_pm_ops = { 164 .suspend = ufshcd_pci_suspend, 165 .resume = ufshcd_pci_resume, 166 .runtime_suspend = ufshcd_pci_runtime_suspend, 167 .runtime_resume = ufshcd_pci_runtime_resume, 168 .runtime_idle = ufshcd_pci_runtime_idle, 169}; 170 171static const struct pci_device_id ufshcd_pci_tbl[] = { 172 { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 173 { } /* terminate list */ 174}; 175 176MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl); 177 178static struct pci_driver ufshcd_pci_driver = { 179 .name = UFSHCD, 180 .id_table = ufshcd_pci_tbl, 181 .probe = ufshcd_pci_probe, 182 .remove = ufshcd_pci_remove, 183 .shutdown = ufshcd_pci_shutdown, 184 .driver = { 185 .pm = &ufshcd_pci_pm_ops 186 }, 187}; 188 189module_pci_driver(ufshcd_pci_driver); 190 191MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); 192MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); 193MODULE_DESCRIPTION("UFS host controller PCI glue driver"); 194MODULE_LICENSE("GPL"); 195MODULE_VERSION(UFSHCD_DRIVER_VERSION); 196