0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/types.h>
0010 #include <linux/errno.h>
0011 #include <linux/kernel.h>
0012 #include <linux/delay.h>
0013 #include <linux/slab.h>
0014 #include <linux/init.h>
0015 #include <linux/wait.h>
0016 #include <linux/completion.h>
0017 #include <linux/of.h>
0018
0019 #include <asm/machdep.h>
0020 #include <asm/io.h>
0021 #include <asm/sections.h>
0022 #include <asm/smu.h>
0023
0024 #include "windfarm.h"
0025
0026 #define VERSION "0.2"
0027
0028 #undef DEBUG
0029
0030 #ifdef DEBUG
0031 #define DBG(args...) printk(args)
0032 #else
0033 #define DBG(args...) do { } while(0)
0034 #endif
0035
0036
0037
0038
0039
0040 static struct smu_sdbp_cpuvcp *cpuvcp;
0041 static int cpuvcp_version;
0042 static struct smu_sdbp_cpudiode *cpudiode;
0043 static struct smu_sdbp_slotspow *slotspow;
0044 static u8 *debugswitches;
0045
0046
0047
0048
0049
0050 static LIST_HEAD(smu_ads);
0051
0052 struct smu_ad_sensor {
0053 struct list_head link;
0054 u32 reg;
0055 struct wf_sensor sens;
0056 };
0057 #define to_smu_ads(c) container_of(c, struct smu_ad_sensor, sens)
0058
0059 static void smu_ads_release(struct wf_sensor *sr)
0060 {
0061 struct smu_ad_sensor *ads = to_smu_ads(sr);
0062
0063 kfree(ads);
0064 }
0065
0066 static int smu_read_adc(u8 id, s32 *value)
0067 {
0068 struct smu_simple_cmd cmd;
0069 DECLARE_COMPLETION_ONSTACK(comp);
0070 int rc;
0071
0072 rc = smu_queue_simple(&cmd, SMU_CMD_READ_ADC, 1,
0073 smu_done_complete, &comp, id);
0074 if (rc)
0075 return rc;
0076 wait_for_completion(&comp);
0077 if (cmd.cmd.status != 0)
0078 return cmd.cmd.status;
0079 if (cmd.cmd.reply_len != 2) {
0080 printk(KERN_ERR "winfarm: read ADC 0x%x returned %d bytes !\n",
0081 id, cmd.cmd.reply_len);
0082 return -EIO;
0083 }
0084 *value = *((u16 *)cmd.buffer);
0085 return 0;
0086 }
0087
0088 static int smu_cputemp_get(struct wf_sensor *sr, s32 *value)
0089 {
0090 struct smu_ad_sensor *ads = to_smu_ads(sr);
0091 int rc;
0092 s32 val;
0093 s64 scaled;
0094
0095 rc = smu_read_adc(ads->reg, &val);
0096 if (rc) {
0097 printk(KERN_ERR "windfarm: read CPU temp failed, err %d\n",
0098 rc);
0099 return rc;
0100 }
0101
0102
0103 scaled = (s64)(((u64)val) * (u64)cpudiode->m_value);
0104 scaled >>= 3;
0105 scaled += ((s64)cpudiode->b_value) << 9;
0106 *value = (s32)(scaled << 1);
0107
0108 return 0;
0109 }
0110
0111 static int smu_cpuamp_get(struct wf_sensor *sr, s32 *value)
0112 {
0113 struct smu_ad_sensor *ads = to_smu_ads(sr);
0114 s32 val, scaled;
0115 int rc;
0116
0117 rc = smu_read_adc(ads->reg, &val);
0118 if (rc) {
0119 printk(KERN_ERR "windfarm: read CPU current failed, err %d\n",
0120 rc);
0121 return rc;
0122 }
0123
0124
0125 scaled = (s32)(val * (u32)cpuvcp->curr_scale);
0126 scaled += (s32)cpuvcp->curr_offset;
0127 *value = scaled << 4;
0128
0129 return 0;
0130 }
0131
0132 static int smu_cpuvolt_get(struct wf_sensor *sr, s32 *value)
0133 {
0134 struct smu_ad_sensor *ads = to_smu_ads(sr);
0135 s32 val, scaled;
0136 int rc;
0137
0138 rc = smu_read_adc(ads->reg, &val);
0139 if (rc) {
0140 printk(KERN_ERR "windfarm: read CPU voltage failed, err %d\n",
0141 rc);
0142 return rc;
0143 }
0144
0145
0146 scaled = (s32)(val * (u32)cpuvcp->volt_scale);
0147 scaled += (s32)cpuvcp->volt_offset;
0148 *value = scaled << 4;
0149
0150 return 0;
0151 }
0152
0153 static int smu_slotspow_get(struct wf_sensor *sr, s32 *value)
0154 {
0155 struct smu_ad_sensor *ads = to_smu_ads(sr);
0156 s32 val, scaled;
0157 int rc;
0158
0159 rc = smu_read_adc(ads->reg, &val);
0160 if (rc) {
0161 printk(KERN_ERR "windfarm: read slots power failed, err %d\n",
0162 rc);
0163 return rc;
0164 }
0165
0166
0167 scaled = (s32)(val * (u32)slotspow->pow_scale);
0168 scaled += (s32)slotspow->pow_offset;
0169 *value = scaled << 4;
0170
0171 return 0;
0172 }
0173
0174
0175 static const struct wf_sensor_ops smu_cputemp_ops = {
0176 .get_value = smu_cputemp_get,
0177 .release = smu_ads_release,
0178 .owner = THIS_MODULE,
0179 };
0180 static const struct wf_sensor_ops smu_cpuamp_ops = {
0181 .get_value = smu_cpuamp_get,
0182 .release = smu_ads_release,
0183 .owner = THIS_MODULE,
0184 };
0185 static const struct wf_sensor_ops smu_cpuvolt_ops = {
0186 .get_value = smu_cpuvolt_get,
0187 .release = smu_ads_release,
0188 .owner = THIS_MODULE,
0189 };
0190 static const struct wf_sensor_ops smu_slotspow_ops = {
0191 .get_value = smu_slotspow_get,
0192 .release = smu_ads_release,
0193 .owner = THIS_MODULE,
0194 };
0195
0196
0197 static struct smu_ad_sensor *smu_ads_create(struct device_node *node)
0198 {
0199 struct smu_ad_sensor *ads;
0200 const char *l;
0201 const u32 *v;
0202
0203 ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL);
0204 if (ads == NULL)
0205 return NULL;
0206 l = of_get_property(node, "location", NULL);
0207 if (l == NULL)
0208 goto fail;
0209
0210
0211
0212
0213
0214
0215
0216
0217 if (of_node_is_type(node, "temp-sensor") &&
0218 !strcmp(l, "CPU T-Diode")) {
0219 ads->sens.ops = &smu_cputemp_ops;
0220 ads->sens.name = "cpu-temp";
0221 if (cpudiode == NULL) {
0222 DBG("wf: cpudiode partition (%02x) not found\n",
0223 SMU_SDB_CPUDIODE_ID);
0224 goto fail;
0225 }
0226 } else if (of_node_is_type(node, "current-sensor") &&
0227 !strcmp(l, "CPU Current")) {
0228 ads->sens.ops = &smu_cpuamp_ops;
0229 ads->sens.name = "cpu-current";
0230 if (cpuvcp == NULL) {
0231 DBG("wf: cpuvcp partition (%02x) not found\n",
0232 SMU_SDB_CPUVCP_ID);
0233 goto fail;
0234 }
0235 } else if (of_node_is_type(node, "voltage-sensor") &&
0236 !strcmp(l, "CPU Voltage")) {
0237 ads->sens.ops = &smu_cpuvolt_ops;
0238 ads->sens.name = "cpu-voltage";
0239 if (cpuvcp == NULL) {
0240 DBG("wf: cpuvcp partition (%02x) not found\n",
0241 SMU_SDB_CPUVCP_ID);
0242 goto fail;
0243 }
0244 } else if (of_node_is_type(node, "power-sensor") &&
0245 !strcmp(l, "Slots Power")) {
0246 ads->sens.ops = &smu_slotspow_ops;
0247 ads->sens.name = "slots-power";
0248 if (slotspow == NULL) {
0249 DBG("wf: slotspow partition (%02x) not found\n",
0250 SMU_SDB_SLOTSPOW_ID);
0251 goto fail;
0252 }
0253 } else
0254 goto fail;
0255
0256 v = of_get_property(node, "reg", NULL);
0257 if (v == NULL)
0258 goto fail;
0259 ads->reg = *v;
0260
0261 if (wf_register_sensor(&ads->sens))
0262 goto fail;
0263 return ads;
0264 fail:
0265 kfree(ads);
0266 return NULL;
0267 }
0268
0269
0270
0271
0272
0273 struct smu_cpu_power_sensor {
0274 struct list_head link;
0275 struct wf_sensor *volts;
0276 struct wf_sensor *amps;
0277 int fake_volts : 1;
0278 int quadratic : 1;
0279 struct wf_sensor sens;
0280 };
0281 #define to_smu_cpu_power(c) container_of(c, struct smu_cpu_power_sensor, sens)
0282
0283 static struct smu_cpu_power_sensor *smu_cpu_power;
0284
0285 static void smu_cpu_power_release(struct wf_sensor *sr)
0286 {
0287 struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
0288
0289 if (pow->volts)
0290 wf_put_sensor(pow->volts);
0291 if (pow->amps)
0292 wf_put_sensor(pow->amps);
0293 kfree(pow);
0294 }
0295
0296 static int smu_cpu_power_get(struct wf_sensor *sr, s32 *value)
0297 {
0298 struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
0299 s32 volts, amps, power;
0300 u64 tmps, tmpa, tmpb;
0301 int rc;
0302
0303 rc = pow->amps->ops->get_value(pow->amps, &s);
0304 if (rc)
0305 return rc;
0306
0307 if (pow->fake_volts) {
0308 *value = amps * 12 - 0x30000;
0309 return 0;
0310 }
0311
0312 rc = pow->volts->ops->get_value(pow->volts, &volts);
0313 if (rc)
0314 return rc;
0315
0316 power = (s32)((((u64)volts) * ((u64)amps)) >> 16);
0317 if (!pow->quadratic) {
0318 *value = power;
0319 return 0;
0320 }
0321 tmps = (((u64)power) * ((u64)power)) >> 16;
0322 tmpa = ((u64)cpuvcp->power_quads[0]) * tmps;
0323 tmpb = ((u64)cpuvcp->power_quads[1]) * ((u64)power);
0324 *value = (tmpa >> 28) + (tmpb >> 28) + (cpuvcp->power_quads[2] >> 12);
0325
0326 return 0;
0327 }
0328
0329 static const struct wf_sensor_ops smu_cpu_power_ops = {
0330 .get_value = smu_cpu_power_get,
0331 .release = smu_cpu_power_release,
0332 .owner = THIS_MODULE,
0333 };
0334
0335
0336 static struct smu_cpu_power_sensor *
0337 smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
0338 {
0339 struct smu_cpu_power_sensor *pow;
0340
0341 pow = kmalloc(sizeof(struct smu_cpu_power_sensor), GFP_KERNEL);
0342 if (pow == NULL)
0343 return NULL;
0344 pow->sens.ops = &smu_cpu_power_ops;
0345 pow->sens.name = "cpu-power";
0346
0347 wf_get_sensor(volts);
0348 pow->volts = volts;
0349 wf_get_sensor(amps);
0350 pow->amps = amps;
0351
0352
0353 if (debugswitches && ((*debugswitches) & 0x80)) {
0354 printk(KERN_INFO "windfarm: CPU Power sensor using faked"
0355 " voltage !\n");
0356 pow->fake_volts = 1;
0357 } else
0358 pow->fake_volts = 0;
0359
0360
0361
0362
0363
0364 if ((of_machine_is_compatible("PowerMac8,1") ||
0365 of_machine_is_compatible("PowerMac8,2") ||
0366 of_machine_is_compatible("PowerMac9,1")) &&
0367 cpuvcp_version >= 2) {
0368 pow->quadratic = 1;
0369 DBG("windfarm: CPU Power using quadratic transform\n");
0370 } else
0371 pow->quadratic = 0;
0372
0373 if (wf_register_sensor(&pow->sens))
0374 goto fail;
0375 return pow;
0376 fail:
0377 kfree(pow);
0378 return NULL;
0379 }
0380
0381 static void smu_fetch_param_partitions(void)
0382 {
0383 const struct smu_sdbp_header *hdr;
0384
0385
0386 hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL);
0387 if (hdr != NULL) {
0388 cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1];
0389
0390 cpuvcp_version = hdr->version;
0391 }
0392
0393
0394 hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL);
0395 if (hdr != NULL)
0396 cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1];
0397
0398
0399 hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL);
0400 if (hdr != NULL)
0401 slotspow = (struct smu_sdbp_slotspow *)&hdr[1];
0402
0403
0404 hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL);
0405 if (hdr != NULL)
0406 debugswitches = (u8 *)&hdr[1];
0407 }
0408
0409 static int __init smu_sensors_init(void)
0410 {
0411 struct device_node *smu, *sensors, *s;
0412 struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL;
0413
0414 if (!smu_present())
0415 return -ENODEV;
0416
0417
0418 smu_fetch_param_partitions();
0419
0420 smu = of_find_node_by_type(NULL, "smu");
0421 if (smu == NULL)
0422 return -ENODEV;
0423
0424
0425 for_each_child_of_node(smu, sensors)
0426 if (of_node_name_eq(sensors, "sensors"))
0427 break;
0428
0429 of_node_put(smu);
0430
0431
0432 for (s = NULL;
0433 sensors && (s = of_get_next_child(sensors, s)) != NULL;) {
0434 struct smu_ad_sensor *ads;
0435
0436 ads = smu_ads_create(s);
0437 if (ads == NULL)
0438 continue;
0439 list_add(&ads->link, &smu_ads);
0440
0441 if (!strcmp(ads->sens.name, "cpu-voltage"))
0442 volt_sensor = ads;
0443 else if (!strcmp(ads->sens.name, "cpu-current"))
0444 curr_sensor = ads;
0445 }
0446
0447 of_node_put(sensors);
0448
0449
0450 if (volt_sensor && curr_sensor)
0451 smu_cpu_power = smu_cpu_power_create(&volt_sensor->sens,
0452 &curr_sensor->sens);
0453
0454 return 0;
0455 }
0456
0457 static void __exit smu_sensors_exit(void)
0458 {
0459 struct smu_ad_sensor *ads;
0460
0461
0462 if (smu_cpu_power)
0463 wf_unregister_sensor(&smu_cpu_power->sens);
0464
0465
0466 while (!list_empty(&smu_ads)) {
0467 ads = list_entry(smu_ads.next, struct smu_ad_sensor, link);
0468 list_del(&ads->link);
0469 wf_unregister_sensor(&ads->sens);
0470 }
0471 }
0472
0473
0474 module_init(smu_sensors_init);
0475 module_exit(smu_sensors_exit);
0476
0477 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
0478 MODULE_DESCRIPTION("SMU sensor objects for PowerMacs thermal control");
0479 MODULE_LICENSE("GPL");
0480