1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2018 NXP 4 * Peng Fan <peng.fan@nxp.com> 5 */ 6 7 #include <common.h> 8 #include <clk-uclass.h> 9 #include <dm.h> 10 #include <asm/arch/sci/sci.h> 11 #include <asm/arch/clock.h> 12 #include <dt-bindings/clock/imx8qxp-clock.h> 13 #include <dt-bindings/soc/imx_rsrc.h> 14 #include <misc.h> 15 16 struct imx8_clks { 17 ulong id; 18 const char *name; 19 }; 20 21 #if CONFIG_IS_ENABLED(CMD_CLK) 22 static struct imx8_clks imx8_clk_names[] = { 23 { IMX8QXP_A35_DIV, "A35_DIV" }, 24 { IMX8QXP_I2C0_CLK, "I2C0" }, 25 { IMX8QXP_I2C1_CLK, "I2C1" }, 26 { IMX8QXP_I2C2_CLK, "I2C2" }, 27 { IMX8QXP_I2C3_CLK, "I2C3" }, 28 { IMX8QXP_UART0_CLK, "UART0" }, 29 { IMX8QXP_UART1_CLK, "UART1" }, 30 { IMX8QXP_UART2_CLK, "UART2" }, 31 { IMX8QXP_UART3_CLK, "UART3" }, 32 { IMX8QXP_SDHC0_CLK, "SDHC0" }, 33 { IMX8QXP_SDHC1_CLK, "SDHC1" }, 34 { IMX8QXP_ENET0_AHB_CLK, "ENET0_AHB" }, 35 { IMX8QXP_ENET0_IPG_CLK, "ENET0_IPG" }, 36 { IMX8QXP_ENET0_REF_DIV, "ENET0_REF" }, 37 { IMX8QXP_ENET0_PTP_CLK, "ENET0_PTP" }, 38 { IMX8QXP_ENET1_AHB_CLK, "ENET1_AHB" }, 39 { IMX8QXP_ENET1_IPG_CLK, "ENET1_IPG" }, 40 { IMX8QXP_ENET1_REF_DIV, "ENET1_REF" }, 41 { IMX8QXP_ENET1_PTP_CLK, "ENET1_PTP" }, 42 }; 43 #endif 44 45 static ulong imx8_clk_get_rate(struct clk *clk) 46 { 47 sc_pm_clk_t pm_clk; 48 ulong rate; 49 u16 resource; 50 int ret; 51 52 debug("%s(#%lu)\n", __func__, clk->id); 53 54 switch (clk->id) { 55 case IMX8QXP_A35_DIV: 56 resource = SC_R_A35; 57 pm_clk = SC_PM_CLK_CPU; 58 break; 59 case IMX8QXP_I2C0_CLK: 60 resource = SC_R_I2C_0; 61 pm_clk = SC_PM_CLK_PER; 62 break; 63 case IMX8QXP_I2C1_CLK: 64 resource = SC_R_I2C_1; 65 pm_clk = SC_PM_CLK_PER; 66 break; 67 case IMX8QXP_I2C2_CLK: 68 resource = SC_R_I2C_2; 69 pm_clk = SC_PM_CLK_PER; 70 break; 71 case IMX8QXP_I2C3_CLK: 72 resource = SC_R_I2C_3; 73 pm_clk = SC_PM_CLK_PER; 74 break; 75 case IMX8QXP_SDHC0_IPG_CLK: 76 case IMX8QXP_SDHC0_CLK: 77 case IMX8QXP_SDHC0_DIV: 78 resource = SC_R_SDHC_0; 79 pm_clk = SC_PM_CLK_PER; 80 break; 81 case IMX8QXP_SDHC1_IPG_CLK: 82 case IMX8QXP_SDHC1_CLK: 83 case IMX8QXP_SDHC1_DIV: 84 resource = SC_R_SDHC_1; 85 pm_clk = SC_PM_CLK_PER; 86 break; 87 case IMX8QXP_UART0_IPG_CLK: 88 case IMX8QXP_UART0_CLK: 89 resource = SC_R_UART_0; 90 pm_clk = SC_PM_CLK_PER; 91 break; 92 case IMX8QXP_UART1_CLK: 93 resource = SC_R_UART_1; 94 pm_clk = SC_PM_CLK_PER; 95 break; 96 case IMX8QXP_UART2_CLK: 97 resource = SC_R_UART_2; 98 pm_clk = SC_PM_CLK_PER; 99 break; 100 case IMX8QXP_UART3_CLK: 101 resource = SC_R_UART_3; 102 pm_clk = SC_PM_CLK_PER; 103 break; 104 case IMX8QXP_ENET0_IPG_CLK: 105 case IMX8QXP_ENET0_AHB_CLK: 106 case IMX8QXP_ENET0_REF_DIV: 107 case IMX8QXP_ENET0_PTP_CLK: 108 resource = SC_R_ENET_0; 109 pm_clk = SC_PM_CLK_PER; 110 break; 111 case IMX8QXP_ENET1_IPG_CLK: 112 case IMX8QXP_ENET1_AHB_CLK: 113 case IMX8QXP_ENET1_REF_DIV: 114 case IMX8QXP_ENET1_PTP_CLK: 115 resource = SC_R_ENET_1; 116 pm_clk = SC_PM_CLK_PER; 117 break; 118 default: 119 if (clk->id < IMX8QXP_UART0_IPG_CLK || 120 clk->id >= IMX8QXP_CLK_END) { 121 printf("%s(Invalid clk ID #%lu)\n", 122 __func__, clk->id); 123 return -EINVAL; 124 } 125 return -ENOTSUPP; 126 }; 127 128 ret = sc_pm_get_clock_rate(-1, resource, pm_clk, 129 (sc_pm_clock_rate_t *)&rate); 130 if (ret) { 131 printf("%s err %d\n", __func__, ret); 132 return ret; 133 } 134 135 return rate; 136 } 137 138 static ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate) 139 { 140 sc_pm_clk_t pm_clk; 141 u32 new_rate = rate; 142 u16 resource; 143 int ret; 144 145 debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate); 146 147 switch (clk->id) { 148 case IMX8QXP_I2C0_CLK: 149 resource = SC_R_I2C_0; 150 pm_clk = SC_PM_CLK_PER; 151 break; 152 case IMX8QXP_I2C1_CLK: 153 resource = SC_R_I2C_1; 154 pm_clk = SC_PM_CLK_PER; 155 break; 156 case IMX8QXP_I2C2_CLK: 157 resource = SC_R_I2C_2; 158 pm_clk = SC_PM_CLK_PER; 159 break; 160 case IMX8QXP_I2C3_CLK: 161 resource = SC_R_I2C_3; 162 pm_clk = SC_PM_CLK_PER; 163 break; 164 case IMX8QXP_UART0_CLK: 165 resource = SC_R_UART_0; 166 pm_clk = SC_PM_CLK_PER; 167 break; 168 case IMX8QXP_UART1_CLK: 169 resource = SC_R_UART_1; 170 pm_clk = SC_PM_CLK_PER; 171 break; 172 case IMX8QXP_UART2_CLK: 173 resource = SC_R_UART_2; 174 pm_clk = SC_PM_CLK_PER; 175 break; 176 case IMX8QXP_UART3_CLK: 177 resource = SC_R_UART_3; 178 pm_clk = SC_PM_CLK_PER; 179 break; 180 case IMX8QXP_SDHC0_IPG_CLK: 181 case IMX8QXP_SDHC0_CLK: 182 case IMX8QXP_SDHC0_DIV: 183 resource = SC_R_SDHC_0; 184 pm_clk = SC_PM_CLK_PER; 185 break; 186 case IMX8QXP_SDHC1_SEL: 187 case IMX8QXP_SDHC0_SEL: 188 return 0; 189 case IMX8QXP_SDHC1_IPG_CLK: 190 case IMX8QXP_SDHC1_CLK: 191 case IMX8QXP_SDHC1_DIV: 192 resource = SC_R_SDHC_1; 193 pm_clk = SC_PM_CLK_PER; 194 break; 195 case IMX8QXP_ENET0_IPG_CLK: 196 case IMX8QXP_ENET0_AHB_CLK: 197 case IMX8QXP_ENET0_REF_DIV: 198 case IMX8QXP_ENET0_PTP_CLK: 199 resource = SC_R_ENET_0; 200 pm_clk = SC_PM_CLK_PER; 201 break; 202 case IMX8QXP_ENET1_IPG_CLK: 203 case IMX8QXP_ENET1_AHB_CLK: 204 case IMX8QXP_ENET1_REF_DIV: 205 case IMX8QXP_ENET1_PTP_CLK: 206 resource = SC_R_ENET_1; 207 pm_clk = SC_PM_CLK_PER; 208 break; 209 default: 210 if (clk->id < IMX8QXP_UART0_IPG_CLK || 211 clk->id >= IMX8QXP_CLK_END) { 212 printf("%s(Invalid clk ID #%lu)\n", 213 __func__, clk->id); 214 return -EINVAL; 215 } 216 return -ENOTSUPP; 217 }; 218 219 ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate); 220 if (ret) { 221 printf("%s err %d\n", __func__, ret); 222 return ret; 223 } 224 225 return new_rate; 226 } 227 228 static int __imx8_clk_enable(struct clk *clk, bool enable) 229 { 230 sc_pm_clk_t pm_clk; 231 u16 resource; 232 int ret; 233 234 debug("%s(#%lu)\n", __func__, clk->id); 235 236 switch (clk->id) { 237 case IMX8QXP_I2C0_CLK: 238 resource = SC_R_I2C_0; 239 pm_clk = SC_PM_CLK_PER; 240 break; 241 case IMX8QXP_I2C1_CLK: 242 resource = SC_R_I2C_1; 243 pm_clk = SC_PM_CLK_PER; 244 break; 245 case IMX8QXP_I2C2_CLK: 246 resource = SC_R_I2C_2; 247 pm_clk = SC_PM_CLK_PER; 248 break; 249 case IMX8QXP_I2C3_CLK: 250 resource = SC_R_I2C_3; 251 pm_clk = SC_PM_CLK_PER; 252 break; 253 case IMX8QXP_UART0_CLK: 254 resource = SC_R_UART_0; 255 pm_clk = SC_PM_CLK_PER; 256 break; 257 case IMX8QXP_UART1_CLK: 258 resource = SC_R_UART_1; 259 pm_clk = SC_PM_CLK_PER; 260 break; 261 case IMX8QXP_UART2_CLK: 262 resource = SC_R_UART_2; 263 pm_clk = SC_PM_CLK_PER; 264 break; 265 case IMX8QXP_UART3_CLK: 266 resource = SC_R_UART_3; 267 pm_clk = SC_PM_CLK_PER; 268 break; 269 case IMX8QXP_SDHC0_IPG_CLK: 270 case IMX8QXP_SDHC0_CLK: 271 case IMX8QXP_SDHC0_DIV: 272 resource = SC_R_SDHC_0; 273 pm_clk = SC_PM_CLK_PER; 274 break; 275 case IMX8QXP_SDHC1_IPG_CLK: 276 case IMX8QXP_SDHC1_CLK: 277 case IMX8QXP_SDHC1_DIV: 278 resource = SC_R_SDHC_1; 279 pm_clk = SC_PM_CLK_PER; 280 break; 281 case IMX8QXP_ENET0_IPG_CLK: 282 case IMX8QXP_ENET0_AHB_CLK: 283 case IMX8QXP_ENET0_REF_DIV: 284 case IMX8QXP_ENET0_PTP_CLK: 285 resource = SC_R_ENET_0; 286 pm_clk = SC_PM_CLK_PER; 287 break; 288 case IMX8QXP_ENET1_IPG_CLK: 289 case IMX8QXP_ENET1_AHB_CLK: 290 case IMX8QXP_ENET1_REF_DIV: 291 case IMX8QXP_ENET1_PTP_CLK: 292 resource = SC_R_ENET_1; 293 pm_clk = SC_PM_CLK_PER; 294 break; 295 default: 296 if (clk->id < IMX8QXP_UART0_IPG_CLK || 297 clk->id >= IMX8QXP_CLK_END) { 298 printf("%s(Invalid clk ID #%lu)\n", 299 __func__, clk->id); 300 return -EINVAL; 301 } 302 return -ENOTSUPP; 303 } 304 305 ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0); 306 if (ret) { 307 printf("%s err %d\n", __func__, ret); 308 return ret; 309 } 310 311 return 0; 312 } 313 314 static int imx8_clk_disable(struct clk *clk) 315 { 316 return __imx8_clk_enable(clk, 0); 317 } 318 319 static int imx8_clk_enable(struct clk *clk) 320 { 321 return __imx8_clk_enable(clk, 1); 322 } 323 324 #if CONFIG_IS_ENABLED(CMD_CLK) 325 int soc_clk_dump(void) 326 { 327 struct udevice *dev; 328 struct clk clk; 329 unsigned long rate; 330 int i, ret; 331 332 ret = uclass_get_device_by_driver(UCLASS_CLK, 333 DM_GET_DRIVER(imx8_clk), &dev); 334 if (ret) 335 return ret; 336 337 printf("Clk\t\tHz\n"); 338 339 for (i = 0; i < ARRAY_SIZE(imx8_clk_names); i++) { 340 clk.id = imx8_clk_names[i].id; 341 ret = clk_request(dev, &clk); 342 if (ret < 0) { 343 debug("%s clk_request() failed: %d\n", __func__, ret); 344 continue; 345 } 346 347 ret = clk_get_rate(&clk); 348 rate = ret; 349 350 clk_free(&clk); 351 352 if (ret == -ENOTSUPP) { 353 printf("clk ID %lu not supported yet\n", 354 imx8_clk_names[i].id); 355 continue; 356 } 357 if (ret < 0) { 358 printf("%s %lu: get_rate err: %d\n", 359 __func__, imx8_clk_names[i].id, ret); 360 continue; 361 } 362 363 printf("%s(%3lu):\t%lu\n", 364 imx8_clk_names[i].name, imx8_clk_names[i].id, rate); 365 } 366 367 return 0; 368 } 369 #endif 370 371 static struct clk_ops imx8_clk_ops = { 372 .set_rate = imx8_clk_set_rate, 373 .get_rate = imx8_clk_get_rate, 374 .enable = imx8_clk_enable, 375 .disable = imx8_clk_disable, 376 }; 377 378 static int imx8_clk_probe(struct udevice *dev) 379 { 380 return 0; 381 } 382 383 static const struct udevice_id imx8_clk_ids[] = { 384 { .compatible = "fsl,imx8qxp-clk" }, 385 { }, 386 }; 387 388 U_BOOT_DRIVER(imx8_clk) = { 389 .name = "clk_imx8", 390 .id = UCLASS_CLK, 391 .of_match = imx8_clk_ids, 392 .ops = &imx8_clk_ops, 393 .probe = imx8_clk_probe, 394 .flags = DM_FLAG_PRE_RELOC, 395 }; 396