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

misc: atmel_tclib: get and use slow clock

Commit dca1a4b5ff6e ("clk: at91: keep slow clk enabled to prevent system
hang") added a workaround for the slow clock as it is not properly handled
by its users.

Get and use the slow clock as it is necessary for the timer counters.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Boris Brezillon and committed by
Alexandre Belloni
7d8d05d1 eed9fb9d

+34 -9
+10 -2
drivers/clocksource/tcb_clksrc.c
··· 193 193 struct clk *t2_clk = tc->clk[2]; 194 194 int irq = tc->irq[2]; 195 195 196 - /* try to enable t2 clk to avoid future errors in mode change */ 197 - ret = clk_prepare_enable(t2_clk); 196 + ret = clk_prepare_enable(tc->slow_clk); 198 197 if (ret) 199 198 return ret; 199 + 200 + /* try to enable t2 clk to avoid future errors in mode change */ 201 + ret = clk_prepare_enable(t2_clk); 202 + if (ret) { 203 + clk_disable_unprepare(tc->slow_clk); 204 + return ret; 205 + } 206 + 200 207 clk_disable(t2_clk); 201 208 202 209 clkevt.regs = tc->regs; ··· 216 209 ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); 217 210 if (ret) { 218 211 clk_unprepare(t2_clk); 212 + clk_disable_unprepare(tc->slow_clk); 219 213 return ret; 220 214 } 221 215
+4
drivers/misc/atmel_tclib.c
··· 125 125 if (IS_ERR(clk)) 126 126 return PTR_ERR(clk); 127 127 128 + tc->slow_clk = devm_clk_get(&pdev->dev, "slow_clk"); 129 + if (IS_ERR(tc->slow_clk)) 130 + return PTR_ERR(tc->slow_clk); 131 + 128 132 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 129 133 tc->regs = devm_ioremap_resource(&pdev->dev, r); 130 134 if (IS_ERR(tc->regs))
+19 -7
drivers/pwm/pwm-atmel-tcb.c
··· 305 305 */ 306 306 if (i == 5) { 307 307 i = slowclk; 308 - rate = 32768; 308 + rate = clk_get_rate(tc->slow_clk); 309 309 min = div_u64(NSEC_PER_SEC, rate); 310 310 max = min << tc->tcb_config->counter_width; 311 311 ··· 387 387 388 388 tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); 389 389 if (tcbpwm == NULL) { 390 - atmel_tc_free(tc); 390 + err = -ENOMEM; 391 391 dev_err(&pdev->dev, "failed to allocate memory\n"); 392 - return -ENOMEM; 392 + goto err_free_tc; 393 393 } 394 394 395 395 tcbpwm->chip.dev = &pdev->dev; ··· 400 400 tcbpwm->chip.npwm = NPWM; 401 401 tcbpwm->tc = tc; 402 402 403 + err = clk_prepare_enable(tc->slow_clk); 404 + if (err) 405 + goto err_free_tc; 406 + 403 407 spin_lock_init(&tcbpwm->lock); 404 408 405 409 err = pwmchip_add(&tcbpwm->chip); 406 - if (err < 0) { 407 - atmel_tc_free(tc); 408 - return err; 409 - } 410 + if (err < 0) 411 + goto err_disable_clk; 410 412 411 413 platform_set_drvdata(pdev, tcbpwm); 412 414 413 415 return 0; 416 + 417 + err_disable_clk: 418 + clk_disable_unprepare(tcbpwm->tc->slow_clk); 419 + 420 + err_free_tc: 421 + atmel_tc_free(tc); 422 + 423 + return err; 414 424 } 415 425 416 426 static int atmel_tcb_pwm_remove(struct platform_device *pdev) 417 427 { 418 428 struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); 419 429 int err; 430 + 431 + clk_disable_unprepare(tcbpwm->tc->slow_clk); 420 432 421 433 err = pwmchip_remove(&tcbpwm->chip); 422 434 if (err < 0)
+1
include/linux/atmel_tc.h
··· 67 67 const struct atmel_tcb_config *tcb_config; 68 68 int irq[3]; 69 69 struct clk *clk[3]; 70 + struct clk *slow_clk; 70 71 struct list_head node; 71 72 bool allocated; 72 73 };