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_request(struct clk *clk)
  90{
  91        struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
  92
  93        if (clk->id > data->chip->num_outputs)
  94                return -EINVAL;
  95
  96        return 0;
  97}
  98
  99static int cdce9xx_clk_probe(struct udevice *dev)
 100{
 101        struct cdce9xx_clk_data *data = dev_get_priv(dev);
 102        struct cdce9xx_chip_info *chip = (void *)dev_get_driver_data(dev);
 103        int ret;
 104        u32 val;
 105        struct clk clk;
 106
 107        val = (u32)dev_read_addr_ptr(dev);
 108
 109        ret = i2c_get_chip(dev->parent, val, 1, &data->i2c);
 110        if (ret) {
 111                dev_err(dev, "I2C probe failed.\n");
 112                return ret;
 113        }
 114
 115        data->chip = chip;
 116
 117        ret = clk_get_by_index(dev, 0, &clk);
 118        data->xtal_rate = clk_get_rate(&clk);
 119
 120        val = dev_read_u32_default(dev, "xtal-load-pf", -1);
 121        if (val >= 0)
 122                cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3);
 123
 124        return 0;
 125}
 126
 127static u16 cdce9xx_clk_get_pdiv(struct clk *clk)
 128{
 129        u8 val;
 130        u16 pdiv;
 131        int ret;
 132
 133        if (clk->id == 0) {
 134                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
 135                if (ret)
 136                        return 0;
 137
 138                pdiv = (val & CDCE9XX_PDIV1_H_MASK) << 8;
 139
 140                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV1L, &val);
 141                if (ret)
 142                        return 0;
 143
 144                pdiv |= val;
 145        } else {
 146                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 147                                       &val);
 148                if (ret)
 149                        return 0;
 150
 151                pdiv = val & CDCE9XX_PDIV_MASK;
 152        }
 153
 154        return pdiv;
 155}
 156
 157static u32 cdce9xx_clk_get_parent_rate(struct clk *clk)
 158{
 159        struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
 160
 161        return data->xtal_rate;
 162}
 163
 164static ulong cdce9xx_clk_get_rate(struct clk *clk)
 165{
 166        u32 parent_rate;
 167        u16 pdiv;
 168
 169        parent_rate = cdce9xx_clk_get_parent_rate(clk);
 170
 171        pdiv = cdce9xx_clk_get_pdiv(clk);
 172
 173        return parent_rate / pdiv;
 174}
 175
 176static ulong cdce9xx_clk_set_rate(struct clk *clk, ulong rate)
 177{
 178        u32 parent_rate;
 179        int pdiv;
 180        u32 diff;
 181        u8 val;
 182        int ret;
 183
 184        parent_rate = cdce9xx_clk_get_parent_rate(clk);
 185
 186        pdiv = parent_rate / rate;
 187
 188        diff = rate - parent_rate / pdiv;
 189
 190        if (rate - parent_rate / (pdiv + 1) < diff)
 191                pdiv++;
 192
 193        if (clk->id == 0) {
 194                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
 195                if (ret)
 196                        return ret;
 197
 198                val &= ~CDCE9XX_PDIV1_H_MASK;
 199
 200                val |= (pdiv >> 8);
 201
 202                ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, val);
 203                if (ret)
 204                        return ret;
 205
 206                ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV1L,
 207                                        (pdiv & 0xff));
 208                if (ret)
 209                        return ret;
 210        } else {
 211                ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 212                                       &val);
 213                if (ret)
 214                        return ret;
 215
 216                val &= ~CDCE9XX_PDIV_MASK;
 217
 218                val |= pdiv;
 219
 220                ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 221                                        val);
 222                if (ret)
 223                        return ret;
 224        }
 225
 226        return 0;
 227}
 228
 229static const struct udevice_id cdce9xx_clk_of_match[] = {
 230        { .compatible = "ti,cdce913", .data = (u32)&cdce913_chip_info },
 231        { .compatible = "ti,cdce925", .data = (u32)&cdce925_chip_info },
 232        { .compatible = "ti,cdce937", .data = (u32)&cdce937_chip_info },
 233        { .compatible = "ti,cdce949", .data = (u32)&cdce949_chip_info },
 234        { /* sentinel */ },
 235};
 236
 237static const struct clk_ops cdce9xx_clk_ops = {
 238        .request = cdce9xx_clk_request,
 239        .get_rate = cdce9xx_clk_get_rate,
 240        .set_rate = cdce9xx_clk_set_rate,
 241};
 242
 243U_BOOT_DRIVER(cdce9xx_clk) = {
 244        .name = "cdce9xx-clk",
 245        .id = UCLASS_CLK,
 246        .of_match = cdce9xx_clk_of_match,
 247        .probe = cdce9xx_clk_probe,
 248        .priv_auto      = sizeof(struct cdce9xx_clk_data),
 249        .ops = &cdce9xx_clk_ops,
 250};
 251