^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * linux/arch/arm/common/icst307.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Support functions for calculating clocks/divisors for the ICST307
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * clock generators. See https://www.idt.com/ for more information
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * on these devices.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * This is an almost identical implementation to the ICST525 clock generator.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * The s2div and idx2s files are different
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <asm/div64.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include "icst.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * Divisors for each OD setting.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) const unsigned char icst307_s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) const unsigned char icst525_s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) EXPORT_SYMBOL(icst307_s2div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) EXPORT_SYMBOL(icst525_s2div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) u64 dividend = p->ref * 2 * (u64)(vco.v + 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) u32 divisor = (vco.r + 2) * p->s2div[vco.s];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) do_div(dividend, divisor);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) return (unsigned long)dividend;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) EXPORT_SYMBOL(icst_hz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * Ascending divisor S values.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) const unsigned char icst307_idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) const unsigned char icst525_idx2s[8] = { 1, 3, 4, 7, 5, 2, 6, 0 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) EXPORT_SYMBOL(icst307_idx2s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) EXPORT_SYMBOL(icst525_idx2s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) struct icst_vco
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) icst_hz_to_vco(const struct icst_params *p, unsigned long freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) struct icst_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) unsigned long f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) unsigned int i = 0, rd, best = (unsigned int)-1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * First, find the PLL output divisor such
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) * that the PLL output is within spec.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) f = freq * p->s2div[p->idx2s[i]];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) if (f > p->vco_min && f <= p->vco_max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) } while (i < 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) if (i >= 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) return vco;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) vco.s = p->idx2s[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * Now find the closest divisor combination
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * which gives a PLL output of 'f'.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) for (rd = p->rd_min; rd <= p->rd_max; rd++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) unsigned long fref_div, f_pll;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) unsigned int vd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) int f_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) fref_div = (2 * p->ref) / rd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) vd = (f + fref_div / 2) / fref_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (vd < p->vd_min || vd > p->vd_max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) f_pll = fref_div * vd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) f_diff = f_pll - f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (f_diff < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) f_diff = -f_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if ((unsigned)f_diff < best) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) vco.v = vd - 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) vco.r = rd - 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) if (f_diff == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) best = f_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return vco;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) EXPORT_SYMBOL(icst_hz_to_vco);