root/drivers/macintosh/windfarm_smu_sensors.c

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

DEFINITIONS

This source file includes following definitions.
  1. smu_ads_release
  2. smu_read_adc
  3. smu_cputemp_get
  4. smu_cpuamp_get
  5. smu_cpuvolt_get
  6. smu_slotspow_get
  7. smu_ads_create
  8. smu_cpu_power_release
  9. smu_cpu_power_get
  10. smu_cpu_power_create
  11. smu_fetch_param_partitions
  12. smu_sensors_init
  13. smu_sensors_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Windfarm PowerMac thermal control. SMU based sensors
   4  *
   5  * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
   6  *                    <benh@kernel.crashing.org>
   7  */
   8 
   9 #include <linux/types.h>
  10 #include <linux/errno.h>
  11 #include <linux/kernel.h>
  12 #include <linux/delay.h>
  13 #include <linux/slab.h>
  14 #include <linux/init.h>
  15 #include <linux/wait.h>
  16 #include <linux/completion.h>
  17 #include <asm/prom.h>
  18 #include <asm/machdep.h>
  19 #include <asm/io.h>
  20 #include <asm/sections.h>
  21 #include <asm/smu.h>
  22 
  23 #include "windfarm.h"
  24 
  25 #define VERSION "0.2"
  26 
  27 #undef DEBUG
  28 
  29 #ifdef DEBUG
  30 #define DBG(args...)    printk(args)
  31 #else
  32 #define DBG(args...)    do { } while(0)
  33 #endif
  34 
  35 /*
  36  * Various SMU "partitions" calibration objects for which we
  37  * keep pointers here for use by bits & pieces of the driver
  38  */
  39 static struct smu_sdbp_cpuvcp *cpuvcp;
  40 static int  cpuvcp_version;
  41 static struct smu_sdbp_cpudiode *cpudiode;
  42 static struct smu_sdbp_slotspow *slotspow;
  43 static u8 *debugswitches;
  44 
  45 /*
  46  * SMU basic sensors objects
  47  */
  48 
  49 static LIST_HEAD(smu_ads);
  50 
  51 struct smu_ad_sensor {
  52         struct list_head        link;
  53         u32                     reg;            /* index in SMU */
  54         struct wf_sensor        sens;
  55 };
  56 #define to_smu_ads(c) container_of(c, struct smu_ad_sensor, sens)
  57 
  58 static void smu_ads_release(struct wf_sensor *sr)
  59 {
  60         struct smu_ad_sensor *ads = to_smu_ads(sr);
  61 
  62         kfree(ads);
  63 }
  64 
  65 static int smu_read_adc(u8 id, s32 *value)
  66 {
  67         struct smu_simple_cmd   cmd;
  68         DECLARE_COMPLETION_ONSTACK(comp);
  69         int rc;
  70 
  71         rc = smu_queue_simple(&cmd, SMU_CMD_READ_ADC, 1,
  72                               smu_done_complete, &comp, id);
  73         if (rc)
  74                 return rc;
  75         wait_for_completion(&comp);
  76         if (cmd.cmd.status != 0)
  77                 return cmd.cmd.status;
  78         if (cmd.cmd.reply_len != 2) {
  79                 printk(KERN_ERR "winfarm: read ADC 0x%x returned %d bytes !\n",
  80                        id, cmd.cmd.reply_len);
  81                 return -EIO;
  82         }
  83         *value = *((u16 *)cmd.buffer);
  84         return 0;
  85 }
  86 
  87 static int smu_cputemp_get(struct wf_sensor *sr, s32 *value)
  88 {
  89         struct smu_ad_sensor *ads = to_smu_ads(sr);
  90         int rc;
  91         s32 val;
  92         s64 scaled;
  93 
  94         rc = smu_read_adc(ads->reg, &val);
  95         if (rc) {
  96                 printk(KERN_ERR "windfarm: read CPU temp failed, err %d\n",
  97                        rc);
  98                 return rc;
  99         }
 100 
 101         /* Ok, we have to scale & adjust, taking units into account */
 102         scaled = (s64)(((u64)val) * (u64)cpudiode->m_value);
 103         scaled >>= 3;
 104         scaled += ((s64)cpudiode->b_value) << 9;
 105         *value = (s32)(scaled << 1);
 106 
 107         return 0;
 108 }
 109 
 110 static int smu_cpuamp_get(struct wf_sensor *sr, s32 *value)
 111 {
 112         struct smu_ad_sensor *ads = to_smu_ads(sr);
 113         s32 val, scaled;
 114         int rc;
 115 
 116         rc = smu_read_adc(ads->reg, &val);
 117         if (rc) {
 118                 printk(KERN_ERR "windfarm: read CPU current failed, err %d\n",
 119                        rc);
 120                 return rc;
 121         }
 122 
 123         /* Ok, we have to scale & adjust, taking units into account */
 124         scaled = (s32)(val * (u32)cpuvcp->curr_scale);
 125         scaled += (s32)cpuvcp->curr_offset;
 126         *value = scaled << 4;
 127 
 128         return 0;
 129 }
 130 
 131 static int smu_cpuvolt_get(struct wf_sensor *sr, s32 *value)
 132 {
 133         struct smu_ad_sensor *ads = to_smu_ads(sr);
 134         s32 val, scaled;
 135         int rc;
 136 
 137         rc = smu_read_adc(ads->reg, &val);
 138         if (rc) {
 139                 printk(KERN_ERR "windfarm: read CPU voltage failed, err %d\n",
 140                        rc);
 141                 return rc;
 142         }
 143 
 144         /* Ok, we have to scale & adjust, taking units into account */
 145         scaled = (s32)(val * (u32)cpuvcp->volt_scale);
 146         scaled += (s32)cpuvcp->volt_offset;
 147         *value = scaled << 4;
 148 
 149         return 0;
 150 }
 151 
 152 static int smu_slotspow_get(struct wf_sensor *sr, s32 *value)
 153 {
 154         struct smu_ad_sensor *ads = to_smu_ads(sr);
 155         s32 val, scaled;
 156         int rc;
 157 
 158         rc = smu_read_adc(ads->reg, &val);
 159         if (rc) {
 160                 printk(KERN_ERR "windfarm: read slots power failed, err %d\n",
 161                        rc);
 162                 return rc;
 163         }
 164 
 165         /* Ok, we have to scale & adjust, taking units into account */
 166         scaled = (s32)(val * (u32)slotspow->pow_scale);
 167         scaled += (s32)slotspow->pow_offset;
 168         *value = scaled << 4;
 169 
 170         return 0;
 171 }
 172 
 173 
 174 static const struct wf_sensor_ops smu_cputemp_ops = {
 175         .get_value      = smu_cputemp_get,
 176         .release        = smu_ads_release,
 177         .owner          = THIS_MODULE,
 178 };
 179 static const struct wf_sensor_ops smu_cpuamp_ops = {
 180         .get_value      = smu_cpuamp_get,
 181         .release        = smu_ads_release,
 182         .owner          = THIS_MODULE,
 183 };
 184 static const struct wf_sensor_ops smu_cpuvolt_ops = {
 185         .get_value      = smu_cpuvolt_get,
 186         .release        = smu_ads_release,
 187         .owner          = THIS_MODULE,
 188 };
 189 static const struct wf_sensor_ops smu_slotspow_ops = {
 190         .get_value      = smu_slotspow_get,
 191         .release        = smu_ads_release,
 192         .owner          = THIS_MODULE,
 193 };
 194 
 195 
 196 static struct smu_ad_sensor *smu_ads_create(struct device_node *node)
 197 {
 198         struct smu_ad_sensor *ads;
 199         const char *l;
 200         const u32 *v;
 201 
 202         ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL);
 203         if (ads == NULL)
 204                 return NULL;
 205         l = of_get_property(node, "location", NULL);
 206         if (l == NULL)
 207                 goto fail;
 208 
 209         /* We currently pick the sensors based on the OF name and location
 210          * properties, while Darwin uses the sensor-id's.
 211          * The problem with the IDs is that they are model specific while it
 212          * looks like apple has been doing a reasonably good job at keeping
 213          * the names and locations consistents so I'll stick with the names
 214          * and locations for now.
 215          */
 216         if (of_node_is_type(node, "temp-sensor") &&
 217             !strcmp(l, "CPU T-Diode")) {
 218                 ads->sens.ops = &smu_cputemp_ops;
 219                 ads->sens.name = "cpu-temp";
 220                 if (cpudiode == NULL) {
 221                         DBG("wf: cpudiode partition (%02x) not found\n",
 222                             SMU_SDB_CPUDIODE_ID);
 223                         goto fail;
 224                 }
 225         } else if (of_node_is_type(node, "current-sensor") &&
 226                    !strcmp(l, "CPU Current")) {
 227                 ads->sens.ops = &smu_cpuamp_ops;
 228                 ads->sens.name = "cpu-current";
 229                 if (cpuvcp == NULL) {
 230                         DBG("wf: cpuvcp partition (%02x) not found\n",
 231                             SMU_SDB_CPUVCP_ID);
 232                         goto fail;
 233                 }
 234         } else if (of_node_is_type(node, "voltage-sensor") &&
 235                    !strcmp(l, "CPU Voltage")) {
 236                 ads->sens.ops = &smu_cpuvolt_ops;
 237                 ads->sens.name = "cpu-voltage";
 238                 if (cpuvcp == NULL) {
 239                         DBG("wf: cpuvcp partition (%02x) not found\n",
 240                             SMU_SDB_CPUVCP_ID);
 241                         goto fail;
 242                 }
 243         } else if (of_node_is_type(node, "power-sensor") &&
 244                    !strcmp(l, "Slots Power")) {
 245                 ads->sens.ops = &smu_slotspow_ops;
 246                 ads->sens.name = "slots-power";
 247                 if (slotspow == NULL) {
 248                         DBG("wf: slotspow partition (%02x) not found\n",
 249                             SMU_SDB_SLOTSPOW_ID);
 250                         goto fail;
 251                 }
 252         } else
 253                 goto fail;
 254 
 255         v = of_get_property(node, "reg", NULL);
 256         if (v == NULL)
 257                 goto fail;
 258         ads->reg = *v;
 259 
 260         if (wf_register_sensor(&ads->sens))
 261                 goto fail;
 262         return ads;
 263  fail:
 264         kfree(ads);
 265         return NULL;
 266 }
 267 
 268 /*
 269  * SMU Power combo sensor object
 270  */
 271 
 272 struct smu_cpu_power_sensor {
 273         struct list_head        link;
 274         struct wf_sensor        *volts;
 275         struct wf_sensor        *amps;
 276         int                     fake_volts : 1;
 277         int                     quadratic : 1;
 278         struct wf_sensor        sens;
 279 };
 280 #define to_smu_cpu_power(c) container_of(c, struct smu_cpu_power_sensor, sens)
 281 
 282 static struct smu_cpu_power_sensor *smu_cpu_power;
 283 
 284 static void smu_cpu_power_release(struct wf_sensor *sr)
 285 {
 286         struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
 287 
 288         if (pow->volts)
 289                 wf_put_sensor(pow->volts);
 290         if (pow->amps)
 291                 wf_put_sensor(pow->amps);
 292         kfree(pow);
 293 }
 294 
 295 static int smu_cpu_power_get(struct wf_sensor *sr, s32 *value)
 296 {
 297         struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
 298         s32 volts, amps, power;
 299         u64 tmps, tmpa, tmpb;
 300         int rc;
 301 
 302         rc = pow->amps->ops->get_value(pow->amps, &amps);
 303         if (rc)
 304                 return rc;
 305 
 306         if (pow->fake_volts) {
 307                 *value = amps * 12 - 0x30000;
 308                 return 0;
 309         }
 310 
 311         rc = pow->volts->ops->get_value(pow->volts, &volts);
 312         if (rc)
 313                 return rc;
 314 
 315         power = (s32)((((u64)volts) * ((u64)amps)) >> 16);
 316         if (!pow->quadratic) {
 317                 *value = power;
 318                 return 0;
 319         }
 320         tmps = (((u64)power) * ((u64)power)) >> 16;
 321         tmpa = ((u64)cpuvcp->power_quads[0]) * tmps;
 322         tmpb = ((u64)cpuvcp->power_quads[1]) * ((u64)power);
 323         *value = (tmpa >> 28) + (tmpb >> 28) + (cpuvcp->power_quads[2] >> 12);
 324 
 325         return 0;
 326 }
 327 
 328 static const struct wf_sensor_ops smu_cpu_power_ops = {
 329         .get_value      = smu_cpu_power_get,
 330         .release        = smu_cpu_power_release,
 331         .owner          = THIS_MODULE,
 332 };
 333 
 334 
 335 static struct smu_cpu_power_sensor *
 336 smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
 337 {
 338         struct smu_cpu_power_sensor *pow;
 339 
 340         pow = kmalloc(sizeof(struct smu_cpu_power_sensor), GFP_KERNEL);
 341         if (pow == NULL)
 342                 return NULL;
 343         pow->sens.ops = &smu_cpu_power_ops;
 344         pow->sens.name = "cpu-power";
 345 
 346         wf_get_sensor(volts);
 347         pow->volts = volts;
 348         wf_get_sensor(amps);
 349         pow->amps = amps;
 350 
 351         /* Some early machines need a faked voltage */
 352         if (debugswitches && ((*debugswitches) & 0x80)) {
 353                 printk(KERN_INFO "windfarm: CPU Power sensor using faked"
 354                        " voltage !\n");
 355                 pow->fake_volts = 1;
 356         } else
 357                 pow->fake_volts = 0;
 358 
 359         /* Try to use quadratic transforms on PowerMac8,1 and 9,1 for now,
 360          * I yet have to figure out what's up with 8,2 and will have to
 361          * adjust for later, unless we can 100% trust the SDB partition...
 362          */
 363         if ((of_machine_is_compatible("PowerMac8,1") ||
 364              of_machine_is_compatible("PowerMac8,2") ||
 365              of_machine_is_compatible("PowerMac9,1")) &&
 366             cpuvcp_version >= 2) {
 367                 pow->quadratic = 1;
 368                 DBG("windfarm: CPU Power using quadratic transform\n");
 369         } else
 370                 pow->quadratic = 0;
 371 
 372         if (wf_register_sensor(&pow->sens))
 373                 goto fail;
 374         return pow;
 375  fail:
 376         kfree(pow);
 377         return NULL;
 378 }
 379 
 380 static void smu_fetch_param_partitions(void)
 381 {
 382         const struct smu_sdbp_header *hdr;
 383 
 384         /* Get CPU voltage/current/power calibration data */
 385         hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL);
 386         if (hdr != NULL) {
 387                 cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1];
 388                 /* Keep version around */
 389                 cpuvcp_version = hdr->version;
 390         }
 391 
 392         /* Get CPU diode calibration data */
 393         hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL);
 394         if (hdr != NULL)
 395                 cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1];
 396 
 397         /* Get slots power calibration data if any */
 398         hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL);
 399         if (hdr != NULL)
 400                 slotspow = (struct smu_sdbp_slotspow *)&hdr[1];
 401 
 402         /* Get debug switches if any */
 403         hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL);
 404         if (hdr != NULL)
 405                 debugswitches = (u8 *)&hdr[1];
 406 }
 407 
 408 static int __init smu_sensors_init(void)
 409 {
 410         struct device_node *smu, *sensors, *s;
 411         struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL;
 412 
 413         if (!smu_present())
 414                 return -ENODEV;
 415 
 416         /* Get parameters partitions */
 417         smu_fetch_param_partitions();
 418 
 419         smu = of_find_node_by_type(NULL, "smu");
 420         if (smu == NULL)
 421                 return -ENODEV;
 422 
 423         /* Look for sensors subdir */
 424         for (sensors = NULL;
 425              (sensors = of_get_next_child(smu, sensors)) != NULL;)
 426                 if (of_node_name_eq(sensors, "sensors"))
 427                         break;
 428 
 429         of_node_put(smu);
 430 
 431         /* Create basic sensors */
 432         for (s = NULL;
 433              sensors && (s = of_get_next_child(sensors, s)) != NULL;) {
 434                 struct smu_ad_sensor *ads;
 435 
 436                 ads = smu_ads_create(s);
 437                 if (ads == NULL)
 438                         continue;
 439                 list_add(&ads->link, &smu_ads);
 440                 /* keep track of cpu voltage & current */
 441                 if (!strcmp(ads->sens.name, "cpu-voltage"))
 442                         volt_sensor = ads;
 443                 else if (!strcmp(ads->sens.name, "cpu-current"))
 444                         curr_sensor = ads;
 445         }
 446 
 447         of_node_put(sensors);
 448 
 449         /* Create CPU power sensor if possible */
 450         if (volt_sensor && curr_sensor)
 451                 smu_cpu_power = smu_cpu_power_create(&volt_sensor->sens,
 452                                                      &curr_sensor->sens);
 453 
 454         return 0;
 455 }
 456 
 457 static void __exit smu_sensors_exit(void)
 458 {
 459         struct smu_ad_sensor *ads;
 460 
 461         /* dispose of power sensor */
 462         if (smu_cpu_power)
 463                 wf_unregister_sensor(&smu_cpu_power->sens);
 464 
 465         /* dispose of basic sensors */
 466         while (!list_empty(&smu_ads)) {
 467                 ads = list_entry(smu_ads.next, struct smu_ad_sensor, link);
 468                 list_del(&ads->link);
 469                 wf_unregister_sensor(&ads->sens);
 470         }
 471 }
 472 
 473 
 474 module_init(smu_sensors_init);
 475 module_exit(smu_sensors_exit);
 476 
 477 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 478 MODULE_DESCRIPTION("SMU sensor objects for PowerMacs thermal control");
 479 MODULE_LICENSE("GPL");
 480 

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