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 20*6cadaa04SWenyou Yang /** 21*6cadaa04SWenyou Yang * generated_clk_bind() - for the generated clock driver 22*6cadaa04SWenyou Yang * Recursively bind its children as clk devices. 23*6cadaa04SWenyou Yang * 24*6cadaa04SWenyou Yang * @return: 0 on success, or negative error code on failure 25*6cadaa04SWenyou Yang */ 26*6cadaa04SWenyou Yang static int generated_clk_bind(struct udevice *dev) 27*6cadaa04SWenyou Yang { 28*6cadaa04SWenyou Yang return at91_clk_sub_device_bind(dev, "generic-clk"); 29*6cadaa04SWenyou Yang } 30*6cadaa04SWenyou Yang 31*6cadaa04SWenyou Yang static const struct udevice_id generated_clk_match[] = { 32*6cadaa04SWenyou Yang { .compatible = "atmel,sama5d2-clk-generated" }, 33*6cadaa04SWenyou Yang {} 34*6cadaa04SWenyou Yang }; 35*6cadaa04SWenyou Yang 36*6cadaa04SWenyou Yang U_BOOT_DRIVER(generated_clk) = { 37*6cadaa04SWenyou Yang .name = "generated-clk", 38*6cadaa04SWenyou Yang .id = UCLASS_MISC, 39*6cadaa04SWenyou Yang .of_match = generated_clk_match, 40*6cadaa04SWenyou Yang .bind = generated_clk_bind, 41*6cadaa04SWenyou Yang }; 42*6cadaa04SWenyou Yang 43*6cadaa04SWenyou Yang /*-------------------------------------------------------------*/ 44*6cadaa04SWenyou Yang 45*6cadaa04SWenyou Yang struct generic_clk_priv { 469e5935c0SWenyou Yang u32 num_parents; 479e5935c0SWenyou Yang }; 489e5935c0SWenyou Yang 49*6cadaa04SWenyou 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; 54*6cadaa04SWenyou 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 65*6cadaa04SWenyou Yang ret = clk_get_by_index(dev_get_parent(clk->dev), parent_id, &parent); 669e5935c0SWenyou Yang if (ret) 679e5935c0SWenyou Yang return 0; 689e5935c0SWenyou Yang 69*6cadaa04SWenyou Yang clk_rate = clk_get_rate(&parent) / (gckdiv + 1); 70*6cadaa04SWenyou Yang 71*6cadaa04SWenyou Yang clk_free(&parent); 72*6cadaa04SWenyou Yang 73*6cadaa04SWenyou Yang return clk_rate; 749e5935c0SWenyou Yang } 759e5935c0SWenyou Yang 76*6cadaa04SWenyou 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; 80*6cadaa04SWenyou 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++) { 91*6cadaa04SWenyou 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 144*6cadaa04SWenyou Yang static struct clk_ops generic_clk_ops = { 145*6cadaa04SWenyou Yang .of_xlate = at91_clk_of_xlate, 146*6cadaa04SWenyou Yang .get_rate = generic_clk_get_rate, 147*6cadaa04SWenyou Yang .set_rate = generic_clk_set_rate, 1489e5935c0SWenyou Yang }; 1499e5935c0SWenyou Yang 150*6cadaa04SWenyou Yang static int generic_clk_ofdata_to_platdata(struct udevice *dev) 1519e5935c0SWenyou Yang { 152*6cadaa04SWenyou Yang struct generic_clk_priv *priv = dev_get_priv(dev); 1539e5935c0SWenyou Yang u32 cells[GENERATED_SOURCE_MAX]; 1549e5935c0SWenyou Yang u32 num_parents; 1559e5935c0SWenyou Yang 156*6cadaa04SWenyou Yang num_parents = fdtdec_get_int_array_count(gd->fdt_blob, 157*6cadaa04SWenyou Yang dev_get_parent(dev)->of_offset, 1589e5935c0SWenyou Yang "clocks", cells, 1599e5935c0SWenyou Yang GENERATED_SOURCE_MAX); 1609e5935c0SWenyou Yang 1619e5935c0SWenyou Yang if (!num_parents) 1629e5935c0SWenyou Yang return -1; 1639e5935c0SWenyou Yang 1649e5935c0SWenyou Yang priv->num_parents = num_parents; 1659e5935c0SWenyou Yang 1669e5935c0SWenyou Yang return 0; 1679e5935c0SWenyou Yang } 1689e5935c0SWenyou Yang 169*6cadaa04SWenyou Yang U_BOOT_DRIVER(generic_clk) = { 170*6cadaa04SWenyou Yang .name = "generic-clk", 1719e5935c0SWenyou Yang .id = UCLASS_CLK, 172*6cadaa04SWenyou Yang .probe = at91_clk_probe, 173*6cadaa04SWenyou Yang .ofdata_to_platdata = generic_clk_ofdata_to_platdata, 174*6cadaa04SWenyou Yang .priv_auto_alloc_size = sizeof(struct generic_clk_priv), 1759e5935c0SWenyou Yang .platdata_auto_alloc_size = sizeof(struct pmc_platdata), 176*6cadaa04SWenyou Yang .ops = &generic_clk_ops, 1779e5935c0SWenyou Yang }; 178