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