root/drivers/clk/clk-axi-clkgen.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. axi_clkgen_lookup_filter
  2. axi_clkgen_lookup_lock
  3. axi_clkgen_calc_params
  4. axi_clkgen_calc_clk_params
  5. axi_clkgen_write
  6. axi_clkgen_read
  7. axi_clkgen_wait_non_busy
  8. axi_clkgen_mmcm_read
  9. axi_clkgen_mmcm_write
  10. axi_clkgen_mmcm_enable
  11. clk_hw_to_axi_clkgen
  12. axi_clkgen_set_rate
  13. axi_clkgen_round_rate
  14. axi_clkgen_recalc_rate
  15. axi_clkgen_enable
  16. axi_clkgen_disable
  17. axi_clkgen_set_parent
  18. axi_clkgen_get_parent
  19. axi_clkgen_probe
  20. axi_clkgen_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * AXI clkgen driver
   4  *
   5  * Copyright 2012-2013 Analog Devices Inc.
   6  *  Author: Lars-Peter Clausen <lars@metafoo.de>
   7  */
   8 
   9 #include <linux/platform_device.h>
  10 #include <linux/clk-provider.h>
  11 #include <linux/slab.h>
  12 #include <linux/io.h>
  13 #include <linux/of.h>
  14 #include <linux/module.h>
  15 #include <linux/err.h>
  16 
  17 #define AXI_CLKGEN_V2_REG_RESET         0x40
  18 #define AXI_CLKGEN_V2_REG_CLKSEL        0x44
  19 #define AXI_CLKGEN_V2_REG_DRP_CNTRL     0x70
  20 #define AXI_CLKGEN_V2_REG_DRP_STATUS    0x74
  21 
  22 #define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
  23 #define AXI_CLKGEN_V2_RESET_ENABLE      BIT(0)
  24 
  25 #define AXI_CLKGEN_V2_DRP_CNTRL_SEL     BIT(29)
  26 #define AXI_CLKGEN_V2_DRP_CNTRL_READ    BIT(28)
  27 
  28 #define AXI_CLKGEN_V2_DRP_STATUS_BUSY   BIT(16)
  29 
  30 #define MMCM_REG_CLKOUT0_1      0x08
  31 #define MMCM_REG_CLKOUT0_2      0x09
  32 #define MMCM_REG_CLK_FB1        0x14
  33 #define MMCM_REG_CLK_FB2        0x15
  34 #define MMCM_REG_CLK_DIV        0x16
  35 #define MMCM_REG_LOCK1          0x18
  36 #define MMCM_REG_LOCK2          0x19
  37 #define MMCM_REG_LOCK3          0x1a
  38 #define MMCM_REG_FILTER1        0x4e
  39 #define MMCM_REG_FILTER2        0x4f
  40 
  41 #define MMCM_CLKOUT_NOCOUNT     BIT(6)
  42 
  43 #define MMCM_CLK_DIV_NOCOUNT    BIT(12)
  44 
  45 struct axi_clkgen {
  46         void __iomem *base;
  47         struct clk_hw clk_hw;
  48 };
  49 
  50 static uint32_t axi_clkgen_lookup_filter(unsigned int m)
  51 {
  52         switch (m) {
  53         case 0:
  54                 return 0x01001990;
  55         case 1:
  56                 return 0x01001190;
  57         case 2:
  58                 return 0x01009890;
  59         case 3:
  60                 return 0x01001890;
  61         case 4:
  62                 return 0x01008890;
  63         case 5 ... 8:
  64                 return 0x01009090;
  65         case 9 ... 11:
  66                 return 0x01000890;
  67         case 12:
  68                 return 0x08009090;
  69         case 13 ... 22:
  70                 return 0x01001090;
  71         case 23 ... 36:
  72                 return 0x01008090;
  73         case 37 ... 46:
  74                 return 0x08001090;
  75         default:
  76                 return 0x08008090;
  77         }
  78 }
  79 
  80 static const uint32_t axi_clkgen_lock_table[] = {
  81         0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
  82         0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
  83         0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
  84         0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
  85         0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
  86         0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
  87         0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
  88         0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
  89         0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
  90 };
  91 
  92 static uint32_t axi_clkgen_lookup_lock(unsigned int m)
  93 {
  94         if (m < ARRAY_SIZE(axi_clkgen_lock_table))
  95                 return axi_clkgen_lock_table[m];
  96         return 0x1f1f00fa;
  97 }
  98 
  99 static const unsigned int fpfd_min = 10000;
 100 static const unsigned int fpfd_max = 300000;
 101 static const unsigned int fvco_min = 600000;
 102 static const unsigned int fvco_max = 1200000;
 103 
 104 static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
 105         unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
 106 {
 107         unsigned long d, d_min, d_max, _d_min, _d_max;
 108         unsigned long m, m_min, m_max;
 109         unsigned long f, dout, best_f, fvco;
 110 
 111         fin /= 1000;
 112         fout /= 1000;
 113 
 114         best_f = ULONG_MAX;
 115         *best_d = 0;
 116         *best_m = 0;
 117         *best_dout = 0;
 118 
 119         d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
 120         d_max = min_t(unsigned long, fin / fpfd_min, 80);
 121 
 122         m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
 123         m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
 124 
 125         for (m = m_min; m <= m_max; m++) {
 126                 _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
 127                 _d_max = min(d_max, fin * m / fvco_min);
 128 
 129                 for (d = _d_min; d <= _d_max; d++) {
 130                         fvco = fin * m / d;
 131 
 132                         dout = DIV_ROUND_CLOSEST(fvco, fout);
 133                         dout = clamp_t(unsigned long, dout, 1, 128);
 134                         f = fvco / dout;
 135                         if (abs(f - fout) < abs(best_f - fout)) {
 136                                 best_f = f;
 137                                 *best_d = d;
 138                                 *best_m = m;
 139                                 *best_dout = dout;
 140                                 if (best_f == fout)
 141                                         return;
 142                         }
 143                 }
 144         }
 145 }
 146 
 147 static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
 148         unsigned int *high, unsigned int *edge, unsigned int *nocount)
 149 {
 150         if (divider == 1)
 151                 *nocount = 1;
 152         else
 153                 *nocount = 0;
 154 
 155         *high = divider / 2;
 156         *edge = divider % 2;
 157         *low = divider - *high;
 158 }
 159 
 160 static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
 161         unsigned int reg, unsigned int val)
 162 {
 163         writel(val, axi_clkgen->base + reg);
 164 }
 165 
 166 static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
 167         unsigned int reg, unsigned int *val)
 168 {
 169         *val = readl(axi_clkgen->base + reg);
 170 }
 171 
 172 static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
 173 {
 174         unsigned int timeout = 10000;
 175         unsigned int val;
 176 
 177         do {
 178                 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
 179         } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
 180 
 181         if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
 182                 return -EIO;
 183 
 184         return val & 0xffff;
 185 }
 186 
 187 static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
 188         unsigned int reg, unsigned int *val)
 189 {
 190         unsigned int reg_val;
 191         int ret;
 192 
 193         ret = axi_clkgen_wait_non_busy(axi_clkgen);
 194         if (ret < 0)
 195                 return ret;
 196 
 197         reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
 198         reg_val |= (reg << 16);
 199 
 200         axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
 201 
 202         ret = axi_clkgen_wait_non_busy(axi_clkgen);
 203         if (ret < 0)
 204                 return ret;
 205 
 206         *val = ret;
 207 
 208         return 0;
 209 }
 210 
 211 static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
 212         unsigned int reg, unsigned int val, unsigned int mask)
 213 {
 214         unsigned int reg_val = 0;
 215         int ret;
 216 
 217         ret = axi_clkgen_wait_non_busy(axi_clkgen);
 218         if (ret < 0)
 219                 return ret;
 220 
 221         if (mask != 0xffff) {
 222                 axi_clkgen_mmcm_read(axi_clkgen, reg, &reg_val);
 223                 reg_val &= ~mask;
 224         }
 225 
 226         reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
 227 
 228         axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
 229 
 230         return 0;
 231 }
 232 
 233 static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
 234         bool enable)
 235 {
 236         unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
 237 
 238         if (enable)
 239                 val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
 240 
 241         axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
 242 }
 243 
 244 static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
 245 {
 246         return container_of(clk_hw, struct axi_clkgen, clk_hw);
 247 }
 248 
 249 static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
 250         unsigned long rate, unsigned long parent_rate)
 251 {
 252         struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 253         unsigned int d, m, dout;
 254         unsigned int nocount;
 255         unsigned int high;
 256         unsigned int edge;
 257         unsigned int low;
 258         uint32_t filter;
 259         uint32_t lock;
 260 
 261         if (parent_rate == 0 || rate == 0)
 262                 return -EINVAL;
 263 
 264         axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
 265 
 266         if (d == 0 || dout == 0 || m == 0)
 267                 return -EINVAL;
 268 
 269         filter = axi_clkgen_lookup_filter(m - 1);
 270         lock = axi_clkgen_lookup_lock(m - 1);
 271 
 272         axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
 273         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
 274                 (high << 6) | low, 0xefff);
 275         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
 276                 (edge << 7) | (nocount << 6), 0x03ff);
 277 
 278         axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
 279         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
 280                 (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
 281 
 282         axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
 283         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
 284                 (high << 6) | low, 0xefff);
 285         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
 286                 (edge << 7) | (nocount << 6), 0x03ff);
 287 
 288         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
 289         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
 290                 (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
 291         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
 292                 (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
 293         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
 294         axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
 295 
 296         return 0;
 297 }
 298 
 299 static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
 300         unsigned long *parent_rate)
 301 {
 302         unsigned int d, m, dout;
 303         unsigned long long tmp;
 304 
 305         axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
 306 
 307         if (d == 0 || dout == 0 || m == 0)
 308                 return -EINVAL;
 309 
 310         tmp = (unsigned long long)*parent_rate * m;
 311         tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
 312 
 313         return min_t(unsigned long long, tmp, LONG_MAX);
 314 }
 315 
 316 static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
 317         unsigned long parent_rate)
 318 {
 319         struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 320         unsigned int d, m, dout;
 321         unsigned int reg;
 322         unsigned long long tmp;
 323 
 324         axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, &reg);
 325         if (reg & MMCM_CLKOUT_NOCOUNT) {
 326                 dout = 1;
 327         } else {
 328                 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
 329                 dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
 330         }
 331 
 332         axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
 333         if (reg & MMCM_CLK_DIV_NOCOUNT)
 334                 d = 1;
 335         else
 336                 d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
 337 
 338         axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, &reg);
 339         if (reg & MMCM_CLKOUT_NOCOUNT) {
 340                 m = 1;
 341         } else {
 342                 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
 343                 m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
 344         }
 345 
 346         if (d == 0 || dout == 0)
 347                 return 0;
 348 
 349         tmp = (unsigned long long)parent_rate * m;
 350         tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
 351 
 352         return min_t(unsigned long long, tmp, ULONG_MAX);
 353 }
 354 
 355 static int axi_clkgen_enable(struct clk_hw *clk_hw)
 356 {
 357         struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 358 
 359         axi_clkgen_mmcm_enable(axi_clkgen, true);
 360 
 361         return 0;
 362 }
 363 
 364 static void axi_clkgen_disable(struct clk_hw *clk_hw)
 365 {
 366         struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 367 
 368         axi_clkgen_mmcm_enable(axi_clkgen, false);
 369 }
 370 
 371 static int axi_clkgen_set_parent(struct clk_hw *clk_hw, u8 index)
 372 {
 373         struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 374 
 375         axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, index);
 376 
 377         return 0;
 378 }
 379 
 380 static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw)
 381 {
 382         struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
 383         unsigned int parent;
 384 
 385         axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, &parent);
 386 
 387         return parent;
 388 }
 389 
 390 static const struct clk_ops axi_clkgen_ops = {
 391         .recalc_rate = axi_clkgen_recalc_rate,
 392         .round_rate = axi_clkgen_round_rate,
 393         .set_rate = axi_clkgen_set_rate,
 394         .enable = axi_clkgen_enable,
 395         .disable = axi_clkgen_disable,
 396         .set_parent = axi_clkgen_set_parent,
 397         .get_parent = axi_clkgen_get_parent,
 398 };
 399 
 400 static const struct of_device_id axi_clkgen_ids[] = {
 401         {
 402                 .compatible = "adi,axi-clkgen-2.00.a",
 403         },
 404         { },
 405 };
 406 MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
 407 
 408 static int axi_clkgen_probe(struct platform_device *pdev)
 409 {
 410         const struct of_device_id *id;
 411         struct axi_clkgen *axi_clkgen;
 412         struct clk_init_data init;
 413         const char *parent_names[2];
 414         const char *clk_name;
 415         struct resource *mem;
 416         unsigned int i;
 417         int ret;
 418 
 419         if (!pdev->dev.of_node)
 420                 return -ENODEV;
 421 
 422         id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
 423         if (!id)
 424                 return -ENODEV;
 425 
 426         axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
 427         if (!axi_clkgen)
 428                 return -ENOMEM;
 429 
 430         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 431         axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
 432         if (IS_ERR(axi_clkgen->base))
 433                 return PTR_ERR(axi_clkgen->base);
 434 
 435         init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
 436         if (init.num_parents < 1 || init.num_parents > 2)
 437                 return -EINVAL;
 438 
 439         for (i = 0; i < init.num_parents; i++) {
 440                 parent_names[i] = of_clk_get_parent_name(pdev->dev.of_node, i);
 441                 if (!parent_names[i])
 442                         return -EINVAL;
 443         }
 444 
 445         clk_name = pdev->dev.of_node->name;
 446         of_property_read_string(pdev->dev.of_node, "clock-output-names",
 447                 &clk_name);
 448 
 449         init.name = clk_name;
 450         init.ops = &axi_clkgen_ops;
 451         init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
 452         init.parent_names = parent_names;
 453 
 454         axi_clkgen_mmcm_enable(axi_clkgen, false);
 455 
 456         axi_clkgen->clk_hw.init = &init;
 457         ret = devm_clk_hw_register(&pdev->dev, &axi_clkgen->clk_hw);
 458         if (ret)
 459                 return ret;
 460 
 461         return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_simple_get,
 462                                       &axi_clkgen->clk_hw);
 463 }
 464 
 465 static int axi_clkgen_remove(struct platform_device *pdev)
 466 {
 467         of_clk_del_provider(pdev->dev.of_node);
 468 
 469         return 0;
 470 }
 471 
 472 static struct platform_driver axi_clkgen_driver = {
 473         .driver = {
 474                 .name = "adi-axi-clkgen",
 475                 .of_match_table = axi_clkgen_ids,
 476         },
 477         .probe = axi_clkgen_probe,
 478         .remove = axi_clkgen_remove,
 479 };
 480 module_platform_driver(axi_clkgen_driver);
 481 
 482 MODULE_LICENSE("GPL v2");
 483 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 484 MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");

/* [<][>][^][v][top][bottom][index][help] */