Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwrng: imx-rngc - add runtime pm

Add runtime power management to the imx-rngc driver. Disable the
peripheral clock when the rngc is idle.

The callback functions from struct hwrng wake the rngc up when they're
called and set it to idle on exit. Helper functions which are invoked
from the callbacks assume that the rngc is active.

Device init and probe are done before runtime pm is enabled. The
peripheral clock will be handled manually during these steps. Do not use
devres any more to enable/disable the peripheral clock, this conflicts
with runtime pm.

Signed-off-by: Martin Kaiser <martin@kaiser.cx>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Martin Kaiser and committed by
Herbert Xu
7a96a64e 1a3fa1c0

+52 -17
+52 -17
drivers/char/hw_random/imx-rngc.c
··· 13 13 #include <linux/clk.h> 14 14 #include <linux/err.h> 15 15 #include <linux/platform_device.h> 16 + #include <linux/pm.h> 17 + #include <linux/pm_runtime.h> 16 18 #include <linux/interrupt.h> 17 19 #include <linux/hw_random.h> 18 20 #include <linux/completion.h> ··· 55 53 56 54 #define RNGC_SELFTEST_TIMEOUT 2500 /* us */ 57 55 #define RNGC_SEED_TIMEOUT 200 /* ms */ 56 + #define RNGC_PM_TIMEOUT 500 /* ms */ 58 57 59 58 static bool self_test = true; 60 59 module_param(self_test, bool, 0); ··· 126 123 { 127 124 struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 128 125 unsigned int status; 129 - int retval = 0; 126 + int err, retval = 0; 127 + 128 + err = pm_runtime_resume_and_get(rngc->dev); 129 + if (err) 130 + return err; 130 131 131 132 while (max >= sizeof(u32)) { 132 133 status = readl(rngc->base + RNGC_STATUS); ··· 148 141 max -= sizeof(u32); 149 142 } 150 143 } 144 + pm_runtime_mark_last_busy(rngc->dev); 145 + pm_runtime_put(rngc->dev); 151 146 152 147 return retval ? retval : -EIO; 153 148 } ··· 178 169 { 179 170 struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 180 171 u32 cmd, ctrl; 181 - int ret; 172 + int ret, err; 173 + 174 + err = pm_runtime_resume_and_get(rngc->dev); 175 + if (err) 176 + return err; 182 177 183 178 /* clear error */ 184 179 cmd = readl(rngc->base + RNGC_COMMAND); ··· 199 186 ret = wait_for_completion_timeout(&rngc->rng_op_done, 200 187 msecs_to_jiffies(RNGC_SEED_TIMEOUT)); 201 188 if (!ret) { 202 - ret = -ETIMEDOUT; 203 - goto err; 189 + err = -ETIMEDOUT; 190 + goto out; 204 191 } 205 192 206 193 } while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR); 207 194 208 195 if (rngc->err_reg) { 209 - ret = -EIO; 210 - goto err; 196 + err = -EIO; 197 + goto out; 211 198 } 212 199 213 200 /* ··· 218 205 ctrl |= RNGC_CTRL_AUTO_SEED; 219 206 writel(ctrl, rngc->base + RNGC_CONTROL); 220 207 208 + out: 221 209 /* 222 210 * if initialisation was successful, we keep the interrupt 223 211 * unmasked until imx_rngc_cleanup is called 224 212 * we mask the interrupt ourselves if we return an error 225 213 */ 226 - return 0; 214 + if (err) 215 + imx_rngc_irq_mask_clear(rngc); 227 216 228 - err: 229 - imx_rngc_irq_mask_clear(rngc); 230 - return ret; 217 + pm_runtime_put(rngc->dev); 218 + return err; 231 219 } 232 220 233 221 static void imx_rngc_cleanup(struct hwrng *rng) 234 222 { 235 223 struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 224 + int err; 236 225 237 - imx_rngc_irq_mask_clear(rngc); 226 + err = pm_runtime_resume_and_get(rngc->dev); 227 + if (!err) { 228 + imx_rngc_irq_mask_clear(rngc); 229 + pm_runtime_put(rngc->dev); 230 + } 238 231 } 239 232 240 233 static int __init imx_rngc_probe(struct platform_device *pdev) ··· 259 240 if (IS_ERR(rngc->base)) 260 241 return PTR_ERR(rngc->base); 261 242 262 - rngc->clk = devm_clk_get_enabled(&pdev->dev, NULL); 243 + rngc->clk = devm_clk_get(&pdev->dev, NULL); 263 244 if (IS_ERR(rngc->clk)) 264 245 return dev_err_probe(&pdev->dev, PTR_ERR(rngc->clk), "Cannot get rng_clk\n"); 265 246 ··· 267 248 if (irq < 0) 268 249 return irq; 269 250 251 + clk_prepare_enable(rngc->clk); 252 + 270 253 ver_id = readl(rngc->base + RNGC_VER_ID); 271 254 rng_type = FIELD_GET(RNG_TYPE, ver_id); 272 255 /* 273 256 * This driver supports only RNGC and RNGB. (There's a different 274 257 * driver for RNGA.) 275 258 */ 276 - if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) 259 + if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) { 260 + clk_disable_unprepare(rngc->clk); 277 261 return -ENODEV; 262 + } 278 263 279 264 init_completion(&rngc->rng_op_done); 280 265 ··· 295 272 296 273 ret = devm_request_irq(&pdev->dev, 297 274 irq, imx_rngc_irq, 0, pdev->name, (void *)rngc); 298 - if (ret) 275 + if (ret) { 276 + clk_disable_unprepare(rngc->clk); 299 277 return dev_err_probe(&pdev->dev, ret, "Can't get interrupt working.\n"); 278 + } 300 279 301 280 if (self_test) { 302 281 ret = imx_rngc_self_test(rngc); 303 - if (ret) 282 + if (ret) { 283 + clk_disable_unprepare(rngc->clk); 304 284 return dev_err_probe(&pdev->dev, ret, "self test failed\n"); 285 + } 305 286 } 287 + 288 + pm_runtime_set_autosuspend_delay(&pdev->dev, RNGC_PM_TIMEOUT); 289 + pm_runtime_use_autosuspend(&pdev->dev); 290 + pm_runtime_set_active(&pdev->dev); 291 + devm_pm_runtime_enable(&pdev->dev); 306 292 307 293 ret = devm_hwrng_register(&pdev->dev, &rngc->rng); 308 294 if (ret) ··· 342 310 return 0; 343 311 } 344 312 345 - static DEFINE_SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); 313 + static const struct dev_pm_ops imx_rngc_pm_ops = { 314 + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) 315 + RUNTIME_PM_OPS(imx_rngc_suspend, imx_rngc_resume, NULL) 316 + }; 346 317 347 318 static const struct of_device_id imx_rngc_dt_ids[] = { 348 319 { .compatible = "fsl,imx25-rngb" }, ··· 356 321 static struct platform_driver imx_rngc_driver = { 357 322 .driver = { 358 323 .name = KBUILD_MODNAME, 359 - .pm = pm_sleep_ptr(&imx_rngc_pm_ops), 324 + .pm = pm_ptr(&imx_rngc_pm_ops), 360 325 .of_match_table = imx_rngc_dt_ids, 361 326 }, 362 327 };