1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2016 Atmel Corporation 4 * Wenyou.Yang <wenyou.yang@atmel.com> 5 */ 6 7 #include <common.h> 8 #include <clk-uclass.h> 9 #include <dm.h> 10 #include <linux/io.h> 11 #include <mach/at91_pmc.h> 12 #include "pmc.h" 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 #define GENERATED_SOURCE_MAX 6 17 #define GENERATED_MAX_DIV 255 18 19 /** 20 * generated_clk_bind() - for the generated clock driver 21 * Recursively bind its children as clk devices. 22 * 23 * @return: 0 on success, or negative error code on failure 24 */ 25 static int generated_clk_bind(struct udevice *dev) 26 { 27 return at91_clk_sub_device_bind(dev, "generic-clk"); 28 } 29 30 static const struct udevice_id generated_clk_match[] = { 31 { .compatible = "atmel,sama5d2-clk-generated" }, 32 {} 33 }; 34 35 U_BOOT_DRIVER(generated_clk) = { 36 .name = "generated-clk", 37 .id = UCLASS_MISC, 38 .of_match = generated_clk_match, 39 .bind = generated_clk_bind, 40 }; 41 42 /*-------------------------------------------------------------*/ 43 44 struct generic_clk_priv { 45 u32 num_parents; 46 }; 47 48 static ulong generic_clk_get_rate(struct clk *clk) 49 { 50 struct pmc_platdata *plat = dev_get_platdata(clk->dev); 51 struct at91_pmc *pmc = plat->reg_base; 52 struct clk parent; 53 ulong clk_rate; 54 u32 tmp, gckdiv; 55 u8 clock_source, parent_index; 56 int ret; 57 58 writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); 59 tmp = readl(&pmc->pcr); 60 clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) & 61 AT91_PMC_PCR_GCKCSS_MASK; 62 gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; 63 64 parent_index = clock_source - 1; 65 ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent); 66 if (ret) 67 return 0; 68 69 clk_rate = clk_get_rate(&parent) / (gckdiv + 1); 70 71 clk_free(&parent); 72 73 return clk_rate; 74 } 75 76 static ulong generic_clk_set_rate(struct clk *clk, ulong rate) 77 { 78 struct pmc_platdata *plat = dev_get_platdata(clk->dev); 79 struct at91_pmc *pmc = plat->reg_base; 80 struct generic_clk_priv *priv = dev_get_priv(clk->dev); 81 struct clk parent, best_parent; 82 ulong tmp_rate, best_rate = rate, parent_rate; 83 int tmp_diff, best_diff = -1; 84 u32 div, best_div = 0; 85 u8 best_parent_index, best_clock_source = 0; 86 u8 i; 87 u32 tmp; 88 int ret; 89 90 for (i = 0; i < priv->num_parents; i++) { 91 ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent); 92 if (ret) 93 return ret; 94 95 parent_rate = clk_get_rate(&parent); 96 if (IS_ERR_VALUE(parent_rate)) 97 return parent_rate; 98 99 for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { 100 tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); 101 tmp_diff = abs(rate - tmp_rate); 102 103 if (best_diff < 0 || best_diff > tmp_diff) { 104 best_rate = tmp_rate; 105 best_diff = tmp_diff; 106 107 best_div = div - 1; 108 best_parent = parent; 109 best_parent_index = i; 110 best_clock_source = best_parent_index + 1; 111 } 112 113 if (!best_diff || tmp_rate < rate) 114 break; 115 } 116 117 if (!best_diff) 118 break; 119 } 120 121 debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n", 122 best_parent.dev->name, best_rate, best_div); 123 124 ret = clk_enable(&best_parent); 125 if (ret) 126 return ret; 127 128 writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr); 129 tmp = readl(&pmc->pcr); 130 tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS); 131 tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) | 132 AT91_PMC_PCR_CMD_WRITE | 133 AT91_PMC_PCR_GCKDIV_(best_div) | 134 AT91_PMC_PCR_GCKEN; 135 writel(tmp, &pmc->pcr); 136 137 while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY)) 138 ; 139 140 return 0; 141 } 142 143 static struct clk_ops generic_clk_ops = { 144 .of_xlate = at91_clk_of_xlate, 145 .get_rate = generic_clk_get_rate, 146 .set_rate = generic_clk_set_rate, 147 }; 148 149 static int generic_clk_ofdata_to_platdata(struct udevice *dev) 150 { 151 struct generic_clk_priv *priv = dev_get_priv(dev); 152 u32 cells[GENERATED_SOURCE_MAX]; 153 u32 num_parents; 154 155 num_parents = fdtdec_get_int_array_count(gd->fdt_blob, 156 dev_of_offset(dev_get_parent(dev)), "clocks", cells, 157 GENERATED_SOURCE_MAX); 158 159 if (!num_parents) 160 return -1; 161 162 priv->num_parents = num_parents; 163 164 return 0; 165 } 166 167 U_BOOT_DRIVER(generic_clk) = { 168 .name = "generic-clk", 169 .id = UCLASS_CLK, 170 .probe = at91_clk_probe, 171 .ofdata_to_platdata = generic_clk_ofdata_to_platdata, 172 .priv_auto_alloc_size = sizeof(struct generic_clk_priv), 173 .platdata_auto_alloc_size = sizeof(struct pmc_platdata), 174 .ops = &generic_clk_ops, 175 }; 176