Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 1 | /* |
| 2 | * (C) Copyright 2010-2011 |
| 3 | * NVIDIA Corporation <www.nvidia.com> |
| 4 | * |
| 5 | * See file CREDITS for list of people who contributed to this |
| 6 | * project. |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public License as |
| 10 | * published by the Free Software Foundation; either version 2 of |
| 11 | * the License, or (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program; if not, write to the Free Software |
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| 21 | * MA 02111-1307 USA |
| 22 | */ |
| 23 | |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 24 | #include <asm/io.h> |
| 25 | #include <asm/arch/tegra2.h> |
Simon Glass | 84a8dcf | 2012-04-02 13:18:46 +0000 | [diff] [blame] | 26 | #include <asm/arch/ap20.h> |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 27 | #include <asm/arch/clk_rst.h> |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 28 | #include <asm/arch/clock.h> |
Simon Glass | 1fed82a | 2012-04-02 13:18:50 +0000 | [diff] [blame] | 29 | #include <asm/arch/fuse.h> |
| 30 | #include <asm/arch/gp_padctrl.h> |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 31 | #include <asm/arch/pmc.h> |
| 32 | #include <asm/arch/pinmux.h> |
| 33 | #include <asm/arch/scu.h> |
| 34 | #include <common.h> |
| 35 | |
Simon Glass | 1fed82a | 2012-04-02 13:18:50 +0000 | [diff] [blame] | 36 | int tegra_get_chip_type(void) |
| 37 | { |
| 38 | struct apb_misc_gp_ctlr *gp; |
| 39 | struct fuse_regs *fuse = (struct fuse_regs *)TEGRA2_FUSE_BASE; |
| 40 | uint tegra_sku_id, rev; |
| 41 | |
| 42 | /* |
| 43 | * This is undocumented, Chip ID is bits 15:8 of the register |
| 44 | * APB_MISC + 0x804, and has value 0x20 for Tegra20, 0x30 for |
| 45 | * Tegra30 |
| 46 | */ |
| 47 | gp = (struct apb_misc_gp_ctlr *)TEGRA2_APB_MISC_GP_BASE; |
| 48 | rev = (readl(&gp->hidrev) & HIDREV_CHIPID_MASK) >> HIDREV_CHIPID_SHIFT; |
| 49 | |
| 50 | tegra_sku_id = readl(&fuse->sku_info) & 0xff; |
| 51 | |
| 52 | switch (rev) { |
| 53 | case CHIPID_TEGRA2: |
| 54 | switch (tegra_sku_id) { |
| 55 | case SKU_ID_T20: |
| 56 | return TEGRA_SOC_T20; |
| 57 | case SKU_ID_T25SE: |
| 58 | case SKU_ID_AP25: |
| 59 | case SKU_ID_T25: |
| 60 | case SKU_ID_AP25E: |
| 61 | case SKU_ID_T25E: |
| 62 | return TEGRA_SOC_T25; |
| 63 | } |
| 64 | break; |
| 65 | } |
| 66 | /* unknown sku id */ |
| 67 | return TEGRA_SOC_UNKNOWN; |
| 68 | } |
| 69 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 70 | /* Returns 1 if the current CPU executing is a Cortex-A9, else 0 */ |
| 71 | static int ap20_cpu_is_cortexa9(void) |
| 72 | { |
| 73 | u32 id = readb(NV_PA_PG_UP_BASE + PG_UP_TAG_0); |
| 74 | return id == (PG_UP_TAG_0_PID_CPU & 0xff); |
| 75 | } |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 76 | |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 77 | void init_pllx(void) |
| 78 | { |
| 79 | struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; |
Simon Glass | 069784e | 2011-09-21 12:40:02 +0000 | [diff] [blame] | 80 | struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_XCPU]; |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 81 | u32 reg; |
| 82 | |
| 83 | /* If PLLX is already enabled, just return */ |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 84 | if (readl(&pll->pll_base) & PLL_ENABLE_MASK) |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 85 | return; |
| 86 | |
| 87 | /* Set PLLX_MISC */ |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 88 | writel(1 << PLL_CPCON_SHIFT, &pll->pll_misc); |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 89 | |
| 90 | /* Use 12MHz clock here */ |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 91 | reg = PLL_BYPASS_MASK | (12 << PLL_DIVM_SHIFT); |
| 92 | reg |= 1000 << PLL_DIVN_SHIFT; |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 93 | writel(reg, &pll->pll_base); |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 94 | |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 95 | reg |= PLL_ENABLE_MASK; |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 96 | writel(reg, &pll->pll_base); |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 97 | |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 98 | reg &= ~PLL_BYPASS_MASK; |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 99 | writel(reg, &pll->pll_base); |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 100 | } |
| 101 | |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 102 | static void enable_cpu_clock(int enable) |
| 103 | { |
| 104 | struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 105 | u32 clk; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 106 | |
| 107 | /* |
| 108 | * NOTE: |
| 109 | * Regardless of whether the request is to enable or disable the CPU |
| 110 | * clock, every processor in the CPU complex except the master (CPU 0) |
| 111 | * will have it's clock stopped because the AVP only talks to the |
| 112 | * master. The AVP does not know (nor does it need to know) that there |
| 113 | * are multiple processors in the CPU complex. |
| 114 | */ |
| 115 | |
| 116 | if (enable) { |
Tom Warren | 30e80f6 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 117 | /* Initialize PLLX */ |
| 118 | init_pllx(); |
| 119 | |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 120 | /* Wait until all clocks are stable */ |
| 121 | udelay(PLL_STABILIZATION_DELAY); |
| 122 | |
| 123 | writel(CCLK_BURST_POLICY, &clkrst->crc_cclk_brst_pol); |
| 124 | writel(SUPER_CCLK_DIVIDER, &clkrst->crc_super_cclk_div); |
| 125 | } |
| 126 | |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 127 | /* |
| 128 | * Read the register containing the individual CPU clock enables and |
| 129 | * always stop the clock to CPU 1. |
| 130 | */ |
| 131 | clk = readl(&clkrst->crc_clk_cpu_cmplx); |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 132 | clk |= 1 << CPU1_CLK_STP_SHIFT; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 133 | |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 134 | /* Stop/Unstop the CPU clock */ |
| 135 | clk &= ~CPU0_CLK_STP_MASK; |
| 136 | clk |= !enable << CPU0_CLK_STP_SHIFT; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 137 | writel(clk, &clkrst->crc_clk_cpu_cmplx); |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 138 | |
| 139 | clock_enable(PERIPH_ID_CPU); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | static int is_cpu_powered(void) |
| 143 | { |
Simon Glass | cad1a27 | 2012-02-03 15:13:52 +0000 | [diff] [blame] | 144 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 145 | |
| 146 | return (readl(&pmc->pmc_pwrgate_status) & CPU_PWRED) ? 1 : 0; |
| 147 | } |
| 148 | |
| 149 | static void remove_cpu_io_clamps(void) |
| 150 | { |
Simon Glass | cad1a27 | 2012-02-03 15:13:52 +0000 | [diff] [blame] | 151 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 152 | u32 reg; |
| 153 | |
| 154 | /* Remove the clamps on the CPU I/O signals */ |
| 155 | reg = readl(&pmc->pmc_remove_clamping); |
| 156 | reg |= CPU_CLMP; |
| 157 | writel(reg, &pmc->pmc_remove_clamping); |
| 158 | |
| 159 | /* Give I/O signals time to stabilize */ |
| 160 | udelay(IO_STABILIZATION_DELAY); |
| 161 | } |
| 162 | |
| 163 | static void powerup_cpu(void) |
| 164 | { |
Simon Glass | cad1a27 | 2012-02-03 15:13:52 +0000 | [diff] [blame] | 165 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 166 | u32 reg; |
| 167 | int timeout = IO_STABILIZATION_DELAY; |
| 168 | |
| 169 | if (!is_cpu_powered()) { |
| 170 | /* Toggle the CPU power state (OFF -> ON) */ |
| 171 | reg = readl(&pmc->pmc_pwrgate_toggle); |
| 172 | reg &= PARTID_CP; |
| 173 | reg |= START_CP; |
| 174 | writel(reg, &pmc->pmc_pwrgate_toggle); |
| 175 | |
| 176 | /* Wait for the power to come up */ |
| 177 | while (!is_cpu_powered()) { |
| 178 | if (timeout-- == 0) |
| 179 | printf("CPU failed to power up!\n"); |
| 180 | else |
| 181 | udelay(10); |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | * Remove the I/O clamps from CPU power partition. |
| 186 | * Recommended only on a Warm boot, if the CPU partition gets |
| 187 | * power gated. Shouldn't cause any harm when called after a |
| 188 | * cold boot according to HW, probably just redundant. |
| 189 | */ |
| 190 | remove_cpu_io_clamps(); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | static void enable_cpu_power_rail(void) |
| 195 | { |
Simon Glass | cad1a27 | 2012-02-03 15:13:52 +0000 | [diff] [blame] | 196 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 197 | u32 reg; |
| 198 | |
| 199 | reg = readl(&pmc->pmc_cntrl); |
| 200 | reg |= CPUPWRREQ_OE; |
| 201 | writel(reg, &pmc->pmc_cntrl); |
| 202 | |
| 203 | /* |
| 204 | * The TI PMU65861C needs a 3.75ms delay between enabling |
| 205 | * the power rail and enabling the CPU clock. This delay |
| 206 | * between SM1EN and SM1 is for switching time + the ramp |
| 207 | * up of the voltage to the CPU (VDD_CPU from PMU). |
| 208 | */ |
| 209 | udelay(3750); |
| 210 | } |
| 211 | |
| 212 | static void reset_A9_cpu(int reset) |
| 213 | { |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 214 | /* |
| 215 | * NOTE: Regardless of whether the request is to hold the CPU in reset |
| 216 | * or take it out of reset, every processor in the CPU complex |
| 217 | * except the master (CPU 0) will be held in reset because the |
| 218 | * AVP only talks to the master. The AVP does not know that there |
| 219 | * are multiple processors in the CPU complex. |
| 220 | */ |
| 221 | |
Simon Glass | e2deddd | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 222 | /* Hold CPU 1 in reset, and CPU 0 if asked */ |
| 223 | reset_cmplx_set_enable(1, crc_rst_cpu | crc_rst_de | crc_rst_debug, 1); |
| 224 | reset_cmplx_set_enable(0, crc_rst_cpu | crc_rst_de | crc_rst_debug, |
| 225 | reset); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 226 | |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 227 | /* Enable/Disable master CPU reset */ |
| 228 | reset_set_enable(PERIPH_ID_CPU, reset); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | static void clock_enable_coresight(int enable) |
| 232 | { |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 233 | u32 rst, src; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 234 | |
Simon Glass | 16134fd | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 235 | clock_set_enable(PERIPH_ID_CORESIGHT, enable); |
| 236 | reset_set_enable(PERIPH_ID_CORESIGHT, !enable); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 237 | |
| 238 | if (enable) { |
| 239 | /* |
| 240 | * Put CoreSight on PLLP_OUT0 (216 MHz) and divide it down by |
| 241 | * 1.5, giving an effective frequency of 144MHz. |
| 242 | * Set PLLP_OUT0 [bits31:30 = 00], and use a 7.1 divisor |
| 243 | * (bits 7:0), so 00000001b == 1.5 (n+1 + .5) |
| 244 | */ |
| 245 | src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000); |
Simon Glass | c2ea5e4 | 2011-09-21 12:40:04 +0000 | [diff] [blame] | 246 | clock_ll_set_source_divisor(PERIPH_ID_CSI, 0, src); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 247 | |
| 248 | /* Unlock the CPU CoreSight interfaces */ |
| 249 | rst = 0xC5ACCE55; |
| 250 | writel(rst, CSITE_CPU_DBG0_LAR); |
| 251 | writel(rst, CSITE_CPU_DBG1_LAR); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | void start_cpu(u32 reset_vector) |
| 256 | { |
| 257 | /* Enable VDD_CPU */ |
| 258 | enable_cpu_power_rail(); |
| 259 | |
| 260 | /* Hold the CPUs in reset */ |
| 261 | reset_A9_cpu(1); |
| 262 | |
| 263 | /* Disable the CPU clock */ |
| 264 | enable_cpu_clock(0); |
| 265 | |
| 266 | /* Enable CoreSight */ |
| 267 | clock_enable_coresight(1); |
| 268 | |
| 269 | /* |
| 270 | * Set the entry point for CPU execution from reset, |
| 271 | * if it's a non-zero value. |
| 272 | */ |
| 273 | if (reset_vector) |
| 274 | writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR); |
| 275 | |
| 276 | /* Enable the CPU clock */ |
| 277 | enable_cpu_clock(1); |
| 278 | |
| 279 | /* If the CPU doesn't already have power, power it up */ |
| 280 | powerup_cpu(); |
| 281 | |
| 282 | /* Take the CPU out of reset */ |
| 283 | reset_A9_cpu(0); |
| 284 | } |
| 285 | |
| 286 | |
| 287 | void halt_avp(void) |
| 288 | { |
| 289 | for (;;) { |
| 290 | writel((HALT_COP_EVENT_JTAG | HALT_COP_EVENT_IRQ_1 \ |
| 291 | | HALT_COP_EVENT_FIQ_1 | (FLOW_MODE_STOP<<29)), |
| 292 | FLOW_CTLR_HALT_COP_EVENTS); |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | void enable_scu(void) |
| 297 | { |
| 298 | struct scu_ctlr *scu = (struct scu_ctlr *)NV_PA_ARM_PERIPHBASE; |
| 299 | u32 reg; |
| 300 | |
| 301 | /* If SCU already setup/enabled, return */ |
| 302 | if (readl(&scu->scu_ctrl) & SCU_CTRL_ENABLE) |
| 303 | return; |
| 304 | |
| 305 | /* Invalidate all ways for all processors */ |
| 306 | writel(0xFFFF, &scu->scu_inv_all); |
| 307 | |
| 308 | /* Enable SCU - bit 0 */ |
| 309 | reg = readl(&scu->scu_ctrl); |
| 310 | reg |= SCU_CTRL_ENABLE; |
| 311 | writel(reg, &scu->scu_ctrl); |
| 312 | } |
| 313 | |
| 314 | void init_pmc_scratch(void) |
| 315 | { |
Simon Glass | cad1a27 | 2012-02-03 15:13:52 +0000 | [diff] [blame] | 316 | struct pmc_ctlr *const pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 317 | int i; |
| 318 | |
| 319 | /* SCRATCH0 is initialized by the boot ROM and shouldn't be cleared */ |
| 320 | for (i = 0; i < 23; i++) |
| 321 | writel(0, &pmc->pmc_scratch1+i); |
| 322 | |
| 323 | /* ODMDATA is for kernel use to determine RAM size, LP config, etc. */ |
| 324 | writel(CONFIG_SYS_BOARD_ODMDATA, &pmc->pmc_scratch20); |
| 325 | } |
| 326 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 327 | void tegra2_start(void) |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 328 | { |
| 329 | struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; |
| 330 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 331 | /* If we are the AVP, start up the first Cortex-A9 */ |
| 332 | if (!ap20_cpu_is_cortexa9()) { |
| 333 | /* enable JTAG */ |
| 334 | writel(0xC0, &pmt->pmt_cfg_ctl); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 335 | |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 336 | /* |
Tom Warren | 8b75b8f | 2012-02-17 06:01:21 +0000 | [diff] [blame] | 337 | * If we are ARM7 - give it a different stack. We are about to |
| 338 | * start up the A9 which will want to use this one. |
| 339 | */ |
| 340 | asm volatile("mov sp, %0\n" |
| 341 | : : "r"(AVP_EARLY_BOOT_STACK_LIMIT)); |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 342 | |
| 343 | start_cpu((u32)_start); |
| 344 | halt_avp(); |
| 345 | /* not reached */ |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 346 | } |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 347 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 348 | /* Init PMC scratch memory */ |
| 349 | init_pmc_scratch(); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 350 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 351 | enable_scu(); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 352 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 353 | /* enable SMP mode and FW for CPU0, by writing to Auxiliary Ctl reg */ |
| 354 | asm volatile( |
| 355 | "mrc p15, 0, r0, c1, c0, 1\n" |
| 356 | "orr r0, r0, #0x41\n" |
| 357 | "mcr p15, 0, r0, c1, c0, 1\n"); |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 358 | |
Simon Glass | ec8dab4 | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 359 | /* FIXME: should have ap20's L2 disabled too? */ |
Tom Warren | 112a188 | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 360 | } |