linux/drivers/macintosh/windfarm_fcu_controls.c
<<
>>
Prefs
   1/*
   2 * Windfarm PowerMac thermal control. FCU fan control
   3 *
   4 * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
   5 *
   6 * Released under the term of the GNU GPL v2.
   7 */
   8#undef DEBUG
   9
  10#include <linux/types.h>
  11#include <linux/errno.h>
  12#include <linux/kernel.h>
  13#include <linux/delay.h>
  14#include <linux/slab.h>
  15#include <linux/init.h>
  16#include <linux/wait.h>
  17#include <linux/i2c.h>
  18#include <asm/prom.h>
  19#include <asm/machdep.h>
  20#include <asm/io.h>
  21#include <asm/sections.h>
  22
  23#include "windfarm.h"
  24#include "windfarm_mpu.h"
  25
  26#define VERSION "1.0"
  27
  28#ifdef DEBUG
  29#define DBG(args...)    printk(args)
  30#else
  31#define DBG(args...)    do { } while(0)
  32#endif
  33
  34/*
  35 * This option is "weird" :) Basically, if you define this to 1
  36 * the control loop for the RPMs fans (not PWMs) will apply the
  37 * correction factor obtained from the PID to the actual RPM
  38 * speed read from the FCU.
  39 *
  40 * If you define the below constant to 0, then it will be
  41 * applied to the setpoint RPM speed, that is basically the
  42 * speed we proviously "asked" for.
  43 *
  44 * I'm using 0 for now which is what therm_pm72 used to do and
  45 * what Darwin -apparently- does based on observed behaviour.
  46 */
  47#define RPM_PID_USE_ACTUAL_SPEED        0
  48
  49/* Default min/max for pumps */
  50#define CPU_PUMP_OUTPUT_MAX             3200
  51#define CPU_PUMP_OUTPUT_MIN             1250
  52
  53#define FCU_FAN_RPM             0
  54#define FCU_FAN_PWM             1
  55
  56struct wf_fcu_priv {
  57        struct kref             ref;
  58        struct i2c_client       *i2c;
  59        struct mutex            lock;
  60        struct list_head        fan_list;
  61        int                     rpm_shift;
  62};
  63
  64struct wf_fcu_fan {
  65        struct list_head        link;
  66        int                     id;
  67        s32                     min, max, target;
  68        struct wf_fcu_priv      *fcu_priv;
  69        struct wf_control       ctrl;
  70};
  71
  72static void wf_fcu_release(struct kref *ref)
  73{
  74        struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref);
  75
  76        kfree(pv);
  77}
  78
  79static void wf_fcu_fan_release(struct wf_control *ct)
  80{
  81        struct wf_fcu_fan *fan = ct->priv;
  82
  83        kref_put(&fan->fcu_priv->ref, wf_fcu_release);
  84        kfree(fan);
  85}
  86
  87static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg,
  88                           unsigned char *buf, int nb)
  89{
  90        int tries, nr, nw;
  91
  92        mutex_lock(&pv->lock);
  93
  94        buf[0] = reg;
  95        tries = 0;
  96        for (;;) {
  97                nw = i2c_master_send(pv->i2c, buf, 1);
  98                if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
  99                        break;
 100                msleep(10);
 101                ++tries;
 102        }
 103        if (nw <= 0) {
 104                pr_err("Failure writing address to FCU: %d", nw);
 105                nr = nw;
 106                goto bail;
 107        }
 108        tries = 0;
 109        for (;;) {
 110                nr = i2c_master_recv(pv->i2c, buf, nb);
 111                if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
 112                        break;
 113                msleep(10);
 114                ++tries;
 115        }
 116        if (nr <= 0)
 117                pr_err("wf_fcu: Failure reading data from FCU: %d", nw);
 118 bail:
 119        mutex_unlock(&pv->lock);
 120        return nr;
 121}
 122
 123static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg,
 124                            const unsigned char *ptr, int nb)
 125{
 126        int tries, nw;
 127        unsigned char buf[16];
 128
 129        buf[0] = reg;
 130        memcpy(buf+1, ptr, nb);
 131        ++nb;
 132        tries = 0;
 133        for (;;) {
 134                nw = i2c_master_send(pv->i2c, buf, nb);
 135                if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
 136                        break;
 137                msleep(10);
 138                ++tries;
 139        }
 140        if (nw < 0)
 141                pr_err("wf_fcu: Failure writing to FCU: %d", nw);
 142        return nw;
 143}
 144
 145static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value)
 146{
 147        struct wf_fcu_fan *fan = ct->priv;
 148        struct wf_fcu_priv *pv = fan->fcu_priv;
 149        int rc, shift = pv->rpm_shift;
 150        unsigned char buf[2];
 151
 152        if (value < fan->min)
 153                value = fan->min;
 154        if (value > fan->max)
 155                value = fan->max;
 156
 157        fan->target = value;
 158
 159        buf[0] = value >> (8 - shift);
 160        buf[1] = value << shift;
 161        rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2);
 162        if (rc < 0)
 163                return -EIO;
 164        return 0;
 165}
 166
 167static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value)
 168{
 169        struct wf_fcu_fan *fan = ct->priv;
 170        struct wf_fcu_priv *pv = fan->fcu_priv;
 171        int rc, reg_base, shift = pv->rpm_shift;
 172        unsigned char failure;
 173        unsigned char active;
 174        unsigned char buf[2];
 175
 176        rc = wf_fcu_read_reg(pv, 0xb, &failure, 1);
 177        if (rc != 1)
 178                return -EIO;
 179        if ((failure & (1 << fan->id)) != 0)
 180                return -EFAULT;
 181        rc = wf_fcu_read_reg(pv, 0xd, &active, 1);
 182        if (rc != 1)
 183                return -EIO;
 184        if ((active & (1 << fan->id)) == 0)
 185                return -ENXIO;
 186
 187        /* Programmed value or real current speed */
 188#if RPM_PID_USE_ACTUAL_SPEED
 189        reg_base = 0x11;
 190#else
 191        reg_base = 0x10;
 192#endif
 193        rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2);
 194        if (rc != 2)
 195                return -EIO;
 196
 197        *value = (buf[0] << (8 - shift)) | buf[1] >> shift;
 198
 199        return 0;
 200}
 201
 202static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value)
 203{
 204        struct wf_fcu_fan *fan = ct->priv;
 205        struct wf_fcu_priv *pv = fan->fcu_priv;
 206        unsigned char buf[2];
 207        int rc;
 208
 209        if (value < fan->min)
 210                value = fan->min;
 211        if (value > fan->max)
 212                value = fan->max;
 213
 214        fan->target = value;
 215
 216        value = (value * 2559) / 1000;
 217        buf[0] = value;
 218        rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1);
 219        if (rc < 0)
 220                return -EIO;
 221        return 0;
 222}
 223
 224static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value)
 225{
 226        struct wf_fcu_fan *fan = ct->priv;
 227        struct wf_fcu_priv *pv = fan->fcu_priv;
 228        unsigned char failure;
 229        unsigned char active;
 230        unsigned char buf[2];
 231        int rc;
 232
 233        rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1);
 234        if (rc != 1)
 235                return -EIO;
 236        if ((failure & (1 << fan->id)) != 0)
 237                return -EFAULT;
 238        rc = wf_fcu_read_reg(pv, 0x2d, &active, 1);
 239        if (rc != 1)
 240                return -EIO;
 241        if ((active & (1 << fan->id)) == 0)
 242                return -ENXIO;
 243
 244        rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1);
 245        if (rc != 1)
 246                return -EIO;
 247
 248        *value = (((s32)buf[0]) * 1000) / 2559;
 249
 250        return 0;
 251}
 252
 253static s32 wf_fcu_fan_min(struct wf_control *ct)
 254{
 255        struct wf_fcu_fan *fan = ct->priv;
 256
 257        return fan->min;
 258}
 259
 260static s32 wf_fcu_fan_max(struct wf_control *ct)
 261{
 262        struct wf_fcu_fan *fan = ct->priv;
 263
 264        return fan->max;
 265}
 266
 267static const struct wf_control_ops wf_fcu_fan_rpm_ops = {
 268        .set_value      = wf_fcu_fan_set_rpm,
 269        .get_value      = wf_fcu_fan_get_rpm,
 270        .get_min        = wf_fcu_fan_min,
 271        .get_max        = wf_fcu_fan_max,
 272        .release        = wf_fcu_fan_release,
 273        .owner          = THIS_MODULE,
 274};
 275
 276static const struct wf_control_ops wf_fcu_fan_pwm_ops = {
 277        .set_value      = wf_fcu_fan_set_pwm,
 278        .get_value      = wf_fcu_fan_get_pwm,
 279        .get_min        = wf_fcu_fan_min,
 280        .get_max        = wf_fcu_fan_max,
 281        .release        = wf_fcu_fan_release,
 282        .owner          = THIS_MODULE,
 283};
 284
 285static void wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan)
 286{
 287        const struct mpu_data *mpu = wf_get_mpu(0);
 288        u16 pump_min = 0, pump_max = 0xffff;
 289        u16 tmp[4];
 290
 291        /* Try to fetch pumps min/max infos from eeprom */
 292        if (mpu) {
 293                memcpy(&tmp, mpu->processor_part_num, 8);
 294                if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
 295                        pump_min = max(pump_min, tmp[0]);
 296                        pump_max = min(pump_max, tmp[1]);
 297                }
 298                if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
 299                        pump_min = max(pump_min, tmp[2]);
 300                        pump_max = min(pump_max, tmp[3]);
 301                }
 302        }
 303
 304        /* Double check the values, this _IS_ needed as the EEPROM on
 305         * some dual 2.5Ghz G5s seem, at least, to have both min & max
 306         * same to the same value ... (grrrr)
 307         */
 308        if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
 309                pump_min = CPU_PUMP_OUTPUT_MIN;
 310                pump_max = CPU_PUMP_OUTPUT_MAX;
 311        }
 312
 313        fan->min = pump_min;
 314        fan->max = pump_max;
 315
 316        DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n",
 317            fan->ctrl.name, pump_min, pump_max);
 318}
 319
 320static void wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan)
 321{
 322        struct wf_fcu_priv *pv = fan->fcu_priv;
 323        const struct mpu_data *mpu0 = wf_get_mpu(0);
 324        const struct mpu_data *mpu1 = wf_get_mpu(1);
 325
 326        /* Default */
 327        fan->min = 2400 >> pv->rpm_shift;
 328        fan->max = 56000 >> pv->rpm_shift;
 329
 330        /* CPU fans have min/max in MPU */
 331        if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) {
 332                fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan);
 333                fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan);
 334                goto bail;
 335        }
 336        if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) {
 337                fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan);
 338                fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan);
 339                goto bail;
 340        }
 341        if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) {
 342                fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan);
 343                fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan);
 344                goto bail;
 345        }
 346        if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) {
 347                fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan);
 348                fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan);
 349                goto bail;
 350        }
 351        /* Rackmac variants, we just use mpu0 intake */
 352        if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) {
 353                fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan);
 354                fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan);
 355                goto bail;
 356        }
 357 bail:
 358        DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n",
 359            fan->ctrl.name, fan->min, fan->max);
 360}
 361
 362static void wf_fcu_add_fan(struct wf_fcu_priv *pv, const char *name,
 363                           int type, int id)
 364{
 365        struct wf_fcu_fan *fan;
 366
 367        fan = kzalloc(sizeof(*fan), GFP_KERNEL);
 368        if (!fan)
 369                return;
 370        fan->fcu_priv = pv;
 371        fan->id = id;
 372        fan->ctrl.name = name;
 373        fan->ctrl.priv = fan;
 374
 375        /* min/max is oddball but the code comes from
 376         * therm_pm72 which seems to work so ...
 377         */
 378        if (type == FCU_FAN_RPM) {
 379                if (!strncmp(name, "cpu-pump", strlen("cpu-pump")))
 380                        wf_fcu_get_pump_minmax(fan);
 381                else
 382                        wf_fcu_get_rpmfan_minmax(fan);
 383                fan->ctrl.type = WF_CONTROL_RPM_FAN;
 384                fan->ctrl.ops = &wf_fcu_fan_rpm_ops;
 385        } else {
 386                fan->min = 10;
 387                fan->max = 100;
 388                fan->ctrl.type = WF_CONTROL_PWM_FAN;
 389                fan->ctrl.ops = &wf_fcu_fan_pwm_ops;
 390        }
 391
 392        if (wf_register_control(&fan->ctrl)) {
 393                pr_err("wf_fcu: Failed to register fan %s\n", name);
 394                kfree(fan);
 395                return;
 396        }
 397        list_add(&fan->link, &pv->fan_list);
 398        kref_get(&pv->ref);
 399}
 400
 401static void wf_fcu_lookup_fans(struct wf_fcu_priv *pv)
 402{
 403        /* Translation of device-tree location properties to
 404         * windfarm fan names
 405         */
 406        static const struct {
 407                const char *dt_name;    /* Device-tree name */
 408                const char *ct_name;    /* Control name */
 409        } loc_trans[] = {
 410                { "BACKSIDE",           "backside-fan",         },
 411                { "SYS CTRLR FAN",      "backside-fan",         },
 412                { "DRIVE BAY",          "drive-bay-fan",        },
 413                { "SLOT",               "slots-fan",            },
 414                { "PCI FAN",            "slots-fan",            },
 415                { "CPU A INTAKE",       "cpu-front-fan-0",      },
 416                { "CPU A EXHAUST",      "cpu-rear-fan-0",       },
 417                { "CPU B INTAKE",       "cpu-front-fan-1",      },
 418                { "CPU B EXHAUST",      "cpu-rear-fan-1",       },
 419                { "CPU A PUMP",         "cpu-pump-0",           },
 420                { "CPU B PUMP",         "cpu-pump-1",           },
 421                { "CPU A 1",            "cpu-fan-a-0",          },
 422                { "CPU A 2",            "cpu-fan-b-0",          },
 423                { "CPU A 3",            "cpu-fan-c-0",          },
 424                { "CPU B 1",            "cpu-fan-a-1",          },
 425                { "CPU B 2",            "cpu-fan-b-1",          },
 426                { "CPU B 3",            "cpu-fan-c-1",          },
 427        };
 428        struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node;
 429        int i;
 430
 431        DBG("Looking up FCU controls in device-tree...\n");
 432
 433        while ((np = of_get_next_child(fcu, np)) != NULL) {
 434                int id, type = -1;
 435                const char *loc;
 436                const char *name;
 437                const u32 *reg;
 438
 439                DBG(" control: %s, type: %s\n", np->name, np->type);
 440
 441                /* Detect control type */
 442                if (!strcmp(np->type, "fan-rpm-control") ||
 443                    !strcmp(np->type, "fan-rpm"))
 444                        type = FCU_FAN_RPM;
 445                if (!strcmp(np->type, "fan-pwm-control") ||
 446                    !strcmp(np->type, "fan-pwm"))
 447                        type = FCU_FAN_PWM;
 448                /* Only care about fans for now */
 449                if (type == -1)
 450                        continue;
 451
 452                /* Lookup for a matching location */
 453                loc = of_get_property(np, "location", NULL);
 454                reg = of_get_property(np, "reg", NULL);
 455                if (loc == NULL || reg == NULL)
 456                        continue;
 457                DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
 458
 459                for (i = 0; i < ARRAY_SIZE(loc_trans); i++) {
 460                        if (strncmp(loc, loc_trans[i].dt_name,
 461                                    strlen(loc_trans[i].dt_name)))
 462                                continue;
 463                        name = loc_trans[i].ct_name;
 464
 465                        DBG(" location match, name: %s\n", name);
 466
 467                        if (type == FCU_FAN_RPM)
 468                                id = ((*reg) - 0x10) / 2;
 469                        else
 470                                id = ((*reg) - 0x30) / 2;
 471                        if (id > 7) {
 472                                pr_warning("wf_fcu: Can't parse "
 473                                       "fan ID in device-tree for %s\n",
 474                                           np->full_name);
 475                                break;
 476                        }
 477                        wf_fcu_add_fan(pv, name, type, id);
 478                        break;
 479                }
 480        }
 481}
 482
 483static void wf_fcu_default_fans(struct wf_fcu_priv *pv)
 484{
 485        /* We only support the default fans for PowerMac7,2 */
 486        if (!of_machine_is_compatible("PowerMac7,2"))
 487                return;
 488
 489        wf_fcu_add_fan(pv, "backside-fan",      FCU_FAN_PWM, 1);
 490        wf_fcu_add_fan(pv, "drive-bay-fan",     FCU_FAN_RPM, 2);
 491        wf_fcu_add_fan(pv, "slots-fan",         FCU_FAN_PWM, 2);
 492        wf_fcu_add_fan(pv, "cpu-front-fan-0",   FCU_FAN_RPM, 3);
 493        wf_fcu_add_fan(pv, "cpu-rear-fan-0",    FCU_FAN_RPM, 4);
 494        wf_fcu_add_fan(pv, "cpu-front-fan-1",   FCU_FAN_RPM, 5);
 495        wf_fcu_add_fan(pv, "cpu-rear-fan-1",    FCU_FAN_RPM, 6);
 496}
 497
 498static int wf_fcu_init_chip(struct wf_fcu_priv *pv)
 499{
 500        unsigned char buf = 0xff;
 501        int rc;
 502
 503        rc = wf_fcu_write_reg(pv, 0xe, &buf, 1);
 504        if (rc < 0)
 505                return -EIO;
 506        rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1);
 507        if (rc < 0)
 508                return -EIO;
 509        rc = wf_fcu_read_reg(pv, 0, &buf, 1);
 510        if (rc < 0)
 511                return -EIO;
 512        pv->rpm_shift = (buf == 1) ? 2 : 3;
 513
 514        pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n",
 515                 pv->rpm_shift);
 516
 517        return 0;
 518}
 519
 520static int wf_fcu_probe(struct i2c_client *client,
 521                        const struct i2c_device_id *id)
 522{
 523        struct wf_fcu_priv *pv;
 524
 525        pv = kzalloc(sizeof(*pv), GFP_KERNEL);
 526        if (!pv)
 527                return -ENOMEM;
 528
 529        kref_init(&pv->ref);
 530        mutex_init(&pv->lock);
 531        INIT_LIST_HEAD(&pv->fan_list);
 532        pv->i2c = client;
 533
 534        /*
 535         * First we must start the FCU which will query the
 536         * shift value to apply to RPMs
 537         */
 538        if (wf_fcu_init_chip(pv)) {
 539                pr_err("wf_fcu: Initialization failed !\n");
 540                kfree(pv);
 541                return -ENXIO;
 542        }
 543
 544        /* First lookup fans in the device-tree */
 545        wf_fcu_lookup_fans(pv);
 546
 547        /*
 548         * Older machines don't have the device-tree entries
 549         * we are looking for, just hard code the list
 550         */
 551        if (list_empty(&pv->fan_list))
 552                wf_fcu_default_fans(pv);
 553
 554        /* Still no fans ? FAIL */
 555        if (list_empty(&pv->fan_list)) {
 556                pr_err("wf_fcu: Failed to find fans for your machine\n");
 557                kfree(pv);
 558                return -ENODEV;
 559        }
 560
 561        dev_set_drvdata(&client->dev, pv);
 562
 563        return 0;
 564}
 565
 566static int wf_fcu_remove(struct i2c_client *client)
 567{
 568        struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev);
 569        struct wf_fcu_fan *fan;
 570
 571        while (!list_empty(&pv->fan_list)) {
 572                fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link);
 573                list_del(&fan->link);
 574                wf_unregister_control(&fan->ctrl);
 575        }
 576        kref_put(&pv->ref, wf_fcu_release);
 577        return 0;
 578}
 579
 580static const struct i2c_device_id wf_fcu_id[] = {
 581        { "MAC,fcu", 0 },
 582        { }
 583};
 584MODULE_DEVICE_TABLE(i2c, wf_fcu_id);
 585
 586static struct i2c_driver wf_fcu_driver = {
 587        .driver = {
 588                .name   = "wf_fcu",
 589        },
 590        .probe          = wf_fcu_probe,
 591        .remove         = wf_fcu_remove,
 592        .id_table       = wf_fcu_id,
 593};
 594
 595module_i2c_driver(wf_fcu_driver);
 596
 597MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 598MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control");
 599MODULE_LICENSE("GPL");
 600
 601
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.