diff options
| author | Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> | 2019-01-22 14:59:35 -0500 |
|---|---|---|
| committer | Geert Uytterhoeven <geert+renesas@glider.be> | 2019-02-05 04:40:05 -0500 |
| commit | db4a0073cc82a95d8d1a9b05fde82355fcce77d8 (patch) | |
| tree | 1665ec55ec174a7d075fd77da2132efad343f435 | |
| parent | 875e8f6b0156c0ad56fd0c29c78e3f2f67ec0b16 (diff) | |
clk: renesas: rcar-gen3: Add RPC clocks
The RPCSRC internal clock is controlled by the RPCCKCR.DIV[4:3] on all
the R-Car gen3 SoCs except V3M (R8A77970) but the encoding of this field
is different between SoCs; it makes sense to support the most common case
of this encoding in the R-Car gen3 CPG driver...
After adding the RPCSRC clock, we can add the RPC[D2] clocks derived from
it and controlled by the RPCCKCR register on all the R-Car gen3 SoCs except
V3M (R8A77970); the composite clock driver seems handy for this task, using
the spinlock added in the previous patch...
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
| -rw-r--r-- | drivers/clk/renesas/rcar-gen3-cpg.c | 101 | ||||
| -rw-r--r-- | drivers/clk/renesas/rcar-gen3-cpg.h | 4 |
2 files changed, 105 insertions, 0 deletions
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 0818691253af..9a8071a8114d 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c | |||
| @@ -422,6 +422,92 @@ free_clock: | |||
| 422 | return clk; | 422 | return clk; |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | struct rpc_clock { | ||
| 426 | struct clk_divider div; | ||
| 427 | struct clk_gate gate; | ||
| 428 | /* | ||
| 429 | * One notifier covers both RPC and RPCD2 clocks as they are both | ||
| 430 | * controlled by the same RPCCKCR register... | ||
| 431 | */ | ||
| 432 | struct cpg_simple_notifier csn; | ||
| 433 | }; | ||
| 434 | |||
| 435 | static const struct clk_div_table cpg_rpcsrc_div_table[] = { | ||
| 436 | { 2, 5 }, { 3, 6 }, { 0, 0 }, | ||
| 437 | }; | ||
| 438 | |||
| 439 | static const struct clk_div_table cpg_rpc_div_table[] = { | ||
| 440 | { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 }, | ||
| 441 | }; | ||
| 442 | |||
| 443 | static struct clk * __init cpg_rpc_clk_register(const char *name, | ||
| 444 | void __iomem *base, const char *parent_name, | ||
| 445 | struct raw_notifier_head *notifiers) | ||
| 446 | { | ||
| 447 | struct rpc_clock *rpc; | ||
| 448 | struct clk *clk; | ||
| 449 | |||
| 450 | rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); | ||
| 451 | if (!rpc) | ||
| 452 | return ERR_PTR(-ENOMEM); | ||
| 453 | |||
| 454 | rpc->div.reg = base + CPG_RPCCKCR; | ||
| 455 | rpc->div.width = 3; | ||
| 456 | rpc->div.table = cpg_rpc_div_table; | ||
| 457 | rpc->div.lock = &cpg_lock; | ||
| 458 | |||
| 459 | rpc->gate.reg = base + CPG_RPCCKCR; | ||
| 460 | rpc->gate.bit_idx = 8; | ||
| 461 | rpc->gate.flags = CLK_GATE_SET_TO_DISABLE; | ||
| 462 | rpc->gate.lock = &cpg_lock; | ||
| 463 | |||
| 464 | rpc->csn.reg = base + CPG_RPCCKCR; | ||
| 465 | |||
| 466 | clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, | ||
| 467 | &rpc->div.hw, &clk_divider_ops, | ||
| 468 | &rpc->gate.hw, &clk_gate_ops, 0); | ||
| 469 | if (IS_ERR(clk)) { | ||
| 470 | kfree(rpc); | ||
| 471 | return clk; | ||
| 472 | } | ||
| 473 | |||
| 474 | cpg_simple_notifier_register(notifiers, &rpc->csn); | ||
| 475 | return clk; | ||
| 476 | } | ||
| 477 | |||
| 478 | struct rpcd2_clock { | ||
| 479 | struct clk_fixed_factor fixed; | ||
| 480 | struct clk_gate gate; | ||
| 481 | }; | ||
| 482 | |||
| 483 | static struct clk * __init cpg_rpcd2_clk_register(const char *name, | ||
| 484 | void __iomem *base, | ||
| 485 | const char *parent_name) | ||
| 486 | { | ||
| 487 | struct rpcd2_clock *rpcd2; | ||
| 488 | struct clk *clk; | ||
| 489 | |||
| 490 | rpcd2 = kzalloc(sizeof(*rpcd2), GFP_KERNEL); | ||
| 491 | if (!rpcd2) | ||
| 492 | return ERR_PTR(-ENOMEM); | ||
| 493 | |||
| 494 | rpcd2->fixed.mult = 1; | ||
| 495 | rpcd2->fixed.div = 2; | ||
| 496 | |||
| 497 | rpcd2->gate.reg = base + CPG_RPCCKCR; | ||
| 498 | rpcd2->gate.bit_idx = 9; | ||
| 499 | rpcd2->gate.flags = CLK_GATE_SET_TO_DISABLE; | ||
| 500 | rpcd2->gate.lock = &cpg_lock; | ||
| 501 | |||
| 502 | clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, | ||
| 503 | &rpcd2->fixed.hw, &clk_fixed_factor_ops, | ||
| 504 | &rpcd2->gate.hw, &clk_gate_ops, 0); | ||
| 505 | if (IS_ERR(clk)) | ||
| 506 | kfree(rpcd2); | ||
| 507 | |||
| 508 | return clk; | ||
| 509 | } | ||
| 510 | |||
| 425 | 511 | ||
| 426 | static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata; | 512 | static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata; |
| 427 | static unsigned int cpg_clk_extalr __initdata; | 513 | static unsigned int cpg_clk_extalr __initdata; |
| @@ -600,6 +686,21 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, | |||
| 600 | } | 686 | } |
| 601 | break; | 687 | break; |
| 602 | 688 | ||
| 689 | case CLK_TYPE_GEN3_RPCSRC: | ||
| 690 | return clk_register_divider_table(NULL, core->name, | ||
| 691 | __clk_get_name(parent), 0, | ||
| 692 | base + CPG_RPCCKCR, 3, 2, 0, | ||
| 693 | cpg_rpcsrc_div_table, | ||
| 694 | &cpg_lock); | ||
| 695 | |||
| 696 | case CLK_TYPE_GEN3_RPC: | ||
| 697 | return cpg_rpc_clk_register(core->name, base, | ||
| 698 | __clk_get_name(parent), notifiers); | ||
| 699 | |||
| 700 | case CLK_TYPE_GEN3_RPCD2: | ||
| 701 | return cpg_rpcd2_clk_register(core->name, base, | ||
| 702 | __clk_get_name(parent)); | ||
| 703 | |||
| 603 | default: | 704 | default: |
| 604 | return ERR_PTR(-EINVAL); | 705 | return ERR_PTR(-EINVAL); |
| 605 | } | 706 | } |
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index f4fb6cf16688..eac1b057455a 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h | |||
| @@ -23,6 +23,9 @@ enum rcar_gen3_clk_types { | |||
| 23 | CLK_TYPE_GEN3_Z2, | 23 | CLK_TYPE_GEN3_Z2, |
| 24 | CLK_TYPE_GEN3_OSC, /* OSC EXTAL predivider and fixed divider */ | 24 | CLK_TYPE_GEN3_OSC, /* OSC EXTAL predivider and fixed divider */ |
| 25 | CLK_TYPE_GEN3_RCKSEL, /* Select parent/divider using RCKCR.CKSEL */ | 25 | CLK_TYPE_GEN3_RCKSEL, /* Select parent/divider using RCKCR.CKSEL */ |
| 26 | CLK_TYPE_GEN3_RPCSRC, | ||
| 27 | CLK_TYPE_GEN3_RPC, | ||
| 28 | CLK_TYPE_GEN3_RPCD2, | ||
| 26 | 29 | ||
| 27 | /* SoC specific definitions start here */ | 30 | /* SoC specific definitions start here */ |
| 28 | CLK_TYPE_GEN3_SOC_BASE, | 31 | CLK_TYPE_GEN3_SOC_BASE, |
| @@ -57,6 +60,7 @@ struct rcar_gen3_cpg_pll_config { | |||
| 57 | u8 osc_prediv; | 60 | u8 osc_prediv; |
| 58 | }; | 61 | }; |
| 59 | 62 | ||
| 63 | #define CPG_RPCCKCR 0x238 | ||
| 60 | #define CPG_RCKCR 0x240 | 64 | #define CPG_RCKCR 0x240 |
| 61 | 65 | ||
| 62 | struct clk *rcar_gen3_cpg_clk_register(struct device *dev, | 66 | struct clk *rcar_gen3_cpg_clk_register(struct device *dev, |
