aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/ufs/ufshcd-pci.c8
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c29
-rw-r--r--drivers/scsi/ufs/ufshcd.c161
-rw-r--r--drivers/scsi/ufs/ufshcd.h34
4 files changed, 194 insertions, 38 deletions
diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index afaabe2aeac8..7a6edbc55f92 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -164,7 +164,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
164 164
165 mmio_base = pcim_iomap_table(pdev)[0]; 165 mmio_base = pcim_iomap_table(pdev)[0];
166 166
167 err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq); 167 err = ufshcd_alloc_host(&pdev->dev, &hba);
168 if (err) {
169 dev_err(&pdev->dev, "Allocation failed\n");
170 return err;
171 }
172
173 err = ufshcd_init(hba, mmio_base, pdev->irq);
168 if (err) { 174 if (err) {
169 dev_err(&pdev->dev, "Initialization failed\n"); 175 dev_err(&pdev->dev, "Initialization failed\n");
170 return err; 176 return err;
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 5e4623225422..d727b1a83e95 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -35,9 +35,24 @@
35 35
36#include <linux/platform_device.h> 36#include <linux/platform_device.h>
37#include <linux/pm_runtime.h> 37#include <linux/pm_runtime.h>
38#include <linux/of.h>
38 39
39#include "ufshcd.h" 40#include "ufshcd.h"
40 41
42static const struct of_device_id ufs_of_match[];
43static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
44{
45 if (dev->of_node) {
46 const struct of_device_id *match;
47
48 match = of_match_node(ufs_of_match, dev->of_node);
49 if (match)
50 return (struct ufs_hba_variant_ops *)match->data;
51 }
52
53 return NULL;
54}
55
41#ifdef CONFIG_PM 56#ifdef CONFIG_PM
42/** 57/**
43 * ufshcd_pltfrm_suspend - suspend power management function 58 * ufshcd_pltfrm_suspend - suspend power management function
@@ -138,8 +153,8 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
138 153
139 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 154 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
140 mmio_base = devm_ioremap_resource(dev, mem_res); 155 mmio_base = devm_ioremap_resource(dev, mem_res);
141 if (IS_ERR(mmio_base)) { 156 if (IS_ERR(*(void **)&mmio_base)) {
142 err = PTR_ERR(mmio_base); 157 err = PTR_ERR(*(void **)&mmio_base);
143 goto out; 158 goto out;
144 } 159 }
145 160
@@ -150,10 +165,18 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
150 goto out; 165 goto out;
151 } 166 }
152 167
168 err = ufshcd_alloc_host(dev, &hba);
169 if (err) {
170 dev_err(&pdev->dev, "Allocation failed\n");
171 goto out;
172 }
173
174 hba->vops = get_variant_ops(&pdev->dev);
175
153 pm_runtime_set_active(&pdev->dev); 176 pm_runtime_set_active(&pdev->dev);
154 pm_runtime_enable(&pdev->dev); 177 pm_runtime_enable(&pdev->dev);
155 178
156 err = ufshcd_init(dev, &hba, mmio_base, irq); 179 err = ufshcd_init(hba, mmio_base, irq);
157 if (err) { 180 if (err) {
158 dev_err(dev, "Intialization failed\n"); 181 dev_err(dev, "Intialization failed\n");
159 goto out_disable_rpm; 182 goto out_disable_rpm;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index ba27215b8034..d0565b07dc8a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3,6 +3,7 @@
3 * 3 *
4 * This code is based on drivers/scsi/ufs/ufshcd.c 4 * This code is based on drivers/scsi/ufs/ufshcd.c
5 * Copyright (C) 2011-2013 Samsung India Software Operations 5 * Copyright (C) 2011-2013 Samsung India Software Operations
6 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
6 * 7 *
7 * Authors: 8 * Authors:
8 * Santosh Yaraganavi <santosh.sy@samsung.com> 9 * Santosh Yaraganavi <santosh.sy@samsung.com>
@@ -31,6 +32,9 @@
31 * circumstances will the contributor of this Program be liable for 32 * circumstances will the contributor of this Program be liable for
32 * any damages of any kind arising from your use or distribution of 33 * any damages of any kind arising from your use or distribution of
33 * this program. 34 * this program.
35 *
36 * The Linux Foundation chooses to take subject only to the GPLv2
37 * license terms, and distributes only under these terms.
34 */ 38 */
35 39
36#include <linux/async.h> 40#include <linux/async.h>
@@ -175,13 +179,14 @@ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
175/** 179/**
176 * ufshcd_is_device_present - Check if any device connected to 180 * ufshcd_is_device_present - Check if any device connected to
177 * the host controller 181 * the host controller
178 * @reg_hcs - host controller status register value 182 * @hba: pointer to adapter instance
179 * 183 *
180 * Returns 1 if device present, 0 if no device detected 184 * Returns 1 if device present, 0 if no device detected
181 */ 185 */
182static inline int ufshcd_is_device_present(u32 reg_hcs) 186static inline int ufshcd_is_device_present(struct ufs_hba *hba)
183{ 187{
184 return (DEVICE_PRESENT & reg_hcs) ? 1 : 0; 188 return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
189 DEVICE_PRESENT) ? 1 : 0;
185} 190}
186 191
187/** 192/**
@@ -1798,11 +1803,10 @@ out:
1798 * @hba: per adapter instance 1803 * @hba: per adapter instance
1799 * 1804 *
1800 * To bring UFS host controller to operational state, 1805 * To bring UFS host controller to operational state,
1801 * 1. Check if device is present 1806 * 1. Enable required interrupts
1802 * 2. Enable required interrupts 1807 * 2. Configure interrupt aggregation
1803 * 3. Configure interrupt aggregation 1808 * 3. Program UTRL and UTMRL base addres
1804 * 4. Program UTRL and UTMRL base addres 1809 * 4. Configure run-stop-registers
1805 * 5. Configure run-stop-registers
1806 * 1810 *
1807 * Returns 0 on success, non-zero value on failure 1811 * Returns 0 on success, non-zero value on failure
1808 */ 1812 */
@@ -1811,14 +1815,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
1811 int err = 0; 1815 int err = 0;
1812 u32 reg; 1816 u32 reg;
1813 1817
1814 /* check if device present */
1815 reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
1816 if (!ufshcd_is_device_present(reg)) {
1817 dev_err(hba->dev, "cc: Device not present\n");
1818 err = -ENXIO;
1819 goto out;
1820 }
1821
1822 /* Enable required interrupts */ 1818 /* Enable required interrupts */
1823 ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); 1819 ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
1824 1820
@@ -1839,6 +1835,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
1839 * UCRDY, UTMRLDY and UTRLRDY bits must be 1 1835 * UCRDY, UTMRLDY and UTRLRDY bits must be 1
1840 * DEI, HEI bits must be 0 1836 * DEI, HEI bits must be 0
1841 */ 1837 */
1838 reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
1842 if (!(ufshcd_get_lists_status(reg))) { 1839 if (!(ufshcd_get_lists_status(reg))) {
1843 ufshcd_enable_run_stop_reg(hba); 1840 ufshcd_enable_run_stop_reg(hba);
1844 } else { 1841 } else {
@@ -1885,6 +1882,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
1885 msleep(5); 1882 msleep(5);
1886 } 1883 }
1887 1884
1885 if (hba->vops && hba->vops->hce_enable_notify)
1886 hba->vops->hce_enable_notify(hba, PRE_CHANGE);
1887
1888 /* start controller initialization sequence */ 1888 /* start controller initialization sequence */
1889 ufshcd_hba_start(hba); 1889 ufshcd_hba_start(hba);
1890 1890
@@ -1912,6 +1912,10 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
1912 } 1912 }
1913 msleep(5); 1913 msleep(5);
1914 } 1914 }
1915
1916 if (hba->vops && hba->vops->hce_enable_notify)
1917 hba->vops->hce_enable_notify(hba, POST_CHANGE);
1918
1915 return 0; 1919 return 0;
1916} 1920}
1917 1921
@@ -1928,12 +1932,28 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
1928 /* enable UIC related interrupts */ 1932 /* enable UIC related interrupts */
1929 ufshcd_enable_intr(hba, UIC_COMMAND_COMPL); 1933 ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
1930 1934
1935 if (hba->vops && hba->vops->link_startup_notify)
1936 hba->vops->link_startup_notify(hba, PRE_CHANGE);
1937
1931 ret = ufshcd_dme_link_startup(hba); 1938 ret = ufshcd_dme_link_startup(hba);
1932 if (ret) 1939 if (ret)
1933 goto out; 1940 goto out;
1934 1941
1935 ret = ufshcd_make_hba_operational(hba); 1942 /* check if device is detected by inter-connect layer */
1943 if (!ufshcd_is_device_present(hba)) {
1944 dev_err(hba->dev, "%s: Device not present\n", __func__);
1945 ret = -ENXIO;
1946 goto out;
1947 }
1948
1949 /* Include any host controller configuration via UIC commands */
1950 if (hba->vops && hba->vops->link_startup_notify) {
1951 ret = hba->vops->link_startup_notify(hba, POST_CHANGE);
1952 if (ret)
1953 goto out;
1954 }
1936 1955
1956 ret = ufshcd_make_hba_operational(hba);
1937out: 1957out:
1938 if (ret) 1958 if (ret)
1939 dev_err(hba->dev, "link startup failed %d\n", ret); 1959 dev_err(hba->dev, "link startup failed %d\n", ret);
@@ -3173,6 +3193,61 @@ static struct scsi_host_template ufshcd_driver_template = {
3173 .can_queue = UFSHCD_CAN_QUEUE, 3193 .can_queue = UFSHCD_CAN_QUEUE,
3174}; 3194};
3175 3195
3196static int ufshcd_variant_hba_init(struct ufs_hba *hba)
3197{
3198 int err = 0;
3199
3200 if (!hba->vops)
3201 goto out;
3202
3203 if (hba->vops->init) {
3204 err = hba->vops->init(hba);
3205 if (err)
3206 goto out;
3207 }
3208
3209 if (hba->vops->setup_clocks) {
3210 err = hba->vops->setup_clocks(hba, true);
3211 if (err)
3212 goto out_exit;
3213 }
3214
3215 if (hba->vops->setup_regulators) {
3216 err = hba->vops->setup_regulators(hba, true);
3217 if (err)
3218 goto out_clks;
3219 }
3220
3221 goto out;
3222
3223out_clks:
3224 if (hba->vops->setup_clocks)
3225 hba->vops->setup_clocks(hba, false);
3226out_exit:
3227 if (hba->vops->exit)
3228 hba->vops->exit(hba);
3229out:
3230 if (err)
3231 dev_err(hba->dev, "%s: variant %s init failed err %d\n",
3232 __func__, hba->vops ? hba->vops->name : "", err);
3233 return err;
3234}
3235
3236static void ufshcd_variant_hba_exit(struct ufs_hba *hba)
3237{
3238 if (!hba->vops)
3239 return;
3240
3241 if (hba->vops->setup_clocks)
3242 hba->vops->setup_clocks(hba, false);
3243
3244 if (hba->vops->setup_regulators)
3245 hba->vops->setup_regulators(hba, false);
3246
3247 if (hba->vops->exit)
3248 hba->vops->exit(hba);
3249}
3250
3176/** 3251/**
3177 * ufshcd_suspend - suspend power management function 3252 * ufshcd_suspend - suspend power management function
3178 * @hba: per adapter instance 3253 * @hba: per adapter instance
@@ -3257,6 +3332,8 @@ void ufshcd_remove(struct ufs_hba *hba)
3257 ufshcd_hba_stop(hba); 3332 ufshcd_hba_stop(hba);
3258 3333
3259 scsi_host_put(hba->host); 3334 scsi_host_put(hba->host);
3335
3336 ufshcd_variant_hba_exit(hba);
3260} 3337}
3261EXPORT_SYMBOL_GPL(ufshcd_remove); 3338EXPORT_SYMBOL_GPL(ufshcd_remove);
3262 3339
@@ -3277,19 +3354,16 @@ static int ufshcd_set_dma_mask(struct ufs_hba *hba)
3277} 3354}
3278 3355
3279/** 3356/**
3280 * ufshcd_init - Driver initialization routine 3357 * ufshcd_alloc_host - allocate Host Bus Adapter (HBA)
3281 * @dev: pointer to device handle 3358 * @dev: pointer to device handle
3282 * @hba_handle: driver private handle 3359 * @hba_handle: driver private handle
3283 * @mmio_base: base register address
3284 * @irq: Interrupt line of device
3285 * Returns 0 on success, non-zero value on failure 3360 * Returns 0 on success, non-zero value on failure
3286 */ 3361 */
3287int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, 3362int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
3288 void __iomem *mmio_base, unsigned int irq)
3289{ 3363{
3290 struct Scsi_Host *host; 3364 struct Scsi_Host *host;
3291 struct ufs_hba *hba; 3365 struct ufs_hba *hba;
3292 int err; 3366 int err = 0;
3293 3367
3294 if (!dev) { 3368 if (!dev) {
3295 dev_err(dev, 3369 dev_err(dev,
@@ -3298,13 +3372,6 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
3298 goto out_error; 3372 goto out_error;
3299 } 3373 }
3300 3374
3301 if (!mmio_base) {
3302 dev_err(dev,
3303 "Invalid memory reference for mmio_base is NULL\n");
3304 err = -ENODEV;
3305 goto out_error;
3306 }
3307
3308 host = scsi_host_alloc(&ufshcd_driver_template, 3375 host = scsi_host_alloc(&ufshcd_driver_template,
3309 sizeof(struct ufs_hba)); 3376 sizeof(struct ufs_hba));
3310 if (!host) { 3377 if (!host) {
@@ -3315,9 +3382,40 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
3315 hba = shost_priv(host); 3382 hba = shost_priv(host);
3316 hba->host = host; 3383 hba->host = host;
3317 hba->dev = dev; 3384 hba->dev = dev;
3385 *hba_handle = hba;
3386
3387out_error:
3388 return err;
3389}
3390EXPORT_SYMBOL(ufshcd_alloc_host);
3391
3392/**
3393 * ufshcd_init - Driver initialization routine
3394 * @hba: per-adapter instance
3395 * @mmio_base: base register address
3396 * @irq: Interrupt line of device
3397 * Returns 0 on success, non-zero value on failure
3398 */
3399int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
3400{
3401 int err;
3402 struct Scsi_Host *host = hba->host;
3403 struct device *dev = hba->dev;
3404
3405 if (!mmio_base) {
3406 dev_err(hba->dev,
3407 "Invalid memory reference for mmio_base is NULL\n");
3408 err = -ENODEV;
3409 goto out_error;
3410 }
3411
3318 hba->mmio_base = mmio_base; 3412 hba->mmio_base = mmio_base;
3319 hba->irq = irq; 3413 hba->irq = irq;
3320 3414
3415 err = ufshcd_variant_hba_init(hba);
3416 if (err)
3417 goto out_error;
3418
3321 /* Read capabilities registers */ 3419 /* Read capabilities registers */
3322 ufshcd_hba_capabilities(hba); 3420 ufshcd_hba_capabilities(hba);
3323 3421
@@ -3395,8 +3493,6 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
3395 goto out_remove_scsi_host; 3493 goto out_remove_scsi_host;
3396 } 3494 }
3397 3495
3398 *hba_handle = hba;
3399
3400 /* Hold auto suspend until async scan completes */ 3496 /* Hold auto suspend until async scan completes */
3401 pm_runtime_get_sync(dev); 3497 pm_runtime_get_sync(dev);
3402 3498
@@ -3408,6 +3504,7 @@ out_remove_scsi_host:
3408 scsi_remove_host(hba->host); 3504 scsi_remove_host(hba->host);
3409out_disable: 3505out_disable:
3410 scsi_host_put(host); 3506 scsi_host_put(host);
3507 ufshcd_variant_hba_exit(hba);
3411out_error: 3508out_error:
3412 return err; 3509 return err;
3413} 3510}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index acf318e338ed..8c6bec05283e 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -68,6 +68,8 @@
68#define UFSHCD "ufshcd" 68#define UFSHCD "ufshcd"
69#define UFSHCD_DRIVER_VERSION "0.2" 69#define UFSHCD_DRIVER_VERSION "0.2"
70 70
71struct ufs_hba;
72
71enum dev_cmd_type { 73enum dev_cmd_type {
72 DEV_CMD_TYPE_NOP = 0x0, 74 DEV_CMD_TYPE_NOP = 0x0,
73 DEV_CMD_TYPE_QUERY = 0x1, 75 DEV_CMD_TYPE_QUERY = 0x1,
@@ -152,6 +154,30 @@ struct ufs_dev_cmd {
152 struct ufs_query query; 154 struct ufs_query query;
153}; 155};
154 156
157#define PRE_CHANGE 0
158#define POST_CHANGE 1
159/**
160 * struct ufs_hba_variant_ops - variant specific callbacks
161 * @name: variant name
162 * @init: called when the driver is initialized
163 * @exit: called to cleanup everything done in init
164 * @setup_clocks: called before touching any of the controller registers
165 * @setup_regulators: called before accessing the host controller
166 * @hce_enable_notify: called before and after HCE enable bit is set to allow
167 * variant specific Uni-Pro initialization.
168 * @link_startup_notify: called before and after Link startup is carried out
169 * to allow variant specific Uni-Pro initialization.
170 */
171struct ufs_hba_variant_ops {
172 const char *name;
173 int (*init)(struct ufs_hba *);
174 void (*exit)(struct ufs_hba *);
175 int (*setup_clocks)(struct ufs_hba *, bool);
176 int (*setup_regulators)(struct ufs_hba *, bool);
177 int (*hce_enable_notify)(struct ufs_hba *, bool);
178 int (*link_startup_notify)(struct ufs_hba *, bool);
179};
180
155/** 181/**
156 * struct ufs_hba - per adapter private structure 182 * struct ufs_hba - per adapter private structure
157 * @mmio_base: UFSHCI base register address 183 * @mmio_base: UFSHCI base register address
@@ -171,6 +197,8 @@ struct ufs_dev_cmd {
171 * @nutrs: Transfer Request Queue depth supported by controller 197 * @nutrs: Transfer Request Queue depth supported by controller
172 * @nutmrs: Task Management Queue depth supported by controller 198 * @nutmrs: Task Management Queue depth supported by controller
173 * @ufs_version: UFS Version to which controller complies 199 * @ufs_version: UFS Version to which controller complies
200 * @vops: pointer to variant specific operations
201 * @priv: pointer to variant specific private data
174 * @irq: Irq number of the controller 202 * @irq: Irq number of the controller
175 * @active_uic_cmd: handle of active UIC command 203 * @active_uic_cmd: handle of active UIC command
176 * @uic_cmd_mutex: mutex for uic command 204 * @uic_cmd_mutex: mutex for uic command
@@ -218,6 +246,8 @@ struct ufs_hba {
218 int nutrs; 246 int nutrs;
219 int nutmrs; 247 int nutmrs;
220 u32 ufs_version; 248 u32 ufs_version;
249 struct ufs_hba_variant_ops *vops;
250 void *priv;
221 unsigned int irq; 251 unsigned int irq;
222 252
223 struct uic_command *active_uic_cmd; 253 struct uic_command *active_uic_cmd;
@@ -256,8 +286,8 @@ struct ufs_hba {
256#define ufshcd_readl(hba, reg) \ 286#define ufshcd_readl(hba, reg) \
257 readl((hba)->mmio_base + (reg)) 287 readl((hba)->mmio_base + (reg))
258 288
259int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * , 289int ufshcd_alloc_host(struct device *, struct ufs_hba **);
260 unsigned int); 290int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int);
261void ufshcd_remove(struct ufs_hba *); 291void ufshcd_remove(struct ufs_hba *);
262 292
263/** 293/**