diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c index 88d77419f1d1..f654d15caa99 100644 --- a/drivers/char/hw_random/msm_rng.c +++ b/drivers/char/hw_random/msm_rng.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -116,10 +117,13 @@ int msm_rng_direct_read(struct msm_rng_device *msm_rng_dev, void *data) u32 val; u32 *retdata = data; int ret; + int failed = 0; pdev = msm_rng_dev->pdev; base = msm_rng_dev->base; + mutex_lock(&msm_rng_dev->rng_lock); + if (msm_rng_dev->qrng_perf_client) { ret = msm_bus_scale_client_update_request( msm_rng_dev->qrng_perf_client, 1); @@ -130,13 +134,21 @@ int msm_rng_direct_read(struct msm_rng_device *msm_rng_dev, void *data) ret = clk_prepare_enable(msm_rng_dev->prng_clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock in callback\n"); - return 0; + goto err; } /* read random data from h/w */ do { /* check status bit if data is available */ - if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001)) - break; /* no data to read so just bail */ + while (!(readl_relaxed(base + PRNG_STATUS_OFFSET) + & 0x00000001)) { + if (failed == 10) { + pr_err("Data not available after retry\n"); + break; + } + pr_err("msm_rng:Data not available!\n"); + msleep_interruptible(10); + failed++; + } /* read FIFO */ val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET); @@ -151,17 +163,17 @@ int msm_rng_direct_read(struct msm_rng_device *msm_rng_dev, void *data) /* vote to turn off clock */ clk_disable_unprepare(msm_rng_dev->prng_clk); - +err: if (msm_rng_dev->qrng_perf_client) { ret = msm_bus_scale_client_update_request( msm_rng_dev->qrng_perf_client, 0); if (ret) pr_err("bus_scale_client_update_req failed!\n"); } + mutex_unlock(&msm_rng_dev->rng_lock); val = 0L; return currsize; - } static int msm_rng_drbg_read(struct hwrng *rng, @@ -170,29 +182,25 @@ static int msm_rng_drbg_read(struct hwrng *rng, struct msm_rng_device *msm_rng_dev; struct platform_device *pdev; void __iomem *base; - size_t maxsize; size_t currsize = 0; u32 val; u32 *retdata = data; int ret, ret1; + int failed = 0; msm_rng_dev = (struct msm_rng_device *)rng->priv; pdev = msm_rng_dev->pdev; base = msm_rng_dev->base; - - down(&msm_rng_dev->drbg_sem); - - /* calculate max size bytes to transfer back to caller */ - maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max); - /* no room for word data */ - if (maxsize < 4) + if (max < 4) return 0; + mutex_lock(&msm_rng_dev->rng_lock); + /* read random data from CTR-AES based DRBG */ if (FIPS140_DRBG_ENABLED == msm_rng_dev->fips140_drbg_enabled) { - ret1 = fips_drbg_gen(msm_rng_dev->drbg_ctx, data, maxsize); + ret1 = fips_drbg_gen(msm_rng_dev->drbg_ctx, data, max); if (FIPS140_PRNG_ERR == ret1) panic("random number generator generator error.\n"); } else @@ -210,14 +218,21 @@ static int msm_rng_drbg_read(struct hwrng *rng, ret = clk_prepare_enable(msm_rng_dev->prng_clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock in callback\n"); - up(&msm_rng_dev->drbg_sem); - return 0; + goto err; } /* read random data from h/w */ do { /* check status bit if data is available */ - if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001)) - break; /* no data to read so just bail */ + while (!(readl_relaxed(base + PRNG_STATUS_OFFSET) + & 0x00000001)) { + if (failed == 10) { + pr_err("Data not available after retry\n"); + break; + } + pr_err("msm_rng:Data not available!\n"); + msleep_interruptible(10); + failed++; + } /* read FIFO */ val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET); @@ -230,12 +245,12 @@ static int msm_rng_drbg_read(struct hwrng *rng, currsize += 4; /* make sure we stay on 32bit boundary */ - if ((maxsize - currsize) < 4) + if ((max - currsize) < 4) break; - } while (currsize < maxsize); + } while (currsize < max); /* vote to turn off clock */ clk_disable_unprepare(msm_rng_dev->prng_clk); - +err: if (msm_rng_dev->qrng_perf_client) { ret = msm_bus_scale_client_update_request( msm_rng_dev->qrng_perf_client, 0); @@ -243,7 +258,7 @@ static int msm_rng_drbg_read(struct hwrng *rng, pr_err("bus_scale_client_update_req failed!\n"); } - up(&msm_rng_dev->drbg_sem); + mutex_unlock(&msm_rng_dev->rng_lock); return currsize; } @@ -503,6 +518,8 @@ static int __devinit msm_rng_probe(struct platform_device *pdev) if (error) goto rollback_clk; + mutex_init(&msm_rng_dev->rng_lock); + /* register with hwrng framework */ msm_rng.priv = (unsigned long) msm_rng_dev; error = hwrng_register(&msm_rng); @@ -528,8 +545,6 @@ static int __devinit msm_rng_probe(struct platform_device *pdev) } cdev_init(&msm_rng_cdev, &msm_rng_fops); - sema_init(&msm_rng_dev->drbg_sem, 1); - _first_msm_drbg_init(msm_rng_dev); return error; diff --git a/drivers/char/hw_random/msm_rng.h b/drivers/char/hw_random/msm_rng.h index b79ba46e77df..d3a2e8339955 100644 --- a/drivers/char/hw_random/msm_rng.h +++ b/drivers/char/hw_random/msm_rng.h @@ -31,7 +31,7 @@ struct msm_rng_device { void __iomem *base; struct clk *prng_clk; uint32_t qrng_perf_client; - struct semaphore drbg_sem; + struct mutex rng_lock; struct fips_drbg_ctx_s *drbg_ctx; int fips140_drbg_enabled; };