19e5935c0SWenyou Yang /* 29e5935c0SWenyou Yang * Copyright (C) 2016 Atmel Corporation 39e5935c0SWenyou Yang * Wenyou.Yang <wenyou.yang@atmel.com> 49e5935c0SWenyou Yang * 59e5935c0SWenyou Yang * SPDX-License-Identifier: GPL-2.0+ 69e5935c0SWenyou Yang */ 79e5935c0SWenyou Yang 89e5935c0SWenyou Yang #include <common.h> 99e5935c0SWenyou Yang #include <clk-uclass.h> 109e5935c0SWenyou Yang #include <dm/device.h> 119e5935c0SWenyou Yang #include <linux/io.h> 129e5935c0SWenyou Yang #include <mach/at91_pmc.h> 139e5935c0SWenyou Yang #include "pmc.h" 149e5935c0SWenyou Yang 159e5935c0SWenyou Yang DECLARE_GLOBAL_DATA_PTR; 169e5935c0SWenyou Yang 179e5935c0SWenyou Yang #define GENERATED_SOURCE_MAX 6 189e5935c0SWenyou Yang #define GENERATED_MAX_DIV 255 199e5935c0SWenyou Yang 206cadaa04SWenyou Yang /** 216cadaa04SWenyou Yang * generated_clk_bind() - for the generated clock driver 226cadaa04SWenyou Yang * Recursively bind its children as clk devices. 236cadaa04SWenyou Yang * 246cadaa04SWenyou Yang * @return: 0 on success, or negative error code on failure 256cadaa04SWenyou Yang */ 266cadaa04SWenyou Yang static int generated_clk_bind(struct udevice *dev) 276cadaa04SWenyou Yang { 286cadaa04SWenyou Yang return at91_clk_sub_device_bind(dev, "generic-clk"); 296cadaa04SWenyou Yang } 306cadaa04SWenyou Yang 316cadaa04SWenyou Yang static const struct udevice_id generated_clk_match[] = { 326cadaa04SWenyou Yang { .compatible = "atmel,sama5d2-clk-generated" }, 336cadaa04SWenyou Yang {} 346cadaa04SWenyou Yang }; 356cadaa04SWenyou Yang 366cadaa04SWenyou Yang U_BOOT_DRIVER(generated_clk) = { 376cadaa04SWenyou Yang .name = "generated-clk", 386cadaa04SWenyou Yang .id = UCLASS_MISC, 396cadaa04SWenyou Yang .of_match = generated_clk_match, 406cadaa04SWenyou Yang .bind = generated_clk_bind, 416cadaa04SWenyou Yang }; 426cadaa04SWenyou Yang 436cadaa04SWenyou Yang /*-------------------------------------------------------------*/ 446cadaa04SWenyou Yang 456cadaa04SWenyou Yang struct generic_clk_priv { 469e5935c0SWenyou Yang u32 num_parents; 479e5935c0SWenyou Yang }; 489e5935c0SWenyou Yang 496cadaa04SWenyou Yang static ulong generic_clk_get_rate(struct clk *clk) 509e5935c0SWenyou Yang { 519e5935c0SWenyou Yang struct pmc_platdata *plat = dev_get_platdata(clk->dev); 529e5935c0SWenyou Yang struct at91_pmc *pmc = plat->reg_base; 539e5935c0SWenyou Yang struct clk parent; 546cadaa04SWenyou Yang ulong clk_rate; 559e5935c0SWenyou Yang u32 tmp, gckdiv; 569e5935c0SWenyou Yang u8 parent_id; 579e5935c0SWenyou Yang int ret; 589e5935c0SWenyou Yang 599e5935c0SWenyou Yang writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); 609e5935c0SWenyou Yang tmp = readl(&pmc->pcr); 619e5935c0SWenyou Yang parent_id = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) & 629e5935c0SWenyou Yang AT91_PMC_PCR_GCKCSS_MASK; 639e5935c0SWenyou Yang gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; 649e5935c0SWenyou Yang 656cadaa04SWenyou Yang ret = clk_get_by_index(dev_get_parent(clk->dev), parent_id, &parent); 669e5935c0SWenyou Yang if (ret) 679e5935c0SWenyou Yang return 0; 689e5935c0SWenyou Yang 696cadaa04SWenyou Yang clk_rate = clk_get_rate(&parent) / (gckdiv + 1); 706cadaa04SWenyou Yang 716cadaa04SWenyou Yang clk_free(&parent); 726cadaa04SWenyou Yang 736cadaa04SWenyou Yang return clk_rate; 749e5935c0SWenyou Yang } 759e5935c0SWenyou Yang 766cadaa04SWenyou Yang static ulong generic_clk_set_rate(struct clk *clk, ulong rate) 779e5935c0SWenyou Yang { 789e5935c0SWenyou Yang struct pmc_platdata *plat = dev_get_platdata(clk->dev); 799e5935c0SWenyou Yang struct at91_pmc *pmc = plat->reg_base; 806cadaa04SWenyou Yang struct generic_clk_priv *priv = dev_get_priv(clk->dev); 819e5935c0SWenyou Yang struct clk parent, best_parent; 829e5935c0SWenyou Yang ulong tmp_rate, best_rate = rate, parent_rate; 839e5935c0SWenyou Yang int tmp_diff, best_diff = -1; 849e5935c0SWenyou Yang u32 div, best_div = 0; 859e5935c0SWenyou Yang u8 best_parent_id = 0; 869e5935c0SWenyou Yang u8 i; 879e5935c0SWenyou Yang u32 tmp; 889e5935c0SWenyou Yang int ret; 899e5935c0SWenyou Yang 909e5935c0SWenyou Yang for (i = 0; i < priv->num_parents; i++) { 916cadaa04SWenyou Yang ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent); 929e5935c0SWenyou Yang if (ret) 939e5935c0SWenyou Yang return ret; 949e5935c0SWenyou Yang 959e5935c0SWenyou Yang parent_rate = clk_get_rate(&parent); 969e5935c0SWenyou Yang if (IS_ERR_VALUE(parent_rate)) 979e5935c0SWenyou Yang return parent_rate; 989e5935c0SWenyou Yang 999e5935c0SWenyou Yang for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { 1009e5935c0SWenyou Yang tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); 1019e5935c0SWenyou Yang if (rate < tmp_rate) 1029e5935c0SWenyou Yang continue; 1039e5935c0SWenyou Yang tmp_diff = rate - tmp_rate; 1049e5935c0SWenyou Yang 1059e5935c0SWenyou Yang if (best_diff < 0 || best_diff > tmp_diff) { 1069e5935c0SWenyou Yang best_rate = tmp_rate; 1079e5935c0SWenyou Yang best_diff = tmp_diff; 1089e5935c0SWenyou Yang 1099e5935c0SWenyou Yang best_div = div - 1; 1109e5935c0SWenyou Yang best_parent = parent; 1119e5935c0SWenyou Yang best_parent_id = i; 1129e5935c0SWenyou Yang } 1139e5935c0SWenyou Yang 1149e5935c0SWenyou Yang if (!best_diff || tmp_rate < rate) 1159e5935c0SWenyou Yang break; 1169e5935c0SWenyou Yang } 1179e5935c0SWenyou Yang 1189e5935c0SWenyou Yang if (!best_diff) 1199e5935c0SWenyou Yang break; 1209e5935c0SWenyou Yang } 1219e5935c0SWenyou Yang 1229e5935c0SWenyou Yang debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n", 1239e5935c0SWenyou Yang best_parent.dev->name, best_rate, best_div); 1249e5935c0SWenyou Yang 1259e5935c0SWenyou Yang ret = clk_enable(&best_parent); 1269e5935c0SWenyou Yang if (ret) 1279e5935c0SWenyou Yang return ret; 1289e5935c0SWenyou Yang 1299e5935c0SWenyou Yang writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); 1309e5935c0SWenyou Yang tmp = readl(&pmc->pcr); 1319e5935c0SWenyou Yang tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS); 1329e5935c0SWenyou Yang tmp |= AT91_PMC_PCR_GCKCSS_(best_parent_id) | 1339e5935c0SWenyou Yang AT91_PMC_PCR_CMD_WRITE | 1349e5935c0SWenyou Yang AT91_PMC_PCR_GCKDIV_(best_div) | 1359e5935c0SWenyou Yang AT91_PMC_PCR_GCKEN; 1369e5935c0SWenyou Yang writel(tmp, &pmc->pcr); 1379e5935c0SWenyou Yang 1389e5935c0SWenyou Yang while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY)) 1399e5935c0SWenyou Yang ; 1409e5935c0SWenyou Yang 1419e5935c0SWenyou Yang return 0; 1429e5935c0SWenyou Yang } 1439e5935c0SWenyou Yang 1446cadaa04SWenyou Yang static struct clk_ops generic_clk_ops = { 1456cadaa04SWenyou Yang .of_xlate = at91_clk_of_xlate, 1466cadaa04SWenyou Yang .get_rate = generic_clk_get_rate, 1476cadaa04SWenyou Yang .set_rate = generic_clk_set_rate, 1489e5935c0SWenyou Yang }; 1499e5935c0SWenyou Yang 1506cadaa04SWenyou Yang static int generic_clk_ofdata_to_platdata(struct udevice *dev) 1519e5935c0SWenyou Yang { 1526cadaa04SWenyou Yang struct generic_clk_priv *priv = dev_get_priv(dev); 1539e5935c0SWenyou Yang u32 cells[GENERATED_SOURCE_MAX]; 1549e5935c0SWenyou Yang u32 num_parents; 1559e5935c0SWenyou Yang 1566cadaa04SWenyou Yang num_parents = fdtdec_get_int_array_count(gd->fdt_blob, 157*e160f7d4SSimon Glass dev_of_offset(dev_get_parent(dev)), "clocks", cells, 1589e5935c0SWenyou Yang GENERATED_SOURCE_MAX); 1599e5935c0SWenyou Yang 1609e5935c0SWenyou Yang if (!num_parents) 1619e5935c0SWenyou Yang return -1; 1629e5935c0SWenyou Yang 1639e5935c0SWenyou Yang priv->num_parents = num_parents; 1649e5935c0SWenyou Yang 1659e5935c0SWenyou Yang return 0; 1669e5935c0SWenyou Yang } 1679e5935c0SWenyou Yang 1686cadaa04SWenyou Yang U_BOOT_DRIVER(generic_clk) = { 1696cadaa04SWenyou Yang .name = "generic-clk", 1709e5935c0SWenyou Yang .id = UCLASS_CLK, 1716cadaa04SWenyou Yang .probe = at91_clk_probe, 1726cadaa04SWenyou Yang .ofdata_to_platdata = generic_clk_ofdata_to_platdata, 1736cadaa04SWenyou Yang .priv_auto_alloc_size = sizeof(struct generic_clk_priv), 1749e5935c0SWenyou Yang .platdata_auto_alloc_size = sizeof(struct pmc_platdata), 1756cadaa04SWenyou Yang .ops = &generic_clk_ops, 1769e5935c0SWenyou Yang }; 177