uboot/drivers/clk/clk-cdce9xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Texas Instruments CDCE913/925/937/949 clock synthesizer driver
   4 *
   5 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
   6 *      Tero Kristo <t-kristo@ti.com>
   7 *
   8 * Based on Linux kernel clk-cdce925.c.
   9 */
  10
  11#include <common.h>
  12#include <dm.h>
  13#include <errno.h>
  14#include <clk-uclass.h>
  15#include <i2c.h>
  16#include <dm/device_compat.h>
  17#include <linux/bitops.h>
  18
  19#define MAX_NUMBER_OF_PLLS              4
  20#define MAX_NUMER_OF_OUTPUTS            9
  21
  22#define CDCE9XX_REG_GLOBAL1             0x01
  23#define CDCE9XX_REG_Y1SPIPDIVH          0x02
  24#define CDCE9XX_REG_PDIV1L              0x03
  25#define CDCE9XX_REG_XCSEL               0x05
  26
  27#define CDCE9XX_PDIV1_H_MASK            0x3
  28
  29#define CDCE9XX_REG_PDIV(clk)           (0x16 + (((clk) - 1) & 1) + \
  30                                         ((clk) - 1) / 2 * 0x10)
  31
  32#define CDCE9XX_PDIV_MASK               0x7f
  33
  34#define CDCE9XX_BYTE_TRANSFER           BIT(7)
  35
  36struct cdce9xx_chip_info {
  37        int num_plls;
  38        int num_outputs;
  39};
  40
  41struct cdce9xx_clk_data {
  42        struct udevice *i2c;
  43        struct cdce9xx_chip_info *chip;
  44        u32 xtal_rate;
  45};
  46
  47static const struct cdce9xx_chip_info cdce913_chip_info = {
  48        .num_plls = 1, .num_outputs = 3,
  49};
  50
  51static const struct cdce9xx_chip_info cdce925_chip_info = {
  52        .num_plls = 2, .num_outputs = 5,
  53};
  54
  55static const struct cdce9xx_chip_info cdce937_chip_info = {
  56        .num_plls = 3, .num_outputs = 7,
  57};
  58
  59static const struct cdce9xx_chip_info cdce949_chip_info = {
  60        .num_plls = 4, .num_outputs = 9,
  61};
  62
  63static int cdce9xx_reg_read(struct udevice *dev, u8 addr, u8 *buf)
  64{
  65        struct cdce9xx_clk_data *data = dev_get_priv(dev);
  66        int ret;
  67
  68        ret = dm_i2c_read(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, buf, 1);
  69        if (ret)
  70                dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
  71                        addr, ret);
  72
  73        return ret;
  74}
  75
  76static int cdce9xx_reg_write(struct udevice *dev, u8 addr, u8 val)
  77{
  78        struct cdce9xx_clk_data *data = dev_get_priv(dev);
  79        int ret;
  80
  81        ret = dm_i2c_write(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, &val, 1);
  82        if (ret)
  83                dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
  84                        addr, ret);
  85
  86        return ret;
  87}
  88
  89static int cdce9xx_clk_of_xlate(struct clk *clk,
  90                                struct ofnode_phandle_args *args)
  91{
  92        struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
  93
  94        if (args->args_count != 1)
  95                return -EINVAL;
  96
  97        if (args->args[0] > data->chip->num_outputs)
  98                return -EINVAL;
  99
 100        clk->id = args->args[0];
 101
 102        return 0;
 103}
 104
 105static int cdce9xx_clk_probe(struct udevice *dev)
 106{
 107        struct cdce9xx_clk_data *data = dev_get_priv(dev);
 108        struct cdce9xx_chip_info *chip = (void *)dev_get_driver_data(dev);
 109        int ret;
 110        u32 val;
 111        struct clk clk;
 112
 113        val = (u32)dev_read_addr_ptr(dev);
 114
 115        ret = i2c_get_chip(dev->parent, val, 1, &data->i2c);
 116        if (ret) {
 117                dev_err(dev, "I2C probe failed.\n");
 118                return ret;
 119        }
 120
 121        data->chip = chip;
 122
 123        ret = clk_get_by_index(dev, 0, &clk);
 124        data->xtal_rate = clk_get_rate(&clk);
 125
 126        val = dev_read_u32_default(dev, "xtal-load-pf", -1);
 127        if (val >= 0)
 128                cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3);
 129
 130        return 0;
 131}
 132
 133static u16 cdce9xx_clk_get_pdiv(struct clk *clk)
 134{
 135        u8 val;
 136        u16 pdiv;
 137        int ret;
 138
 139        if (clk->id == 0) {
 140                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
 141                if (ret)
 142                        return 0;
 143
 144                pdiv = (val & CDCE9XX_PDIV1_H_MASK) << 8;
 145
 146                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV1L, &val);
 147                if (ret)
 148                        return 0;
 149
 150                pdiv |= val;
 151        } else {
 152                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 153                                       &val);
 154                if (ret)
 155                        return 0;
 156
 157                pdiv = val & CDCE9XX_PDIV_MASK;
 158        }
 159
 160        return pdiv;
 161}
 162
 163static u32 cdce9xx_clk_get_parent_rate(struct clk *clk)
 164{
 165        struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
 166
 167        return data->xtal_rate;
 168}
 169
 170static ulong cdce9xx_clk_get_rate(struct clk *clk)
 171{
 172        u32 parent_rate;
 173        u16 pdiv;
 174
 175        parent_rate = cdce9xx_clk_get_parent_rate(clk);
 176
 177        pdiv = cdce9xx_clk_get_pdiv(clk);
 178
 179        return parent_rate / pdiv;
 180}
 181
 182static ulong cdce9xx_clk_set_rate(struct clk *clk, ulong rate)
 183{
 184        u32 parent_rate;
 185        int pdiv;
 186        u32 diff;
 187        u8 val;
 188        int ret;
 189
 190        parent_rate = cdce9xx_clk_get_parent_rate(clk);
 191
 192        pdiv = parent_rate / rate;
 193
 194        diff = rate - parent_rate / pdiv;
 195
 196        if (rate - parent_rate / (pdiv + 1) < diff)
 197                pdiv++;
 198
 199        if (clk->id == 0) {
 200                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
 201                if (ret)
 202                        return ret;
 203
 204                val &= ~CDCE9XX_PDIV1_H_MASK;
 205
 206                val |= (pdiv >> 8);
 207
 208                ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, val);
 209                if (ret)
 210                        return ret;
 211
 212                ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV1L,
 213                                        (pdiv & 0xff));
 214                if (ret)
 215                        return ret;
 216        } else {
 217                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 218                                       &val);
 219                if (ret)
 220                        return ret;
 221
 222                val &= ~CDCE9XX_PDIV_MASK;
 223
 224                val |= pdiv;
 225
 226                ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 227                                        val);
 228                if (ret)
 229                        return ret;
 230        }
 231
 232        return 0;
 233}
 234
 235static const struct udevice_id cdce9xx_clk_of_match[] = {
 236        { .compatible = "ti,cdce913", .data = (u32)&cdce913_chip_info },
 237        { .compatible = "ti,cdce925", .data = (u32)&cdce925_chip_info },
 238        { .compatible = "ti,cdce937", .data = (u32)&cdce937_chip_info },
 239        { .compatible = "ti,cdce949", .data = (u32)&cdce949_chip_info },
 240        { /* sentinel */ },
 241};
 242
 243static const struct clk_ops cdce9xx_clk_ops = {
 244        .of_xlate = cdce9xx_clk_of_xlate,
 245        .get_rate = cdce9xx_clk_get_rate,
 246        .set_rate = cdce9xx_clk_set_rate,
 247};
 248
 249U_BOOT_DRIVER(cdce9xx_clk) = {
 250        .name = "cdce9xx-clk",
 251        .id = UCLASS_CLK,
 252        .of_match = cdce9xx_clk_of_match,
 253        .probe = cdce9xx_clk_probe,
 254        .priv_auto      = sizeof(struct cdce9xx_clk_data),
 255        .ops = &cdce9xx_clk_ops,
 256};
 257