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