Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2014 Imagination Technologies |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License as |
| 8 | * published by the Free Software Foundation; version 2 of |
| 9 | * the License. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 15 | */ |
| 16 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 17 | #include <device/mmio.h> |
Ionela Voinescu | 1d4c305 | 2015-06-07 23:22:34 +0100 | [diff] [blame] | 18 | #include <assert.h> |
| 19 | #include <delay.h> |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 20 | #include <soc/clocks.h> |
| 21 | #include <timer.h> |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 22 | |
| 23 | /* Definitions for PLL enable */ |
| 24 | #define PISTACHIO_CLOCK_SWITCH 0xB8144200 |
| 25 | |
| 26 | #define SYS_EXTERN_PLL_BYPASS_MASK 0x00002000 |
| 27 | #define SYS_PLL_CTRL4_ADDR 0xB8144048 |
| 28 | #define SYS_INTERNAL_PLL_BYPASS_MASK 0x10000000 |
| 29 | #define SYS_PLL_PD_CTRL_ADDR 0xB8144044 |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 30 | #define SYS_PLL_PD_CTRL_PD_MASK 0x00000039 |
| 31 | #define SYS_PLL_DACPD_ADDR 0xB8144044 |
| 32 | #define SYS_PLL_DACPD_MASK 0x00000002 |
| 33 | #define SYS_PLL_DSMPD_ADDR 0xB8144044 |
| 34 | #define SYS_PLL_DSMPD_MASK 0x00000004 |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 35 | |
| 36 | #define MIPS_EXTERN_PLL_BYPASS_MASK 0x00000002 |
| 37 | #define MIPS_PLL_CTRL2_ADDR 0xB8144008 |
| 38 | #define MIPS_INTERNAL_PLL_BYPASS_MASK 0x10000000 |
| 39 | #define MIPS_PLL_PD_CTRL_ADDR 0xB8144004 |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 40 | #define MIPS_PLL_PD_CTRL_PD_MASK 0x0D000000 |
| 41 | #define MIPS_PLL_DSMPD_ADDR 0xB8144004 |
| 42 | #define MIPS_PLL_DSMPD_MASK 0x02000000 |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 43 | |
| 44 | /* Definitions for PLL dividers */ |
| 45 | #define SYS_PLL_POSTDIV_ADDR 0xB8144040 |
| 46 | #define SYS_PLL_POSTDIV1_MASK 0x07000000 |
| 47 | #define SYS_PLL_POSTDIV1_SHIFT 24 |
| 48 | #define SYS_PLL_POSTDIV2_MASK 0x38000000 |
| 49 | #define SYS_PLL_POSTDIV2_SHIFT 27 |
| 50 | #define SYS_PLL_STATUS_ADDR 0xB8144038 |
| 51 | #define SYS_PLL_STATUS_LOCK_MASK 0x00000001 |
| 52 | |
Ionela Voinescu | 90d1235 | 2015-07-15 12:10:05 +0100 | [diff] [blame] | 53 | #define SYS_PLL_REFDIV_ADDR 0xB814403C |
| 54 | #define SYS_PLL_REFDIV_MASK 0x0000003F |
| 55 | #define SYS_PLL_REFDIV_SHIFT 0 |
| 56 | #define SYS_PLL_FEEDBACK_ADDR 0xB814403C |
| 57 | #define SYS_PLL_FEEDBACK_MASK 0x0003FFC0 |
| 58 | #define SYS_PLL_FEEDBACK_SHIFT 6 |
| 59 | |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 60 | #define MIPS_PLL_POSTDIV_ADDR 0xB8144004 |
| 61 | #define MIPS_PLL_POSTDIV1_MASK 0x001C0000 |
| 62 | #define MIPS_PLL_POSTDIV1_SHIFT 18 |
| 63 | #define MIPS_PLL_POSTDIV2_MASK 0x00E00000 |
| 64 | #define MIPS_PLL_POSTDIV2_SHIFT 21 |
| 65 | #define MIPS_PLL_STATUS_ADDR 0xB8144000 |
| 66 | #define MIPS_PLL_STATUS_LOCK_MASK 0x00000001 |
| 67 | |
| 68 | #define MIPS_REFDIV_ADDR 0xB8144004 |
| 69 | #define MIPS_REFDIV_MASK 0x0000003F |
| 70 | #define MIPS_REFDIV_SHIFT 0 |
| 71 | #define MIPS_FEEDBACK_ADDR 0xB8144004 |
| 72 | #define MIPS_FEEDBACK_MASK 0x0003FFC0 |
| 73 | #define MIPS_FEEDBACK_SHIFT 6 |
| 74 | |
| 75 | /* Definitions for system clock setup */ |
| 76 | #define SYSCLKINTERNAL_CTRL_ADDR 0xB8144244 |
| 77 | #define SYSCLKINTERNAL_MASK 0X00000007 |
| 78 | |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 79 | /* Definitions for MIPS clock setup */ |
| 80 | #define MIPSCLKINTERNAL_CTRL_ADDR 0xB8144204 |
| 81 | #define MIPSCLKINTERNAL_MASK 0x00000003 |
| 82 | #define MIPSCLKOUT_CTRL_ADDR 0xB8144208 |
| 83 | #define MIPSCLKOUT_MASK 0x000000FF |
| 84 | |
Ionela Voinescu | 4f3d400 | 2015-11-01 19:55:48 +0000 | [diff] [blame] | 85 | /* Peripheral Clock gate reg */ |
| 86 | #define MIPS_CLOCK_GATE_ADDR 0xB8144900 |
| 87 | #define RPU_CLOCK_GATE_ADDR 0xB8144904 |
| 88 | #define MIPS_CLOCK_GATE_ALL_ON 0x3fff |
| 89 | #define RPU_CLOCK_GATE_ALL_OFF 0x0 |
| 90 | |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 91 | /* Definitions for USB clock setup */ |
| 92 | #define USBPHYCLKOUT_CTRL_ADDR 0xB814422C |
| 93 | #define USBPHYCLKOUT_MASK 0X0000003F |
| 94 | #define USBPHYCONTROL1_ADDR 0xB8149004 |
| 95 | #define USBPHYCONTROL1_FSEL_SHIFT 2 |
| 96 | #define USBPHYCONTROL1_FSEL_MASK 0x1C |
| 97 | #define USBPHYSTRAPCTRL_ADDR 0xB8149010 |
| 98 | #define USBPHYSTRAPCTRL_REFCLKSEL_SHIFT 4 |
| 99 | #define USBPHYSTRAPCTRL_REFCLKSEL_MASK 0x30 |
| 100 | #define USBPHYSTATUS_ADDR 0xB8149014 |
| 101 | #define USBPHYSTATUS_RX_PHY_CLK_MASK 0x200 |
| 102 | #define USBPHYSTATUS_RX_UTMI_CLK_MASK 0x100 |
| 103 | #define USBPHYSTATUS_VBUS_FAULT_MASK 0x80 |
| 104 | |
| 105 | /* Definitions for UART0/1 setup */ |
| 106 | #define UART0CLKINTERNAL_CTRL_ADDR 0xB8144234 |
| 107 | #define UART0CLKINTERNAL_MASK 0x00000007 |
| 108 | #define UART0CLKOUT_CTRL_ADDR 0xB8144238 |
| 109 | #define UART0CLKOUT_MASK 0x000003FF |
| 110 | #define UART1CLKINTERNAL_CTRL_ADDR 0xB814423C |
| 111 | #define UART1CLKINTERNAL_MASK 0x00000007 |
| 112 | #define UART1CLKOUT_CTRL_ADDR 0xB8144240 |
| 113 | #define UART1CLKOUT_MASK 0x000003FF |
| 114 | |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 115 | /* Definitions for I2C setup */ |
Ionela Voinescu | 8835754 | 2015-09-15 13:56:30 +0100 | [diff] [blame] | 116 | #define I2CCLKDIV1_CTRL_ADDR(i) (0xB8144800 + 0x013C + (2*(i)*4)) |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 117 | #define I2CCLKDIV1_MASK 0x0000007F |
Ionela Voinescu | 8835754 | 2015-09-15 13:56:30 +0100 | [diff] [blame] | 118 | #define I2CCLKOUT_CTRL_ADDR(i) (0xB8144800 + 0x0140 + (2*(i)*4)) |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 119 | #define I2CCLKOUT_MASK 0x0000007F |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 120 | |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 121 | /* Definitions for ROM clock setup */ |
Ionela Voinescu | a702390 | 2015-02-02 14:34:24 +0000 | [diff] [blame] | 122 | #define ROMCLKOUT_CTRL_ADDR 0xB814490C |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 123 | #define ROMCLKOUT_MASK 0x0000007F |
| 124 | |
| 125 | /* Definitions for ETH clock setup */ |
| 126 | #define ENETCLKMUX_MASK 0x00004000 |
| 127 | #define ENETCLKDIV_CTRL_ADDR 0xB8144230 |
| 128 | #define ENETCLKDIV_MASK 0x0000003F |
| 129 | |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 130 | /* Definitions for timeout values */ |
| 131 | #define PLL_TIMEOUT_VALUE_US 20000 |
| 132 | #define USB_TIMEOUT_VALUE_US 200000 |
| 133 | #define SYS_CLK_LOCK_DELAY 3 |
| 134 | |
| 135 | struct pll_parameters { |
| 136 | u32 external_bypass_mask; |
| 137 | u32 ctrl_addr; |
| 138 | u32 internal_bypass_mask; |
| 139 | u32 power_down_ctrl_addr; |
| 140 | u32 power_down_ctrl_mask; |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 141 | u32 dacpd_addr; |
| 142 | u32 dacpd_mask; |
| 143 | u32 dsmpd_addr; |
| 144 | u32 dsmpd_mask; |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 145 | u32 postdiv_addr; |
| 146 | u32 postdiv1_shift; |
| 147 | u32 postdiv1_mask; |
| 148 | u32 postdiv2_shift; |
| 149 | u32 postdiv2_mask; |
| 150 | u32 status_addr; |
| 151 | u32 status_lock_mask; |
| 152 | u32 refdivider; |
| 153 | u32 refdiv_addr; |
| 154 | u32 refdiv_shift; |
| 155 | u32 refdiv_mask; |
| 156 | u32 feedback; |
| 157 | u32 feedback_addr; |
| 158 | u32 feedback_shift; |
| 159 | u32 feedback_mask; |
| 160 | }; |
| 161 | |
| 162 | enum plls { |
| 163 | SYS_PLL = 0, |
| 164 | MIPS_PLL = 1 |
| 165 | }; |
| 166 | |
| 167 | static struct pll_parameters pll_params[] = { |
| 168 | [SYS_PLL] = { |
| 169 | .external_bypass_mask = SYS_EXTERN_PLL_BYPASS_MASK, |
| 170 | .ctrl_addr = SYS_PLL_CTRL4_ADDR, |
| 171 | .internal_bypass_mask = SYS_INTERNAL_PLL_BYPASS_MASK, |
| 172 | .power_down_ctrl_addr = SYS_PLL_PD_CTRL_ADDR, |
| 173 | .power_down_ctrl_mask = SYS_PLL_PD_CTRL_PD_MASK, |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 174 | /* Noise cancellation */ |
| 175 | .dacpd_addr = SYS_PLL_DACPD_ADDR, |
| 176 | .dacpd_mask = SYS_PLL_DACPD_MASK, |
| 177 | .dsmpd_addr = SYS_PLL_DSMPD_ADDR, |
| 178 | /* 0 - Integer mode |
| 179 | * SYS_PLL_DSMPD_MASK - Fractional mode |
| 180 | */ |
| 181 | .dsmpd_mask = 0, |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 182 | .postdiv_addr = SYS_PLL_POSTDIV_ADDR, |
| 183 | .postdiv1_shift = SYS_PLL_POSTDIV1_SHIFT, |
| 184 | .postdiv1_mask = SYS_PLL_POSTDIV1_MASK, |
| 185 | .postdiv2_shift = SYS_PLL_POSTDIV2_SHIFT, |
| 186 | .postdiv2_mask = SYS_PLL_POSTDIV2_MASK, |
| 187 | .status_addr = SYS_PLL_STATUS_ADDR, |
| 188 | .status_lock_mask = SYS_PLL_STATUS_LOCK_MASK, |
| 189 | .refdivider = 0, /* Not defined yet */ |
Ionela Voinescu | 90d1235 | 2015-07-15 12:10:05 +0100 | [diff] [blame] | 190 | .refdiv_addr = SYS_PLL_REFDIV_ADDR, |
| 191 | .refdiv_shift = SYS_PLL_REFDIV_SHIFT, |
| 192 | .refdiv_mask = SYS_PLL_REFDIV_MASK, |
| 193 | .feedback = 0, /* Not defined yet */ |
| 194 | .feedback_addr = SYS_PLL_FEEDBACK_ADDR, |
| 195 | .feedback_shift = SYS_PLL_FEEDBACK_SHIFT, |
| 196 | .feedback_mask = SYS_PLL_FEEDBACK_MASK |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 197 | }, |
| 198 | |
| 199 | [MIPS_PLL] = { |
| 200 | .external_bypass_mask = MIPS_EXTERN_PLL_BYPASS_MASK, |
| 201 | .ctrl_addr = MIPS_PLL_CTRL2_ADDR, |
| 202 | .internal_bypass_mask = MIPS_INTERNAL_PLL_BYPASS_MASK, |
| 203 | .power_down_ctrl_addr = MIPS_PLL_PD_CTRL_ADDR, |
| 204 | .power_down_ctrl_mask = MIPS_PLL_PD_CTRL_PD_MASK, |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 205 | .dacpd_addr = 0, |
| 206 | .dacpd_mask = 0, |
| 207 | .dsmpd_addr = MIPS_PLL_DSMPD_ADDR, |
| 208 | .dsmpd_mask = MIPS_PLL_DSMPD_MASK, |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 209 | .postdiv_addr = MIPS_PLL_POSTDIV_ADDR, |
| 210 | .postdiv1_shift = MIPS_PLL_POSTDIV1_SHIFT, |
| 211 | .postdiv1_mask = MIPS_PLL_POSTDIV1_MASK, |
| 212 | .postdiv2_shift = MIPS_PLL_POSTDIV2_SHIFT, |
| 213 | .postdiv2_mask = MIPS_PLL_POSTDIV2_MASK, |
| 214 | .status_addr = MIPS_PLL_STATUS_ADDR, |
| 215 | .status_lock_mask = MIPS_PLL_STATUS_LOCK_MASK, |
| 216 | .refdivider = 0, /* Not defined yet */ |
| 217 | .refdiv_addr = MIPS_REFDIV_ADDR, |
| 218 | .refdiv_shift = MIPS_REFDIV_SHIFT, |
| 219 | .refdiv_mask = MIPS_REFDIV_MASK, |
| 220 | .feedback = 0, /* Not defined yet */ |
| 221 | .feedback_addr = MIPS_FEEDBACK_ADDR, |
| 222 | .feedback_shift = MIPS_FEEDBACK_SHIFT, |
| 223 | .feedback_mask = MIPS_FEEDBACK_MASK |
| 224 | } |
| 225 | }; |
| 226 | |
| 227 | static int pll_setup(struct pll_parameters *param, u8 divider1, u8 divider2) |
| 228 | { |
| 229 | u32 reg; |
| 230 | struct stopwatch sw; |
| 231 | |
| 232 | /* Check input parameters */ |
Ionela Voinescu | 41d1ca8 | 2015-01-26 17:15:59 +0000 | [diff] [blame] | 233 | assert(!((divider1 << param->postdiv1_shift) & |
| 234 | ~(param->postdiv1_mask))); |
| 235 | assert(!((divider2 << param->postdiv2_shift) & |
| 236 | ~(param->postdiv2_mask))); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 237 | |
| 238 | /* Temporary bypass PLL (select XTAL as clock input) */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 239 | reg = read32_x(PISTACHIO_CLOCK_SWITCH); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 240 | reg &= ~(param->external_bypass_mask); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 241 | write32_x(PISTACHIO_CLOCK_SWITCH, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 242 | |
| 243 | /* Un-bypass PLL's internal bypass */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 244 | reg = read32_x(param->ctrl_addr); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 245 | reg &= ~(param->internal_bypass_mask); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 246 | write32_x(param->ctrl_addr, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 247 | |
| 248 | /* Disable power down */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 249 | reg = read32_x(param->power_down_ctrl_addr); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 250 | reg &= ~(param->power_down_ctrl_mask); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 251 | write32_x(param->power_down_ctrl_addr, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 252 | |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 253 | /* Noise cancellation */ |
| 254 | if (param->dacpd_addr) { |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 255 | reg = read32_x(param->dacpd_addr); |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 256 | reg &= ~(param->dacpd_mask); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 257 | write32_x(param->dacpd_addr, reg); |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 258 | } |
| 259 | |
| 260 | /* Functional mode */ |
| 261 | if (param->dsmpd_addr) { |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 262 | reg = read32_x(param->dsmpd_addr); |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 263 | reg &= ~(param->dsmpd_mask); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 264 | write32_x(param->dsmpd_addr, reg); |
Ionela Voinescu | 1136447 | 2015-07-15 12:42:01 +0100 | [diff] [blame] | 265 | } |
| 266 | |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 267 | if (param->feedback_addr) { |
Ionela Voinescu | 41d1ca8 | 2015-01-26 17:15:59 +0000 | [diff] [blame] | 268 | assert(!((param->feedback << param->feedback_shift) & |
| 269 | ~(param->feedback_mask))); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 270 | reg = read32_x(param->feedback_addr); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 271 | reg &= ~(param->feedback_mask); |
| 272 | reg |= (param->feedback << param->feedback_shift) & |
| 273 | param->feedback_mask; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 274 | write32_x(param->feedback_addr, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | if (param->refdiv_addr) { |
Ionela Voinescu | 41d1ca8 | 2015-01-26 17:15:59 +0000 | [diff] [blame] | 278 | assert(!((param->refdivider << param->refdiv_shift) & |
| 279 | ~(param->refdiv_mask))); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 280 | reg = read32_x(param->refdiv_addr); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 281 | reg &= ~(param->refdiv_mask); |
| 282 | reg |= (param->refdivider << param->refdiv_shift) & |
| 283 | param->refdiv_mask; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 284 | write32_x(param->refdiv_addr, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 285 | } |
| 286 | |
| 287 | /* Read postdivider register value */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 288 | reg = read32_x(param->postdiv_addr); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 289 | /* Set divider 1 */ |
| 290 | reg &= ~(param->postdiv1_mask); |
| 291 | reg |= (divider1 << param->postdiv1_shift) & |
| 292 | param->postdiv1_mask; |
| 293 | /* Set divider 2 */ |
| 294 | reg &= ~(param->postdiv2_mask); |
| 295 | reg |= (divider2 << param->postdiv2_shift) & |
| 296 | param->postdiv2_mask; |
| 297 | /* Write back to register */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 298 | write32_x(param->postdiv_addr, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 299 | |
| 300 | /* Waiting for PLL to lock*/ |
| 301 | stopwatch_init_usecs_expire(&sw, PLL_TIMEOUT_VALUE_US); |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 302 | while (!(read32_x(param->status_addr) & param->status_lock_mask)) { |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 303 | if (stopwatch_expired(&sw)) |
| 304 | return PLL_TIMEOUT; |
| 305 | } |
| 306 | |
| 307 | /* Start using PLL */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 308 | reg = read32_x(PISTACHIO_CLOCK_SWITCH); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 309 | reg |= param->external_bypass_mask; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 310 | write32_x(PISTACHIO_CLOCK_SWITCH, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 311 | |
| 312 | return CLOCKS_OK; |
| 313 | } |
| 314 | |
Ionela Voinescu | 90d1235 | 2015-07-15 12:10:05 +0100 | [diff] [blame] | 315 | int sys_pll_setup(u8 divider1, u8 divider2, u8 refdivider, u32 feedback) |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 316 | { |
Ionela Voinescu | 90d1235 | 2015-07-15 12:10:05 +0100 | [diff] [blame] | 317 | pll_params[SYS_PLL].refdivider = refdivider; |
| 318 | pll_params[SYS_PLL].feedback = feedback; |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 319 | return pll_setup(&(pll_params[SYS_PLL]), divider1, divider2); |
| 320 | } |
| 321 | |
| 322 | int mips_pll_setup(u8 divider1, u8 divider2, u8 refdivider, u32 feedback) |
| 323 | { |
| 324 | pll_params[MIPS_PLL].refdivider = refdivider; |
| 325 | pll_params[MIPS_PLL].feedback = feedback; |
| 326 | return pll_setup(&(pll_params[MIPS_PLL]), divider1, divider2); |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | * uart1_clk_setup: sets up clocks for UART1 |
| 331 | * divider1: 3-bit divider value |
| 332 | * divider2: 10-bit divider value |
| 333 | */ |
| 334 | void uart1_clk_setup(u8 divider1, u16 divider2) |
| 335 | { |
| 336 | u32 reg; |
| 337 | |
| 338 | /* Check input parameters */ |
| 339 | assert(!(divider1 & ~(UART1CLKINTERNAL_MASK))); |
| 340 | assert(!(divider2 & ~(UART1CLKOUT_MASK))); |
| 341 | |
| 342 | /* Set divider 1 */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 343 | reg = read32_x(UART1CLKINTERNAL_CTRL_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 344 | reg &= ~UART1CLKINTERNAL_MASK; |
| 345 | reg |= divider1 & UART1CLKINTERNAL_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 346 | write32_x(UART1CLKINTERNAL_CTRL_ADDR, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 347 | |
| 348 | /* Set divider 2 */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 349 | reg = read32_x(UART1CLKOUT_CTRL_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 350 | reg &= ~UART1CLKOUT_MASK; |
| 351 | reg |= divider2 & UART1CLKOUT_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 352 | write32_x(UART1CLKOUT_CTRL_ADDR, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 353 | } |
| 354 | |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 355 | /* |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 356 | * i2c_clk_setup: sets up clocks for I2C |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 357 | * divider1: 7-bit divider value |
| 358 | * divider2: 7-bit divider value |
| 359 | */ |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 360 | void i2c_clk_setup(u8 divider1, u16 divider2, u8 interface) |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 361 | { |
| 362 | u32 reg; |
| 363 | |
| 364 | /* Check input parameters */ |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 365 | assert(!(divider1 & ~(I2CCLKDIV1_MASK))); |
| 366 | assert(!(divider2 & ~(I2CCLKOUT_MASK))); |
| 367 | assert(interface < 4); |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 368 | /* Set divider 1 */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 369 | reg = read32_x(I2CCLKDIV1_CTRL_ADDR(interface)); |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 370 | reg &= ~I2CCLKDIV1_MASK; |
| 371 | reg |= divider1 & I2CCLKDIV1_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 372 | write32_x(I2CCLKDIV1_CTRL_ADDR(interface), reg); |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 373 | |
| 374 | /* Set divider 2 */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 375 | reg = read32_x(I2CCLKOUT_CTRL_ADDR(interface)); |
Ionela Voinescu | 38063b0 | 2015-03-05 13:09:57 +0000 | [diff] [blame] | 376 | reg &= ~I2CCLKOUT_MASK; |
| 377 | reg |= divider2 & I2CCLKOUT_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 378 | write32_x(I2CCLKOUT_CTRL_ADDR(interface), reg); |
Ionela Voinescu | fdce680 | 2015-02-17 18:28:34 +0000 | [diff] [blame] | 379 | } |
| 380 | |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 381 | /* system_clk_setup: sets up the system (peripheral) clock */ |
| 382 | void system_clk_setup(u8 divider) |
| 383 | { |
| 384 | u32 reg; |
| 385 | |
| 386 | /* Check input parameters */ |
| 387 | assert(!(divider & ~(SYSCLKINTERNAL_MASK))); |
| 388 | |
| 389 | /* Set system clock divider */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 390 | reg = read32_x(SYSCLKINTERNAL_CTRL_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 391 | reg &= ~SYSCLKINTERNAL_MASK; |
| 392 | reg |= divider & SYSCLKINTERNAL_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 393 | write32_x(SYSCLKINTERNAL_CTRL_ADDR, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 394 | |
| 395 | /* Small delay to cover a maximum lock time of 1500 cycles */ |
| 396 | udelay(SYS_CLK_LOCK_DELAY); |
| 397 | } |
| 398 | |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 399 | void mips_clk_setup(u8 divider1, u8 divider2) |
| 400 | { |
| 401 | u32 reg; |
| 402 | |
| 403 | /* Check input parameters */ |
| 404 | assert(!(divider1 & ~(MIPSCLKINTERNAL_MASK))); |
| 405 | assert(!(divider2 & ~(MIPSCLKOUT_MASK))); |
| 406 | |
| 407 | /* Set divider 1 */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 408 | reg = read32_x(MIPSCLKINTERNAL_CTRL_ADDR); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 409 | reg &= ~MIPSCLKINTERNAL_MASK; |
| 410 | reg |= divider1 & MIPSCLKINTERNAL_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 411 | write32_x(MIPSCLKINTERNAL_CTRL_ADDR, reg); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 412 | |
| 413 | /* Set divider 2 */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 414 | reg = read32_x(MIPSCLKOUT_CTRL_ADDR); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 415 | reg &= ~MIPSCLKOUT_MASK; |
| 416 | reg |= divider2 & MIPSCLKOUT_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 417 | write32_x(MIPSCLKOUT_CTRL_ADDR, reg); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 418 | } |
| 419 | |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 420 | /* usb_clk_setup: sets up USB clock */ |
| 421 | int usb_clk_setup(u8 divider, u8 refclksel, u8 fsel) |
| 422 | { |
| 423 | u32 reg; |
| 424 | struct stopwatch sw; |
| 425 | |
| 426 | /* Check input parameters */ |
| 427 | assert(!(divider & ~(USBPHYCLKOUT_MASK))); |
Ionela Voinescu | 41d1ca8 | 2015-01-26 17:15:59 +0000 | [diff] [blame] | 428 | assert(!((refclksel << USBPHYSTRAPCTRL_REFCLKSEL_SHIFT) & |
| 429 | ~(USBPHYSTRAPCTRL_REFCLKSEL_MASK))); |
| 430 | assert(!((fsel << USBPHYCONTROL1_FSEL_SHIFT) & |
| 431 | ~(USBPHYCONTROL1_FSEL_MASK))); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 432 | |
| 433 | /* Set USB divider */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 434 | reg = read32_x(USBPHYCLKOUT_CTRL_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 435 | reg &= ~USBPHYCLKOUT_MASK; |
| 436 | reg |= divider & USBPHYCLKOUT_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 437 | write32_x(USBPHYCLKOUT_CTRL_ADDR, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 438 | |
| 439 | /* Set REFCLKSEL */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 440 | reg = read32_x(USBPHYSTRAPCTRL_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 441 | reg &= ~USBPHYSTRAPCTRL_REFCLKSEL_MASK; |
| 442 | reg |= (refclksel << USBPHYSTRAPCTRL_REFCLKSEL_SHIFT) & |
| 443 | USBPHYSTRAPCTRL_REFCLKSEL_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 444 | write32_x(USBPHYSTRAPCTRL_ADDR, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 445 | |
| 446 | /* Set FSEL */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 447 | reg = read32_x(USBPHYCONTROL1_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 448 | reg &= ~USBPHYCONTROL1_FSEL_MASK; |
| 449 | reg |= (fsel << USBPHYCONTROL1_FSEL_SHIFT) & |
| 450 | USBPHYCONTROL1_FSEL_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 451 | write32_x(USBPHYCONTROL1_ADDR, reg); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 452 | |
| 453 | /* Waiting for USB clock status */ |
| 454 | stopwatch_init_usecs_expire(&sw, USB_TIMEOUT_VALUE_US); |
| 455 | while (1) { |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 456 | reg = read32_x(USBPHYSTATUS_ADDR); |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 457 | if (reg & USBPHYSTATUS_VBUS_FAULT_MASK) |
| 458 | return USB_VBUS_FAULT; |
| 459 | if (stopwatch_expired(&sw)) |
| 460 | return USB_TIMEOUT; |
Ionela Voinescu | 41d1ca8 | 2015-01-26 17:15:59 +0000 | [diff] [blame] | 461 | /* Check if USB is set up properly */ |
| 462 | if ((reg & USBPHYSTATUS_RX_PHY_CLK_MASK) && |
| 463 | (reg & USBPHYSTATUS_RX_UTMI_CLK_MASK)) |
| 464 | break; |
Ionela Voinescu | b3f666b | 2015-01-18 22:37:11 +0000 | [diff] [blame] | 465 | } |
| 466 | |
| 467 | return CLOCKS_OK; |
| 468 | } |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 469 | |
| 470 | void rom_clk_setup(u8 divider) |
| 471 | { |
| 472 | u32 reg; |
| 473 | |
| 474 | /* Check input parameter */ |
| 475 | assert(!(divider & ~(ROMCLKOUT_MASK))); |
| 476 | |
| 477 | /* Set ROM divider */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 478 | reg = read32_x(ROMCLKOUT_CTRL_ADDR); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 479 | reg &= ~ROMCLKOUT_MASK; |
| 480 | reg |= divider & ROMCLKOUT_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 481 | write32_x(ROMCLKOUT_CTRL_ADDR, reg); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 482 | } |
| 483 | |
| 484 | void eth_clk_setup(u8 mux, u8 divider) |
| 485 | { |
| 486 | |
| 487 | u32 reg; |
| 488 | |
| 489 | /* Check input parameters */ |
| 490 | assert(!(divider & ~(ENETCLKDIV_MASK))); |
| 491 | /* This can be either 0 or 1, selecting between |
| 492 | * ENET and system clock as clocksource */ |
| 493 | assert(!(mux & ~(0x1))); |
| 494 | |
| 495 | /* Set ETH divider */ |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 496 | reg = read32_x(ENETCLKDIV_CTRL_ADDR); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 497 | reg &= ~ENETCLKDIV_MASK; |
| 498 | reg |= divider & ENETCLKDIV_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 499 | write32_x(ENETCLKDIV_CTRL_ADDR, reg); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 500 | |
| 501 | /* Select source */ |
| 502 | if (mux) { |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 503 | reg = read32_x(PISTACHIO_CLOCK_SWITCH); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 504 | reg |= ENETCLKMUX_MASK; |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 505 | write32_x(PISTACHIO_CLOCK_SWITCH, reg); |
Ionela Voinescu | 8b1f23e | 2015-01-26 13:15:12 +0000 | [diff] [blame] | 506 | } |
| 507 | } |
Ionela Voinescu | 4f3d400 | 2015-11-01 19:55:48 +0000 | [diff] [blame] | 508 | |
| 509 | void setup_clk_gate_defaults(void) |
| 510 | { |
Kyösti Mälkki | a9506db | 2019-03-20 20:30:02 +0200 | [diff] [blame] | 511 | write32_x(MIPS_CLOCK_GATE_ADDR, MIPS_CLOCK_GATE_ALL_ON); |
| 512 | write32_x(RPU_CLOCK_GATE_ADDR, RPU_CLOCK_GATE_ALL_OFF); |
Ionela Voinescu | 4f3d400 | 2015-11-01 19:55:48 +0000 | [diff] [blame] | 513 | } |