1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2017 4 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc 5 * 6 * based on the gdsys osd driver, which is 7 * 8 * (C) Copyright 2010 9 * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de 10 */ 11 12 #include <common.h> 13 #include <dm.h> 14 #include <clk-uclass.h> 15 #include <i2c.h> 16 17 const long long ICS8N3QV01_FREF = 114285000; 18 const long long ICS8N3QV01_FREF_LL = 114285000LL; 19 const long long ICS8N3QV01_F_DEFAULT_0 = 156250000LL; 20 const long long ICS8N3QV01_F_DEFAULT_1 = 125000000LL; 21 const long long ICS8N3QV01_F_DEFAULT_2 = 100000000LL; 22 const long long ICS8N3QV01_F_DEFAULT_3 = 25175000LL; 23 24 const uint MAX_FREQ_INDEX = 3; 25 26 struct ics8n3qv01_priv { 27 ulong rate; 28 }; 29 30 static int ics8n3qv01_get_fout_calc(struct udevice *dev, uint index, 31 uint *fout_calc) 32 { 33 u64 n, mint, mfrac; 34 u8 reg_a, reg_b, reg_c, reg_d, reg_f; 35 int val[6]; 36 int i; 37 38 if (index > MAX_FREQ_INDEX) 39 return -EINVAL; 40 41 for (i = 0; i <= 5; ++i) { 42 u8 tmp = dm_i2c_reg_read(dev, 4 * i + index); 43 44 if (tmp < 0) { 45 debug("%s: Error while reading i2c register %d.\n", 46 dev->name, 4 * i + index); 47 return tmp; 48 } 49 50 val[i] = tmp; 51 } 52 53 reg_a = val[0]; /* Register 0 + index */ 54 reg_b = val[1]; /* Register 4 + index */ 55 reg_c = val[2]; /* Register 8 + index */ 56 reg_d = val[3]; /* Register 12 + index */ 57 reg_f = val[5]; /* Register 20 + index */ 58 59 mint = ((reg_a >> 1) & 0x1f) | /* MINTi[4-0]*/ 60 (reg_f & 0x20); /* MINTi[5] */ 61 mfrac = ((reg_a & 0x01) << 17) | /* MFRACi[17] */ 62 (reg_b << 9) | /* MFRACi[16-9] */ 63 (reg_c << 1) | /* MFRACi[8-1] */ 64 (reg_d >> 7); /* MFRACi[0] */ 65 n = reg_d & 0x7f; /* Ni[6-0] */ 66 67 *fout_calc = (mint * ICS8N3QV01_FREF_LL 68 + mfrac * ICS8N3QV01_FREF_LL / 262144LL 69 + ICS8N3QV01_FREF_LL / 524288LL 70 + n / 2) 71 / n 72 * 1000000 73 / (1000000 - 100); 74 75 return 0; 76 } 77 78 static int ics8n3qv01_calc_parameters(uint fout, uint *_mint, uint *_mfrac, 79 uint *_n) 80 { 81 uint n, foutiic, fvcoiic, mint; 82 u64 mfrac; 83 84 n = (2215000000U + fout / 2) / fout; 85 if (fout < 417000000U) 86 n = 2 * ((2215000000U / 2 + fout / 2) / fout); 87 else 88 n = (2215000000U + fout / 2) / fout; 89 90 if ((n & 1) && n > 5) 91 n -= 1; 92 93 foutiic = fout - (fout / 10000); 94 fvcoiic = foutiic * n; 95 96 mint = fvcoiic / 114285000; 97 if (mint < 17 || mint > 63) 98 return -EINVAL; 99 100 mfrac = ((u64)fvcoiic % 114285000LL) * 262144LL 101 / 114285000LL; 102 103 *_mint = mint; 104 *_mfrac = mfrac; 105 *_n = n; 106 107 return 0; 108 } 109 110 static ulong ics8n3qv01_set_rate(struct clk *clk, ulong fout) 111 { 112 struct ics8n3qv01_priv *priv = dev_get_priv(clk->dev); 113 uint n, mint, mfrac; 114 uint fout_calc = 0; 115 u64 fout_prog; 116 long long off_ppm; 117 int res, i; 118 u8 reg[6]; 119 int tmp; 120 int addr[] = {0, 4, 8, 12, 18, 20}; 121 122 priv->rate = fout; 123 124 res = ics8n3qv01_get_fout_calc(clk->dev, 1, &fout_calc); 125 126 if (res) { 127 debug("%s: Error during output frequency calculation.\n", 128 clk->dev->name); 129 return res; 130 } 131 132 off_ppm = (fout_calc - ICS8N3QV01_F_DEFAULT_1) * 1000000 133 / ICS8N3QV01_F_DEFAULT_1; 134 printf("%s: PLL is off by %lld ppm\n", clk->dev->name, off_ppm); 135 fout_prog = (u64)fout * (u64)fout_calc 136 / ICS8N3QV01_F_DEFAULT_1; 137 res = ics8n3qv01_calc_parameters(fout_prog, &mint, &mfrac, &n); 138 139 if (res) { 140 debug("%s: Cannot determine mint parameter.\n", 141 clk->dev->name); 142 return res; 143 } 144 145 /* Register 0 */ 146 tmp = dm_i2c_reg_read(clk->dev, 0) & 0xc0; 147 if (tmp < 0) 148 return tmp; 149 reg[0] = tmp | (mint & 0x1f) << 1; 150 reg[0] |= (mfrac >> 17) & 0x01; 151 152 /* Register 4 */ 153 reg[1] = mfrac >> 9; 154 155 /* Register 8 */ 156 reg[2] = mfrac >> 1; 157 158 /* Register 12 */ 159 reg[3] = mfrac << 7; 160 reg[3] |= n & 0x7f; 161 162 /* Register 18 */ 163 tmp = dm_i2c_reg_read(clk->dev, 18) & 0x03; 164 if (tmp < 0) 165 return tmp; 166 reg[4] = tmp | 0x20; 167 168 /* Register 20 */ 169 tmp = dm_i2c_reg_read(clk->dev, 20) & 0x1f; 170 if (tmp < 0) 171 return tmp; 172 reg[5] = tmp | (mint & (1 << 5)); 173 174 for (i = 0; i <= 5; ++i) { 175 res = dm_i2c_reg_write(clk->dev, addr[i], reg[i]); 176 if (res < 0) 177 return res; 178 } 179 180 return 0; 181 } 182 183 static int ics8n3qv01_request(struct clk *clock) 184 { 185 return 0; 186 } 187 188 static ulong ics8n3qv01_get_rate(struct clk *clk) 189 { 190 struct ics8n3qv01_priv *priv = dev_get_priv(clk->dev); 191 192 return priv->rate; 193 } 194 195 static int ics8n3qv01_enable(struct clk *clk) 196 { 197 return 0; 198 } 199 200 static int ics8n3qv01_disable(struct clk *clk) 201 { 202 return 0; 203 } 204 205 static const struct clk_ops ics8n3qv01_ops = { 206 .request = ics8n3qv01_request, 207 .get_rate = ics8n3qv01_get_rate, 208 .set_rate = ics8n3qv01_set_rate, 209 .enable = ics8n3qv01_enable, 210 .disable = ics8n3qv01_disable, 211 }; 212 213 static const struct udevice_id ics8n3qv01_ids[] = { 214 { .compatible = "idt,ics8n3qv01" }, 215 { /* sentinel */ } 216 }; 217 218 int ics8n3qv01_probe(struct udevice *dev) 219 { 220 return 0; 221 } 222 223 U_BOOT_DRIVER(ics8n3qv01) = { 224 .name = "ics8n3qv01", 225 .id = UCLASS_CLK, 226 .ops = &ics8n3qv01_ops, 227 .of_match = ics8n3qv01_ids, 228 .probe = ics8n3qv01_probe, 229 .priv_auto_alloc_size = sizeof(struct ics8n3qv01_priv), 230 }; 231