uboot/drivers/misc/k3_avs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Texas Instruments' K3 Clas 0 Adaptive Voltage Scaling driver
   4 *
   5 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
   6 *      Tero Kristo <t-kristo@ti.com>
   7 *
   8 */
   9
  10#include <common.h>
  11#include <dm.h>
  12#include <errno.h>
  13#include <asm/io.h>
  14#include <i2c.h>
  15#include <k3-avs.h>
  16#include <dm/device_compat.h>
  17#include <linux/bitops.h>
  18#include <power/regulator.h>
  19
  20#define AM6_VTM_DEVINFO(i)      (priv->base + 0x100 + 0x20 * (i))
  21#define AM6_VTM_OPPVID_VD(i)    (priv->base + 0x104 + 0x20 * (i))
  22
  23#define AM6_VTM_AVS0_SUPPORTED  BIT(12)
  24
  25#define AM6_VTM_OPP_SHIFT(opp)  (8 * (opp))
  26#define AM6_VTM_OPP_MASK        0xff
  27
  28#define VD_FLAG_INIT_DONE       BIT(0)
  29
  30struct k3_avs_privdata {
  31        void *base;
  32        struct vd_config *vd_config;
  33};
  34
  35struct opp {
  36        u32 freq;
  37        u32 volt;
  38};
  39
  40struct vd_data {
  41        int id;
  42        u8 opp;
  43        u8 flags;
  44        int dev_id;
  45        int clk_id;
  46        struct opp opps[NUM_OPPS];
  47        struct udevice *supply;
  48};
  49
  50struct vd_config {
  51        struct vd_data *vds;
  52        u32 (*efuse_xlate)(struct k3_avs_privdata *priv, int idx, int opp);
  53};
  54
  55static struct k3_avs_privdata *k3_avs_priv;
  56
  57/**
  58 * am6_efuse_voltage: read efuse voltage from VTM
  59 * @priv: driver private data
  60 * @idx: VD to read efuse for
  61 * @opp: opp id to read
  62 *
  63 * Reads efuse value for the specified OPP, and converts the register
  64 * value to a voltage. Returns the voltage in uV, or 0 if nominal voltage
  65 * should be used.
  66 *
  67 * Efuse val to volt conversion logic:
  68 *
  69 * val > 171 volt increments in 20mV steps with base 171 => 1.66V
  70 * val between 115 to 11 increments in 10mV steps with base 115 => 1.1V
  71 * val between 15 to 115 increments in 5mV steps with base 15 => .6V
  72 * val between 1 to 15 increments in 20mv steps with base 0 => .3V
  73 * val 0 is invalid
  74 */
  75static u32 am6_efuse_xlate(struct k3_avs_privdata *priv, int idx, int opp)
  76{
  77        u32 val = readl(AM6_VTM_OPPVID_VD(idx));
  78
  79        val >>= AM6_VTM_OPP_SHIFT(opp);
  80        val &= AM6_VTM_OPP_MASK;
  81
  82        if (!val)
  83                return 0;
  84
  85        if (val > 171)
  86                return 1660000 + 20000 * (val - 171);
  87
  88        if (val > 115)
  89                return 1100000 + 10000 * (val - 115);
  90
  91        if (val > 15)
  92                return 600000 + 5000 * (val - 15);
  93
  94        return 300000 + 20000 * val;
  95}
  96
  97static int k3_avs_program_voltage(struct k3_avs_privdata *priv,
  98                                  struct vd_data *vd,
  99                                  int opp_id)
 100{
 101        u32 volt = vd->opps[opp_id].volt;
 102        struct vd_data *vd2;
 103
 104        if (!vd->supply)
 105                return -ENODEV;
 106
 107        vd->opp = opp_id;
 108        vd->flags |= VD_FLAG_INIT_DONE;
 109
 110        /* Take care of ganged rails and pick the Max amongst them*/
 111        for (vd2 = priv->vd_config->vds; vd2->id >= 0; vd2++) {
 112                if (vd == vd2)
 113                        continue;
 114
 115                if (vd2->supply != vd->supply)
 116                        continue;
 117
 118                if (vd2->opps[vd2->opp].volt > volt)
 119                        volt = vd2->opps[vd2->opp].volt;
 120
 121                vd2->flags |= VD_FLAG_INIT_DONE;
 122        }
 123
 124        return regulator_set_value(vd->supply, volt);
 125}
 126
 127static struct vd_data *get_vd(struct k3_avs_privdata *priv, int idx)
 128{
 129        struct vd_data *vd;
 130
 131        for (vd = priv->vd_config->vds; vd->id >= 0 && vd->id != idx; vd++)
 132                ;
 133
 134        if (vd->id < 0)
 135                return NULL;
 136
 137        return vd;
 138}
 139
 140/**
 141 * k3_avs_set_opp: Sets the voltage for an arbitrary VD rail
 142 * @dev: AVS device
 143 * @vdd_id: voltage domain ID
 144 * @opp_id: OPP ID
 145 *
 146 * Programs the desired OPP value for the defined voltage rail. This
 147 * should be called from board files if reconfiguration is desired.
 148 * Returns 0 on success, negative error value on failure.
 149 */
 150int k3_avs_set_opp(struct udevice *dev, int vdd_id, int opp_id)
 151{
 152        struct k3_avs_privdata *priv = dev_get_priv(dev);
 153        struct vd_data *vd;
 154
 155        vd = get_vd(priv, vdd_id);
 156        if (!vd)
 157                return -EINVAL;
 158
 159        return k3_avs_program_voltage(priv, vd, opp_id);
 160}
 161
 162static int match_opp(struct vd_data *vd, u32 freq)
 163{
 164        struct opp *opp;
 165        int opp_id;
 166
 167        for (opp_id = 0; opp_id < NUM_OPPS; opp_id++) {
 168                opp = &vd->opps[opp_id];
 169                if (opp->freq == freq)
 170                        return opp_id;
 171        }
 172
 173        printf("No matching OPP found for freq %d.\n", freq);
 174
 175        return -EINVAL;
 176}
 177
 178/**
 179 * k3_avs_notify_freq: Notify clock rate change towards AVS subsystem
 180 * @dev_id: Device ID for the clock to be changed
 181 * @clk_id: Clock ID for the clock to be changed
 182 * @freq: New frequency for clock
 183 *
 184 * Checks if the provided clock is the MPU clock or not, if not, return
 185 * immediately. If MPU clock is provided, maps the provided MPU frequency
 186 * towards an MPU OPP, and programs the voltage to the regulator. Return 0
 187 * on success, negative error value on failure.
 188 */
 189int k3_avs_notify_freq(int dev_id, int clk_id, u32 freq)
 190{
 191        int opp_id;
 192        struct k3_avs_privdata *priv = k3_avs_priv;
 193        struct vd_data *vd;
 194
 195        /* Driver may not be probed yet */
 196        if (!priv)
 197                return -EINVAL;
 198
 199        for (vd = priv->vd_config->vds; vd->id >= 0; vd++) {
 200                if (vd->dev_id != dev_id || vd->clk_id != clk_id)
 201                        continue;
 202
 203                opp_id = match_opp(vd, freq);
 204                if (opp_id < 0)
 205                        return opp_id;
 206
 207                vd->opp = opp_id;
 208                return k3_avs_program_voltage(priv, vd, opp_id);
 209        }
 210
 211        return -EINVAL;
 212}
 213
 214static int k3_avs_configure(struct udevice *dev, struct k3_avs_privdata *priv)
 215{
 216        struct vd_config *conf;
 217        int ret;
 218        char pname[20];
 219        struct vd_data *vd;
 220
 221        conf = (void *)dev_get_driver_data(dev);
 222
 223        priv->vd_config = conf;
 224
 225        for (vd = conf->vds; vd->id >= 0; vd++) {
 226                sprintf(pname, "vdd-supply-%d", vd->id);
 227                ret = device_get_supply_regulator(dev, pname, &vd->supply);
 228                if (ret)
 229                        dev_warn(dev, "supply not found for VD%d.\n", vd->id);
 230
 231                sprintf(pname, "ti,default-opp-%d", vd->id);
 232                ret = dev_read_u32_default(dev, pname, -1);
 233                if (ret != -1)
 234                        vd->opp = ret;
 235        }
 236
 237        return 0;
 238}
 239
 240/**
 241 * k3_avs_probe: parses VD info from VTM, and re-configures the OPP data
 242 *
 243 * Parses all VDs on a device calculating the AVS class-0 voltages for them,
 244 * and updates the vd_data based on this. The vd_data itself shall be used
 245 * to program the required OPPs later on. Returns 0 on success, negative
 246 * error value on failure.
 247 */
 248static int k3_avs_probe(struct udevice *dev)
 249{
 250        int opp_id;
 251        u32 volt;
 252        struct opp *opp;
 253        struct k3_avs_privdata *priv;
 254        struct vd_data *vd;
 255        int ret;
 256
 257        priv = dev_get_priv(dev);
 258
 259        k3_avs_priv = priv;
 260
 261        ret = k3_avs_configure(dev, priv);
 262        if (ret)
 263                return ret;
 264
 265        priv->base = dev_read_addr_ptr(dev);
 266        if (!priv->base)
 267                return -ENODEV;
 268
 269        for (vd = priv->vd_config->vds; vd->id >= 0; vd++) {
 270                if (!(readl(AM6_VTM_DEVINFO(vd->id)) &
 271                      AM6_VTM_AVS0_SUPPORTED)) {
 272                        dev_warn(dev, "AVS-class 0 not supported for VD%d\n",
 273                                 vd->id);
 274                        continue;
 275                }
 276
 277                for (opp_id = 0; opp_id < NUM_OPPS; opp_id++) {
 278                        opp = &vd->opps[opp_id];
 279
 280                        if (!opp->freq)
 281                                continue;
 282
 283                        volt = priv->vd_config->efuse_xlate(priv, vd->id,
 284                                                            opp_id);
 285                        if (volt)
 286                                opp->volt = volt;
 287                }
 288        }
 289
 290        for (vd = priv->vd_config->vds; vd->id >= 0; vd++) {
 291                if (vd->flags & VD_FLAG_INIT_DONE)
 292                        continue;
 293
 294                k3_avs_program_voltage(priv, vd, vd->opp);
 295        }
 296
 297        return 0;
 298}
 299
 300static struct vd_data am654_vd_data[] = {
 301        {
 302                .id = AM6_VDD_CORE,
 303                .dev_id = 82, /* AM6_DEV_CBASS0 */
 304                .clk_id = 0, /* main sysclk0 */
 305                .opp = AM6_OPP_NOM,
 306                .opps = {
 307                        [AM6_OPP_NOM] = {
 308                                .volt = 1000000,
 309                                .freq = 250000000, /* CBASS0 */
 310                        },
 311                },
 312        },
 313        {
 314                .id = AM6_VDD_MPU0,
 315                .dev_id = 202, /* AM6_DEV_COMPUTE_CLUSTER_A53_0 */
 316                .clk_id = 0, /* ARM clock */
 317                .opp = AM6_OPP_NOM,
 318                .opps = {
 319                        [AM6_OPP_NOM] = {
 320                                .volt = 1100000,
 321                                .freq = 800000000,
 322                        },
 323                        [AM6_OPP_OD] = {
 324                                .volt = 1200000,
 325                                .freq = 1000000000,
 326                        },
 327                        [AM6_OPP_TURBO] = {
 328                                .volt = 1240000,
 329                                .freq = 1100000000,
 330                        },
 331                },
 332        },
 333        {
 334                .id = AM6_VDD_MPU1,
 335                .opp = AM6_OPP_NOM,
 336                .dev_id = 204, /* AM6_DEV_COMPUTE_CLUSTER_A53_2 */
 337                .clk_id = 0, /* ARM clock */
 338                .opps = {
 339                        [AM6_OPP_NOM] = {
 340                                .volt = 1100000,
 341                                .freq = 800000000,
 342                        },
 343                        [AM6_OPP_OD] = {
 344                                .volt = 1200000,
 345                                .freq = 1000000000,
 346                        },
 347                        [AM6_OPP_TURBO] = {
 348                                .volt = 1240000,
 349                                .freq = 1100000000,
 350                        },
 351                },
 352        },
 353        { .id = -1 },
 354};
 355
 356static struct vd_data j721e_vd_data[] = {
 357        {
 358                .id = J721E_VDD_MPU,
 359                .opp = AM6_OPP_NOM,
 360                .dev_id = 202, /* J721E_DEV_A72SS0_CORE0 */
 361                .clk_id = 2, /* ARM clock */
 362                .opps = {
 363                        [AM6_OPP_NOM] = {
 364                                .volt = 880000, /* TBD in DM */
 365                                .freq = 2000000000,
 366                        },
 367                },
 368        },
 369        { .id = -1 },
 370};
 371
 372static struct vd_config j721e_vd_config = {
 373        .efuse_xlate = am6_efuse_xlate,
 374        .vds = j721e_vd_data,
 375};
 376
 377static struct vd_config am654_vd_config = {
 378        .efuse_xlate = am6_efuse_xlate,
 379        .vds = am654_vd_data,
 380};
 381
 382static const struct udevice_id k3_avs_ids[] = {
 383        { .compatible = "ti,am654-avs", .data = (ulong)&am654_vd_config },
 384        { .compatible = "ti,j721e-avs", .data = (ulong)&j721e_vd_config },
 385        {}
 386};
 387
 388U_BOOT_DRIVER(k3_avs) = {
 389        .name = "k3_avs",
 390        .of_match = k3_avs_ids,
 391        .id = UCLASS_MISC,
 392        .probe = k3_avs_probe,
 393        .priv_auto      = sizeof(struct k3_avs_privdata),
 394};
 395