1/* 2 * linux/arch/arm/common/icst525.c 3 * 4 * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Support functions for calculating clocks/divisors for the ICST525 11 * clock generators. See http://www.icst.com/ for more information 12 * on these devices. 13 */ 14#include <linux/module.h> 15#include <linux/kernel.h> 16 17#include <asm/hardware/icst525.h> 18 19/* 20 * Divisors for each OD setting. 21 */ 22static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; 23 24unsigned long icst525_khz(const struct icst525_params *p, struct icst525_vco vco) 25{ 26 return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); 27} 28 29EXPORT_SYMBOL(icst525_khz); 30 31/* 32 * Ascending divisor S values. 33 */ 34static unsigned char idx2s[] = { 1, 3, 4, 7, 5, 2, 6, 0 }; 35 36struct icst525_vco 37icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq) 38{ 39 struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; 40 unsigned long f; 41 unsigned int i = 0, rd, best = (unsigned int)-1; 42 43 /* 44 * First, find the PLL output divisor such 45 * that the PLL output is within spec. 46 */ 47 do { 48 f = freq * s2div[idx2s[i]]; 49 50 /* 51 * f must be between 10MHz and 52 * 320MHz (5V) or 200MHz (3V) 53 */ 54 if (f > 10000 && f <= p->vco_max) 55 break; 56 } while (i < ARRAY_SIZE(idx2s)); 57 58 if (i >= ARRAY_SIZE(idx2s)) 59 return vco; 60 61 vco.s = idx2s[i]; 62 63 /* 64 * Now find the closest divisor combination 65 * which gives a PLL output of 'f'. 66 */ 67 for (rd = p->rd_min; rd <= p->rd_max; rd++) { 68 unsigned long fref_div, f_pll; 69 unsigned int vd; 70 int f_diff; 71 72 fref_div = (2 * p->ref) / rd; 73 74 vd = (f + fref_div / 2) / fref_div; 75 if (vd < p->vd_min || vd > p->vd_max) 76 continue; 77 78 f_pll = fref_div * vd; 79 f_diff = f_pll - f; 80 if (f_diff < 0) 81 f_diff = -f_diff; 82 83 if ((unsigned)f_diff < best) { 84 vco.v = vd - 8; 85 vco.r = rd - 2; 86 if (f_diff == 0) 87 break; 88 best = f_diff; 89 } 90 } 91 92 return vco; 93} 94 95EXPORT_SYMBOL(icst525_khz_to_vco); 96 97struct icst525_vco 98icst525_ps_to_vco(const struct icst525_params *p, unsigned long period) 99{ 100 struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; 101 unsigned long f, ps; 102 unsigned int i = 0, rd, best = (unsigned int)-1; 103 104 ps = 1000000000UL / p->vco_max; 105 106 /* 107 * First, find the PLL output divisor such 108 * that the PLL output is within spec. 109 */ 110 do { 111 f = period / s2div[idx2s[i]]; 112 113 /* 114 * f must be between 10MHz and 115 * 320MHz (5V) or 200MHz (3V) 116 */ 117 if (f >= ps && f < 100000) 118 break; 119 } while (i < ARRAY_SIZE(idx2s)); 120 121 if (i >= ARRAY_SIZE(idx2s)) 122 return vco; 123 124 vco.s = idx2s[i]; 125 126 ps = 500000000UL / p->ref; 127 128 /* 129 * Now find the closest divisor combination 130 * which gives a PLL output of 'f'. 131 */ 132 for (rd = p->rd_min; rd <= p->rd_max; rd++) { 133 unsigned long f_in_div, f_pll; 134 unsigned int vd; 135 int f_diff; 136 137 f_in_div = ps * rd; 138 139 vd = (f_in_div + f / 2) / f; 140 if (vd < p->vd_min || vd > p->vd_max) 141 continue; 142 143 f_pll = (f_in_div + vd / 2) / vd; 144 f_diff = f_pll - f; 145 if (f_diff < 0) 146 f_diff = -f_diff; 147 148 if ((unsigned)f_diff < best) { 149 vco.v = vd - 8; 150 vco.r = rd - 2; 151 if (f_diff == 0) 152 break; 153 best = f_diff; 154 } 155 } 156 157 return vco; 158} 159 160EXPORT_SYMBOL(icst525_ps_to_vco); 161