| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * R-Car Generation 2 support |
| 4 | * |
| 5 | * Copyright (C) 2013 Renesas Solutions Corp. |
| 6 | * Copyright (C) 2013 Magnus Damm |
| 7 | * Copyright (C) 2014 Ulrich Hecht |
| 8 | */ |
| 9 | |
| 10 | #include <linux/clocksource.h> |
| 11 | #include <linux/io.h> |
| 12 | #include <linux/kernel.h> |
| 13 | #include <linux/memblock.h> |
| 14 | #include <linux/of.h> |
| 15 | #include <linux/of_clk.h> |
| 16 | #include <linux/psci.h> |
| 17 | #include <asm/mach/arch.h> |
| 18 | #include <asm/secure_cntvoff.h> |
| 19 | #include "common.h" |
| 20 | |
| 21 | static const struct of_device_id cpg_matches[] __initconst = { |
| 22 | { .compatible = "renesas,r8a7742-cpg-mssr" , .data = "extal" }, |
| 23 | { .compatible = "renesas,r8a7743-cpg-mssr" , .data = "extal" }, |
| 24 | { .compatible = "renesas,r8a7744-cpg-mssr" , .data = "extal" }, |
| 25 | { .compatible = "renesas,r8a7790-cpg-mssr" , .data = "extal" }, |
| 26 | { .compatible = "renesas,r8a7791-cpg-mssr" , .data = "extal" }, |
| 27 | { .compatible = "renesas,r8a7793-cpg-mssr" , .data = "extal" }, |
| 28 | { /* sentinel */ } |
| 29 | }; |
| 30 | |
| 31 | static unsigned int __init get_extal_freq(void) |
| 32 | { |
| 33 | const struct of_device_id *match; |
| 34 | struct device_node *cpg, *extal; |
| 35 | u32 freq = 20000000; |
| 36 | int idx = 0; |
| 37 | |
| 38 | cpg = of_find_matching_node_and_match(NULL, matches: cpg_matches, match: &match); |
| 39 | if (!cpg) |
| 40 | return freq; |
| 41 | |
| 42 | if (match->data) |
| 43 | idx = of_property_match_string(np: cpg, propname: "clock-names" , string: match->data); |
| 44 | extal = of_parse_phandle(np: cpg, phandle_name: "clocks" , index: idx); |
| 45 | of_node_put(node: cpg); |
| 46 | if (!extal) |
| 47 | return freq; |
| 48 | |
| 49 | of_property_read_u32(np: extal, propname: "clock-frequency" , out_value: &freq); |
| 50 | of_node_put(node: extal); |
| 51 | return freq; |
| 52 | } |
| 53 | |
| 54 | #define CNTCR 0 |
| 55 | #define CNTFID0 0x20 |
| 56 | |
| 57 | static void __init rcar_gen2_timer_init(void) |
| 58 | { |
| 59 | bool need_update = true; |
| 60 | void __iomem *base; |
| 61 | u32 freq; |
| 62 | |
| 63 | /* |
| 64 | * If PSCI is available then most likely we are running on PSCI-enabled |
| 65 | * U-Boot which, we assume, has already taken care of resetting CNTVOFF |
| 66 | * and updating counter module before switching to non-secure mode |
| 67 | * and we don't need to. |
| 68 | */ |
| 69 | #ifdef CONFIG_ARM_PSCI_FW |
| 70 | if (psci_ops.cpu_on) |
| 71 | need_update = false; |
| 72 | #endif |
| 73 | |
| 74 | if (need_update == false) |
| 75 | goto skip_update; |
| 76 | |
| 77 | secure_cntvoff_init(); |
| 78 | |
| 79 | if (of_machine_is_compatible(compat: "renesas,r8a7745" ) || |
| 80 | of_machine_is_compatible(compat: "renesas,r8a77470" ) || |
| 81 | of_machine_is_compatible(compat: "renesas,r8a7792" ) || |
| 82 | of_machine_is_compatible(compat: "renesas,r8a7794" )) { |
| 83 | freq = 260000000 / 8; /* ZS / 8 */ |
| 84 | } else { |
| 85 | /* At Linux boot time the r8a7790 arch timer comes up |
| 86 | * with the counter disabled. Moreover, it may also report |
| 87 | * a potentially incorrect fixed 13 MHz frequency. To be |
| 88 | * correct these registers need to be updated to use the |
| 89 | * frequency EXTAL / 2. |
| 90 | */ |
| 91 | freq = get_extal_freq() / 2; |
| 92 | } |
| 93 | |
| 94 | /* Remap "armgcnt address map" space */ |
| 95 | base = ioremap(offset: 0xe6080000, PAGE_SIZE); |
| 96 | |
| 97 | /* |
| 98 | * Update the timer if it is either not running, or is not at the |
| 99 | * right frequency. The timer is only configurable in secure mode |
| 100 | * so this avoids an abort if the loader started the timer and |
| 101 | * entered the kernel in non-secure mode. |
| 102 | */ |
| 103 | |
| 104 | if ((ioread32(base + CNTCR) & 1) == 0 || |
| 105 | ioread32(base + CNTFID0) != freq) { |
| 106 | /* Update registers with correct frequency */ |
| 107 | iowrite32(freq, base + CNTFID0); |
| 108 | asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq)); |
| 109 | |
| 110 | /* make sure arch timer is started by setting bit 0 of CNTCR */ |
| 111 | iowrite32(1, base + CNTCR); |
| 112 | } |
| 113 | |
| 114 | iounmap(addr: base); |
| 115 | |
| 116 | skip_update: |
| 117 | of_clk_init(NULL); |
| 118 | timer_probe(); |
| 119 | } |
| 120 | |
| 121 | static const char * const rcar_gen2_boards_compat_dt[] __initconst = { |
| 122 | "renesas,r8a7790" , |
| 123 | "renesas,r8a7791" , |
| 124 | "renesas,r8a7792" , |
| 125 | "renesas,r8a7793" , |
| 126 | "renesas,r8a7794" , |
| 127 | NULL |
| 128 | }; |
| 129 | |
| 130 | DT_MACHINE_START(RCAR_GEN2_DT, "Generic R-Car Gen2 (Flattened Device Tree)" ) |
| 131 | .init_late = shmobile_init_late, |
| 132 | .init_time = rcar_gen2_timer_init, |
| 133 | .dt_compat = rcar_gen2_boards_compat_dt, |
| 134 | MACHINE_END |
| 135 | |
| 136 | static const char * const rz_g1_boards_compat_dt[] __initconst = { |
| 137 | "renesas,r8a7742" , |
| 138 | "renesas,r8a7743" , |
| 139 | "renesas,r8a7744" , |
| 140 | "renesas,r8a7745" , |
| 141 | "renesas,r8a77470" , |
| 142 | NULL |
| 143 | }; |
| 144 | |
| 145 | DT_MACHINE_START(RZ_G1_DT, "Generic RZ/G1 (Flattened Device Tree)" ) |
| 146 | .init_late = shmobile_init_late, |
| 147 | .init_time = rcar_gen2_timer_init, |
| 148 | .dt_compat = rz_g1_boards_compat_dt, |
| 149 | MACHINE_END |
| 150 | |