root/drivers/macintosh/windfarm_fcu_controls.c

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

DEFINITIONS

This source file includes following definitions.
  1. wf_fcu_release
  2. wf_fcu_fan_release
  3. wf_fcu_read_reg
  4. wf_fcu_write_reg
  5. wf_fcu_fan_set_rpm
  6. wf_fcu_fan_get_rpm
  7. wf_fcu_fan_set_pwm
  8. wf_fcu_fan_get_pwm
  9. wf_fcu_fan_min
  10. wf_fcu_fan_max
  11. wf_fcu_get_pump_minmax
  12. wf_fcu_get_rpmfan_minmax
  13. wf_fcu_add_fan
  14. wf_fcu_lookup_fans
  15. wf_fcu_default_fans
  16. wf_fcu_init_chip
  17. wf_fcu_probe
  18. wf_fcu_remove

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

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