diff options
Diffstat (limited to 'drivers/scsi/ufs/ufshcd.c')
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 161 |
1 files changed, 129 insertions, 32 deletions
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 | */ |
182 | static inline int ufshcd_is_device_present(u32 reg_hcs) | 186 | static 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); | ||
1937 | out: | 1957 | out: |
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 | ||
3196 | static 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 | |||
3223 | out_clks: | ||
3224 | if (hba->vops->setup_clocks) | ||
3225 | hba->vops->setup_clocks(hba, false); | ||
3226 | out_exit: | ||
3227 | if (hba->vops->exit) | ||
3228 | hba->vops->exit(hba); | ||
3229 | out: | ||
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 | |||
3236 | static 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 | } |
3261 | EXPORT_SYMBOL_GPL(ufshcd_remove); | 3338 | EXPORT_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 | */ |
3287 | int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, | 3362 | int 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 | |||
3387 | out_error: | ||
3388 | return err; | ||
3389 | } | ||
3390 | EXPORT_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 | */ | ||
3399 | int 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); |
3409 | out_disable: | 3505 | out_disable: |
3410 | scsi_host_put(host); | 3506 | scsi_host_put(host); |
3507 | ufshcd_variant_hba_exit(hba); | ||
3411 | out_error: | 3508 | out_error: |
3412 | return err; | 3509 | return err; |
3413 | } | 3510 | } |