uboot/drivers/cpu/imx8_cpu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2019 NXP
   4 */
   5
   6#include <common.h>
   7#include <cpu.h>
   8#include <dm.h>
   9#include <thermal.h>
  10#include <asm/global_data.h>
  11#include <asm/system.h>
  12#include <firmware/imx/sci/sci.h>
  13#include <asm/arch/sys_proto.h>
  14#include <asm/arch-imx/cpu.h>
  15#include <asm/armv8/cpu.h>
  16#include <imx_thermal.h>
  17#include <linux/bitops.h>
  18#include <linux/clk-provider.h>
  19
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22struct cpu_imx_plat {
  23        const char *name;
  24        const char *rev;
  25        const char *type;
  26        u32 cpu_rsrc;
  27        u32 cpurev;
  28        u32 freq_mhz;
  29        u32 mpidr;
  30};
  31
  32static const char *get_imx_type_str(u32 imxtype)
  33{
  34        switch (imxtype) {
  35        case MXC_CPU_IMX8QXP:
  36        case MXC_CPU_IMX8QXP_A0:
  37                return "8QXP";
  38        case MXC_CPU_IMX8QM:
  39                return "8QM";
  40        case MXC_CPU_IMX93:
  41                return "93(52)";/* iMX93 Dual core with NPU */
  42        case MXC_CPU_IMX9351:
  43                return "93(51)";/* iMX93 Single core with NPU */
  44        case MXC_CPU_IMX9332:
  45                return "93(32)";/* iMX93 Dual core without NPU */
  46        case MXC_CPU_IMX9331:
  47                return "93(31)";/* iMX93 Single core without NPU */
  48        case MXC_CPU_IMX9322:
  49                return "93(22)";/* iMX93 9x9 Dual core  */
  50        case MXC_CPU_IMX9321:
  51                return "93(21)";/* iMX93 9x9 Single core  */
  52        case MXC_CPU_IMX9312:
  53                return "93(12)";/* iMX93 9x9 Dual core without NPU */
  54        case MXC_CPU_IMX9311:
  55                return "93(11)";/* iMX93 9x9 Single core without NPU */
  56        default:
  57                return "??";
  58        }
  59}
  60
  61static const char *get_imx_rev_str(u32 rev)
  62{
  63        static char revision[4];
  64
  65        if (IS_ENABLED(CONFIG_IMX8)) {
  66                switch (rev) {
  67                case CHIP_REV_A:
  68                        return "A";
  69                case CHIP_REV_B:
  70                        return "B";
  71                case CHIP_REV_C:
  72                        return "C";
  73                default:
  74                        return "?";
  75                }
  76        } else {
  77                revision[0] = '1' + (((rev & 0xf0) - CHIP_REV_1_0) >> 4);
  78                revision[1] = '.';
  79                revision[2] = '0' + (rev & 0xf);
  80                revision[3] = '\0';
  81
  82                return revision;
  83        }
  84}
  85
  86static void set_core_data(struct udevice *dev)
  87{
  88        struct cpu_imx_plat *plat = dev_get_plat(dev);
  89
  90        if (device_is_compatible(dev, "arm,cortex-a35")) {
  91                plat->cpu_rsrc = SC_R_A35;
  92                plat->name = "A35";
  93        } else if (device_is_compatible(dev, "arm,cortex-a53")) {
  94                plat->cpu_rsrc = SC_R_A53;
  95                plat->name = "A53";
  96        } else if (device_is_compatible(dev, "arm,cortex-a72")) {
  97                plat->cpu_rsrc = SC_R_A72;
  98                plat->name = "A72";
  99        } else if (device_is_compatible(dev, "arm,cortex-a55")) {
 100                plat->name = "A55";
 101        } else {
 102                plat->cpu_rsrc = SC_R_A53;
 103                plat->name = "?";
 104        }
 105}
 106
 107#if IS_ENABLED(CONFIG_DM_THERMAL)
 108static int cpu_imx_get_temp(struct cpu_imx_plat *plat)
 109{
 110        struct udevice *thermal_dev;
 111        int cpu_tmp, ret;
 112        int idx = 1; /* use "cpu-thermal0" device */
 113
 114        if (IS_ENABLED(CONFIG_IMX8)) {
 115                if (plat->cpu_rsrc == SC_R_A72)
 116                        idx = 2; /* use "cpu-thermal1" device */
 117        } else {
 118                idx = 1;
 119        }
 120
 121        ret = uclass_get_device(UCLASS_THERMAL, idx, &thermal_dev);
 122        if (!ret) {
 123                ret = thermal_get_temp(thermal_dev, &cpu_tmp);
 124                if (ret)
 125                        return 0xdeadbeef;
 126        } else {
 127                return 0xdeadbeef;
 128        }
 129
 130        return cpu_tmp;
 131}
 132#else
 133static int cpu_imx_get_temp(struct cpu_imx_plat *plat)
 134{
 135        return 0;
 136}
 137#endif
 138
 139__weak u32 get_cpu_temp_grade(int *minc, int *maxc)
 140{
 141        return 0;
 142}
 143
 144static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size)
 145{
 146        struct cpu_imx_plat *plat = dev_get_plat(dev);
 147        const char *grade;
 148        int ret, temp;
 149        int minc, maxc;
 150
 151        if (size < 100)
 152                return -ENOSPC;
 153
 154        ret = snprintf(buf, size, "NXP i.MX%s Rev%s %s at %u MHz",
 155                       plat->type, plat->rev, plat->name, plat->freq_mhz);
 156
 157        if (IS_ENABLED(CONFIG_IMX9)) {
 158                switch (get_cpu_temp_grade(&minc, &maxc)) {
 159                case TEMP_AUTOMOTIVE:
 160                        grade = "Automotive temperature grade ";
 161                        break;
 162                case TEMP_INDUSTRIAL:
 163                        grade = "Industrial temperature grade ";
 164                        break;
 165                case TEMP_EXTCOMMERCIAL:
 166                        grade = "Extended Consumer temperature grade ";
 167                        break;
 168                default:
 169                        grade = "Consumer temperature grade ";
 170                        break;
 171                }
 172
 173                buf = buf + ret;
 174                size = size - ret;
 175                ret = snprintf(buf, size, "\nCPU:   %s (%dC to %dC)", grade, minc, maxc);
 176        }
 177
 178        if (IS_ENABLED(CONFIG_DM_THERMAL)) {
 179                temp = cpu_imx_get_temp(plat);
 180                buf = buf + ret;
 181                size = size - ret;
 182                if (temp != 0xdeadbeef)
 183                        ret = snprintf(buf, size, " at %dC", temp);
 184                else
 185                        ret = snprintf(buf, size, " - invalid sensor data");
 186        }
 187
 188        snprintf(buf + ret, size - ret, "\n");
 189
 190        return 0;
 191}
 192
 193static int cpu_imx_get_info(const struct udevice *dev, struct cpu_info *info)
 194{
 195        struct cpu_imx_plat *plat = dev_get_plat(dev);
 196
 197        info->cpu_freq = plat->freq_mhz * 1000;
 198        info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
 199        return 0;
 200}
 201
 202static int cpu_imx_get_count(const struct udevice *dev)
 203{
 204        ofnode node;
 205        int num = 0;
 206
 207        ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
 208                const char *device_type;
 209
 210                if (!ofnode_is_enabled(node))
 211                        continue;
 212
 213                device_type = ofnode_read_string(node, "device_type");
 214                if (!device_type)
 215                        continue;
 216
 217                if (!strcmp(device_type, "cpu"))
 218                        num++;
 219        }
 220
 221        return num;
 222}
 223
 224static int cpu_imx_get_vendor(const struct udevice *dev,  char *buf, int size)
 225{
 226        snprintf(buf, size, "NXP");
 227        return 0;
 228}
 229
 230static int cpu_imx_is_current(struct udevice *dev)
 231{
 232        struct cpu_imx_plat *plat = dev_get_plat(dev);
 233
 234        if (plat->mpidr == (read_mpidr() & 0xffff))
 235                return 1;
 236
 237        return 0;
 238}
 239
 240static const struct cpu_ops cpu_imx_ops = {
 241        .get_desc       = cpu_imx_get_desc,
 242        .get_info       = cpu_imx_get_info,
 243        .get_count      = cpu_imx_get_count,
 244        .get_vendor     = cpu_imx_get_vendor,
 245        .is_current     = cpu_imx_is_current,
 246};
 247
 248static const struct udevice_id cpu_imx_ids[] = {
 249        { .compatible = "arm,cortex-a35" },
 250        { .compatible = "arm,cortex-a53" },
 251        { .compatible = "arm,cortex-a55" },
 252        { .compatible = "arm,cortex-a72" },
 253        { }
 254};
 255
 256static ulong imx_get_cpu_rate(struct udevice *dev)
 257{
 258        struct cpu_imx_plat *plat = dev_get_plat(dev);
 259        struct clk clk;
 260        ulong rate;
 261        int ret;
 262
 263        if (IS_ENABLED(CONFIG_IMX8)) {
 264                ret = sc_pm_get_clock_rate(-1, plat->cpu_rsrc, SC_PM_CLK_CPU,
 265                                           (sc_pm_clock_rate_t *)&rate);
 266        } else {
 267                ret = clk_get_by_index(dev, 0, &clk);
 268                if (!ret) {
 269                        rate = clk_get_rate(&clk);
 270                        if (!rate)
 271                                ret = -EOPNOTSUPP;
 272                }
 273        }
 274        if (ret) {
 275                printf("Could not read CPU frequency: %d\n", ret);
 276                return 0;
 277        }
 278
 279        return rate;
 280}
 281
 282static int imx_cpu_probe(struct udevice *dev)
 283{
 284        struct cpu_imx_plat *plat = dev_get_plat(dev);
 285        u32 cpurev;
 286
 287        set_core_data(dev);
 288        cpurev = get_cpu_rev();
 289        plat->cpurev = cpurev;
 290        plat->rev = get_imx_rev_str(cpurev & 0xFFF);
 291        plat->type = get_imx_type_str((cpurev & 0xFF000) >> 12);
 292        plat->freq_mhz = imx_get_cpu_rate(dev) / 1000000;
 293        plat->mpidr = dev_read_addr(dev);
 294        if (plat->mpidr == FDT_ADDR_T_NONE) {
 295                printf("%s: Failed to get CPU reg property\n", __func__);
 296                return -EINVAL;
 297        }
 298
 299        return 0;
 300}
 301
 302U_BOOT_DRIVER(cpu_imx_drv) = {
 303        .name           = "imx_cpu",
 304        .id             = UCLASS_CPU,
 305        .of_match       = cpu_imx_ids,
 306        .ops            = &cpu_imx_ops,
 307        .probe          = imx_cpu_probe,
 308        .plat_auto      = sizeof(struct cpu_imx_plat),
 309        .flags          = DM_FLAG_PRE_RELOC,
 310};
 311