18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Windfarm PowerMac thermal control. SMU based sensors
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
68c2ecf20Sopenharmony_ci *                    <benh@kernel.crashing.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/types.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/wait.h>
168c2ecf20Sopenharmony_ci#include <linux/completion.h>
178c2ecf20Sopenharmony_ci#include <asm/prom.h>
188c2ecf20Sopenharmony_ci#include <asm/machdep.h>
198c2ecf20Sopenharmony_ci#include <asm/io.h>
208c2ecf20Sopenharmony_ci#include <asm/sections.h>
218c2ecf20Sopenharmony_ci#include <asm/smu.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "windfarm.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define VERSION "0.2"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#undef DEBUG
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#ifdef DEBUG
308c2ecf20Sopenharmony_ci#define DBG(args...)	printk(args)
318c2ecf20Sopenharmony_ci#else
328c2ecf20Sopenharmony_ci#define DBG(args...)	do { } while(0)
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Various SMU "partitions" calibration objects for which we
378c2ecf20Sopenharmony_ci * keep pointers here for use by bits & pieces of the driver
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic struct smu_sdbp_cpuvcp *cpuvcp;
408c2ecf20Sopenharmony_cistatic int  cpuvcp_version;
418c2ecf20Sopenharmony_cistatic struct smu_sdbp_cpudiode *cpudiode;
428c2ecf20Sopenharmony_cistatic struct smu_sdbp_slotspow *slotspow;
438c2ecf20Sopenharmony_cistatic u8 *debugswitches;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * SMU basic sensors objects
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic LIST_HEAD(smu_ads);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct smu_ad_sensor {
528c2ecf20Sopenharmony_ci	struct list_head	link;
538c2ecf20Sopenharmony_ci	u32			reg;		/* index in SMU */
548c2ecf20Sopenharmony_ci	struct wf_sensor	sens;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci#define to_smu_ads(c) container_of(c, struct smu_ad_sensor, sens)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void smu_ads_release(struct wf_sensor *sr)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads = to_smu_ads(sr);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	kfree(ads);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int smu_read_adc(u8 id, s32 *value)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct smu_simple_cmd	cmd;
688c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(comp);
698c2ecf20Sopenharmony_ci	int rc;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	rc = smu_queue_simple(&cmd, SMU_CMD_READ_ADC, 1,
728c2ecf20Sopenharmony_ci			      smu_done_complete, &comp, id);
738c2ecf20Sopenharmony_ci	if (rc)
748c2ecf20Sopenharmony_ci		return rc;
758c2ecf20Sopenharmony_ci	wait_for_completion(&comp);
768c2ecf20Sopenharmony_ci	if (cmd.cmd.status != 0)
778c2ecf20Sopenharmony_ci		return cmd.cmd.status;
788c2ecf20Sopenharmony_ci	if (cmd.cmd.reply_len != 2) {
798c2ecf20Sopenharmony_ci		printk(KERN_ERR "winfarm: read ADC 0x%x returned %d bytes !\n",
808c2ecf20Sopenharmony_ci		       id, cmd.cmd.reply_len);
818c2ecf20Sopenharmony_ci		return -EIO;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	*value = *((u16 *)cmd.buffer);
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int smu_cputemp_get(struct wf_sensor *sr, s32 *value)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads = to_smu_ads(sr);
908c2ecf20Sopenharmony_ci	int rc;
918c2ecf20Sopenharmony_ci	s32 val;
928c2ecf20Sopenharmony_ci	s64 scaled;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	rc = smu_read_adc(ads->reg, &val);
958c2ecf20Sopenharmony_ci	if (rc) {
968c2ecf20Sopenharmony_ci		printk(KERN_ERR "windfarm: read CPU temp failed, err %d\n",
978c2ecf20Sopenharmony_ci		       rc);
988c2ecf20Sopenharmony_ci		return rc;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Ok, we have to scale & adjust, taking units into account */
1028c2ecf20Sopenharmony_ci	scaled = (s64)(((u64)val) * (u64)cpudiode->m_value);
1038c2ecf20Sopenharmony_ci	scaled >>= 3;
1048c2ecf20Sopenharmony_ci	scaled += ((s64)cpudiode->b_value) << 9;
1058c2ecf20Sopenharmony_ci	*value = (s32)(scaled << 1);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int smu_cpuamp_get(struct wf_sensor *sr, s32 *value)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads = to_smu_ads(sr);
1138c2ecf20Sopenharmony_ci	s32 val, scaled;
1148c2ecf20Sopenharmony_ci	int rc;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	rc = smu_read_adc(ads->reg, &val);
1178c2ecf20Sopenharmony_ci	if (rc) {
1188c2ecf20Sopenharmony_ci		printk(KERN_ERR "windfarm: read CPU current failed, err %d\n",
1198c2ecf20Sopenharmony_ci		       rc);
1208c2ecf20Sopenharmony_ci		return rc;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Ok, we have to scale & adjust, taking units into account */
1248c2ecf20Sopenharmony_ci	scaled = (s32)(val * (u32)cpuvcp->curr_scale);
1258c2ecf20Sopenharmony_ci	scaled += (s32)cpuvcp->curr_offset;
1268c2ecf20Sopenharmony_ci	*value = scaled << 4;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int smu_cpuvolt_get(struct wf_sensor *sr, s32 *value)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads = to_smu_ads(sr);
1348c2ecf20Sopenharmony_ci	s32 val, scaled;
1358c2ecf20Sopenharmony_ci	int rc;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	rc = smu_read_adc(ads->reg, &val);
1388c2ecf20Sopenharmony_ci	if (rc) {
1398c2ecf20Sopenharmony_ci		printk(KERN_ERR "windfarm: read CPU voltage failed, err %d\n",
1408c2ecf20Sopenharmony_ci		       rc);
1418c2ecf20Sopenharmony_ci		return rc;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Ok, we have to scale & adjust, taking units into account */
1458c2ecf20Sopenharmony_ci	scaled = (s32)(val * (u32)cpuvcp->volt_scale);
1468c2ecf20Sopenharmony_ci	scaled += (s32)cpuvcp->volt_offset;
1478c2ecf20Sopenharmony_ci	*value = scaled << 4;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int smu_slotspow_get(struct wf_sensor *sr, s32 *value)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads = to_smu_ads(sr);
1558c2ecf20Sopenharmony_ci	s32 val, scaled;
1568c2ecf20Sopenharmony_ci	int rc;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	rc = smu_read_adc(ads->reg, &val);
1598c2ecf20Sopenharmony_ci	if (rc) {
1608c2ecf20Sopenharmony_ci		printk(KERN_ERR "windfarm: read slots power failed, err %d\n",
1618c2ecf20Sopenharmony_ci		       rc);
1628c2ecf20Sopenharmony_ci		return rc;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Ok, we have to scale & adjust, taking units into account */
1668c2ecf20Sopenharmony_ci	scaled = (s32)(val * (u32)slotspow->pow_scale);
1678c2ecf20Sopenharmony_ci	scaled += (s32)slotspow->pow_offset;
1688c2ecf20Sopenharmony_ci	*value = scaled << 4;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return 0;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops smu_cputemp_ops = {
1758c2ecf20Sopenharmony_ci	.get_value	= smu_cputemp_get,
1768c2ecf20Sopenharmony_ci	.release	= smu_ads_release,
1778c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops smu_cpuamp_ops = {
1808c2ecf20Sopenharmony_ci	.get_value	= smu_cpuamp_get,
1818c2ecf20Sopenharmony_ci	.release	= smu_ads_release,
1828c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops smu_cpuvolt_ops = {
1858c2ecf20Sopenharmony_ci	.get_value	= smu_cpuvolt_get,
1868c2ecf20Sopenharmony_ci	.release	= smu_ads_release,
1878c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops smu_slotspow_ops = {
1908c2ecf20Sopenharmony_ci	.get_value	= smu_slotspow_get,
1918c2ecf20Sopenharmony_ci	.release	= smu_ads_release,
1928c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1938c2ecf20Sopenharmony_ci};
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic struct smu_ad_sensor *smu_ads_create(struct device_node *node)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads;
1998c2ecf20Sopenharmony_ci	const char *l;
2008c2ecf20Sopenharmony_ci	const u32 *v;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL);
2038c2ecf20Sopenharmony_ci	if (ads == NULL)
2048c2ecf20Sopenharmony_ci		return NULL;
2058c2ecf20Sopenharmony_ci	l = of_get_property(node, "location", NULL);
2068c2ecf20Sopenharmony_ci	if (l == NULL)
2078c2ecf20Sopenharmony_ci		goto fail;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* We currently pick the sensors based on the OF name and location
2108c2ecf20Sopenharmony_ci	 * properties, while Darwin uses the sensor-id's.
2118c2ecf20Sopenharmony_ci	 * The problem with the IDs is that they are model specific while it
2128c2ecf20Sopenharmony_ci	 * looks like apple has been doing a reasonably good job at keeping
2138c2ecf20Sopenharmony_ci	 * the names and locations consistents so I'll stick with the names
2148c2ecf20Sopenharmony_ci	 * and locations for now.
2158c2ecf20Sopenharmony_ci	 */
2168c2ecf20Sopenharmony_ci	if (of_node_is_type(node, "temp-sensor") &&
2178c2ecf20Sopenharmony_ci	    !strcmp(l, "CPU T-Diode")) {
2188c2ecf20Sopenharmony_ci		ads->sens.ops = &smu_cputemp_ops;
2198c2ecf20Sopenharmony_ci		ads->sens.name = "cpu-temp";
2208c2ecf20Sopenharmony_ci		if (cpudiode == NULL) {
2218c2ecf20Sopenharmony_ci			DBG("wf: cpudiode partition (%02x) not found\n",
2228c2ecf20Sopenharmony_ci			    SMU_SDB_CPUDIODE_ID);
2238c2ecf20Sopenharmony_ci			goto fail;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci	} else if (of_node_is_type(node, "current-sensor") &&
2268c2ecf20Sopenharmony_ci		   !strcmp(l, "CPU Current")) {
2278c2ecf20Sopenharmony_ci		ads->sens.ops = &smu_cpuamp_ops;
2288c2ecf20Sopenharmony_ci		ads->sens.name = "cpu-current";
2298c2ecf20Sopenharmony_ci		if (cpuvcp == NULL) {
2308c2ecf20Sopenharmony_ci			DBG("wf: cpuvcp partition (%02x) not found\n",
2318c2ecf20Sopenharmony_ci			    SMU_SDB_CPUVCP_ID);
2328c2ecf20Sopenharmony_ci			goto fail;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci	} else if (of_node_is_type(node, "voltage-sensor") &&
2358c2ecf20Sopenharmony_ci		   !strcmp(l, "CPU Voltage")) {
2368c2ecf20Sopenharmony_ci		ads->sens.ops = &smu_cpuvolt_ops;
2378c2ecf20Sopenharmony_ci		ads->sens.name = "cpu-voltage";
2388c2ecf20Sopenharmony_ci		if (cpuvcp == NULL) {
2398c2ecf20Sopenharmony_ci			DBG("wf: cpuvcp partition (%02x) not found\n",
2408c2ecf20Sopenharmony_ci			    SMU_SDB_CPUVCP_ID);
2418c2ecf20Sopenharmony_ci			goto fail;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci	} else if (of_node_is_type(node, "power-sensor") &&
2448c2ecf20Sopenharmony_ci		   !strcmp(l, "Slots Power")) {
2458c2ecf20Sopenharmony_ci		ads->sens.ops = &smu_slotspow_ops;
2468c2ecf20Sopenharmony_ci		ads->sens.name = "slots-power";
2478c2ecf20Sopenharmony_ci		if (slotspow == NULL) {
2488c2ecf20Sopenharmony_ci			DBG("wf: slotspow partition (%02x) not found\n",
2498c2ecf20Sopenharmony_ci			    SMU_SDB_SLOTSPOW_ID);
2508c2ecf20Sopenharmony_ci			goto fail;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci	} else
2538c2ecf20Sopenharmony_ci		goto fail;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	v = of_get_property(node, "reg", NULL);
2568c2ecf20Sopenharmony_ci	if (v == NULL)
2578c2ecf20Sopenharmony_ci		goto fail;
2588c2ecf20Sopenharmony_ci	ads->reg = *v;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (wf_register_sensor(&ads->sens))
2618c2ecf20Sopenharmony_ci		goto fail;
2628c2ecf20Sopenharmony_ci	return ads;
2638c2ecf20Sopenharmony_ci fail:
2648c2ecf20Sopenharmony_ci	kfree(ads);
2658c2ecf20Sopenharmony_ci	return NULL;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * SMU Power combo sensor object
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistruct smu_cpu_power_sensor {
2738c2ecf20Sopenharmony_ci	struct list_head	link;
2748c2ecf20Sopenharmony_ci	struct wf_sensor	*volts;
2758c2ecf20Sopenharmony_ci	struct wf_sensor	*amps;
2768c2ecf20Sopenharmony_ci	unsigned int		fake_volts : 1;
2778c2ecf20Sopenharmony_ci	unsigned int		quadratic : 1;
2788c2ecf20Sopenharmony_ci	struct wf_sensor	sens;
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci#define to_smu_cpu_power(c) container_of(c, struct smu_cpu_power_sensor, sens)
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic struct smu_cpu_power_sensor *smu_cpu_power;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic void smu_cpu_power_release(struct wf_sensor *sr)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (pow->volts)
2898c2ecf20Sopenharmony_ci		wf_put_sensor(pow->volts);
2908c2ecf20Sopenharmony_ci	if (pow->amps)
2918c2ecf20Sopenharmony_ci		wf_put_sensor(pow->amps);
2928c2ecf20Sopenharmony_ci	kfree(pow);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int smu_cpu_power_get(struct wf_sensor *sr, s32 *value)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct smu_cpu_power_sensor *pow = to_smu_cpu_power(sr);
2988c2ecf20Sopenharmony_ci	s32 volts, amps, power;
2998c2ecf20Sopenharmony_ci	u64 tmps, tmpa, tmpb;
3008c2ecf20Sopenharmony_ci	int rc;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	rc = pow->amps->ops->get_value(pow->amps, &amps);
3038c2ecf20Sopenharmony_ci	if (rc)
3048c2ecf20Sopenharmony_ci		return rc;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (pow->fake_volts) {
3078c2ecf20Sopenharmony_ci		*value = amps * 12 - 0x30000;
3088c2ecf20Sopenharmony_ci		return 0;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	rc = pow->volts->ops->get_value(pow->volts, &volts);
3128c2ecf20Sopenharmony_ci	if (rc)
3138c2ecf20Sopenharmony_ci		return rc;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	power = (s32)((((u64)volts) * ((u64)amps)) >> 16);
3168c2ecf20Sopenharmony_ci	if (!pow->quadratic) {
3178c2ecf20Sopenharmony_ci		*value = power;
3188c2ecf20Sopenharmony_ci		return 0;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	tmps = (((u64)power) * ((u64)power)) >> 16;
3218c2ecf20Sopenharmony_ci	tmpa = ((u64)cpuvcp->power_quads[0]) * tmps;
3228c2ecf20Sopenharmony_ci	tmpb = ((u64)cpuvcp->power_quads[1]) * ((u64)power);
3238c2ecf20Sopenharmony_ci	*value = (tmpa >> 28) + (tmpb >> 28) + (cpuvcp->power_quads[2] >> 12);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return 0;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops smu_cpu_power_ops = {
3298c2ecf20Sopenharmony_ci	.get_value	= smu_cpu_power_get,
3308c2ecf20Sopenharmony_ci	.release	= smu_cpu_power_release,
3318c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
3328c2ecf20Sopenharmony_ci};
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic struct smu_cpu_power_sensor *
3368c2ecf20Sopenharmony_cismu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct smu_cpu_power_sensor *pow;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	pow = kmalloc(sizeof(struct smu_cpu_power_sensor), GFP_KERNEL);
3418c2ecf20Sopenharmony_ci	if (pow == NULL)
3428c2ecf20Sopenharmony_ci		return NULL;
3438c2ecf20Sopenharmony_ci	pow->sens.ops = &smu_cpu_power_ops;
3448c2ecf20Sopenharmony_ci	pow->sens.name = "cpu-power";
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	wf_get_sensor(volts);
3478c2ecf20Sopenharmony_ci	pow->volts = volts;
3488c2ecf20Sopenharmony_ci	wf_get_sensor(amps);
3498c2ecf20Sopenharmony_ci	pow->amps = amps;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Some early machines need a faked voltage */
3528c2ecf20Sopenharmony_ci	if (debugswitches && ((*debugswitches) & 0x80)) {
3538c2ecf20Sopenharmony_ci		printk(KERN_INFO "windfarm: CPU Power sensor using faked"
3548c2ecf20Sopenharmony_ci		       " voltage !\n");
3558c2ecf20Sopenharmony_ci		pow->fake_volts = 1;
3568c2ecf20Sopenharmony_ci	} else
3578c2ecf20Sopenharmony_ci		pow->fake_volts = 0;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Try to use quadratic transforms on PowerMac8,1 and 9,1 for now,
3608c2ecf20Sopenharmony_ci	 * I yet have to figure out what's up with 8,2 and will have to
3618c2ecf20Sopenharmony_ci	 * adjust for later, unless we can 100% trust the SDB partition...
3628c2ecf20Sopenharmony_ci	 */
3638c2ecf20Sopenharmony_ci	if ((of_machine_is_compatible("PowerMac8,1") ||
3648c2ecf20Sopenharmony_ci	     of_machine_is_compatible("PowerMac8,2") ||
3658c2ecf20Sopenharmony_ci	     of_machine_is_compatible("PowerMac9,1")) &&
3668c2ecf20Sopenharmony_ci	    cpuvcp_version >= 2) {
3678c2ecf20Sopenharmony_ci		pow->quadratic = 1;
3688c2ecf20Sopenharmony_ci		DBG("windfarm: CPU Power using quadratic transform\n");
3698c2ecf20Sopenharmony_ci	} else
3708c2ecf20Sopenharmony_ci		pow->quadratic = 0;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (wf_register_sensor(&pow->sens))
3738c2ecf20Sopenharmony_ci		goto fail;
3748c2ecf20Sopenharmony_ci	return pow;
3758c2ecf20Sopenharmony_ci fail:
3768c2ecf20Sopenharmony_ci	kfree(pow);
3778c2ecf20Sopenharmony_ci	return NULL;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void smu_fetch_param_partitions(void)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	const struct smu_sdbp_header *hdr;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Get CPU voltage/current/power calibration data */
3858c2ecf20Sopenharmony_ci	hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL);
3868c2ecf20Sopenharmony_ci	if (hdr != NULL) {
3878c2ecf20Sopenharmony_ci		cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1];
3888c2ecf20Sopenharmony_ci		/* Keep version around */
3898c2ecf20Sopenharmony_ci		cpuvcp_version = hdr->version;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Get CPU diode calibration data */
3938c2ecf20Sopenharmony_ci	hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL);
3948c2ecf20Sopenharmony_ci	if (hdr != NULL)
3958c2ecf20Sopenharmony_ci		cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1];
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* Get slots power calibration data if any */
3988c2ecf20Sopenharmony_ci	hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL);
3998c2ecf20Sopenharmony_ci	if (hdr != NULL)
4008c2ecf20Sopenharmony_ci		slotspow = (struct smu_sdbp_slotspow *)&hdr[1];
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* Get debug switches if any */
4038c2ecf20Sopenharmony_ci	hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL);
4048c2ecf20Sopenharmony_ci	if (hdr != NULL)
4058c2ecf20Sopenharmony_ci		debugswitches = (u8 *)&hdr[1];
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int __init smu_sensors_init(void)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct device_node *smu, *sensors, *s;
4118c2ecf20Sopenharmony_ci	struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (!smu_present())
4148c2ecf20Sopenharmony_ci		return -ENODEV;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* Get parameters partitions */
4178c2ecf20Sopenharmony_ci	smu_fetch_param_partitions();
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	smu = of_find_node_by_type(NULL, "smu");
4208c2ecf20Sopenharmony_ci	if (smu == NULL)
4218c2ecf20Sopenharmony_ci		return -ENODEV;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Look for sensors subdir */
4248c2ecf20Sopenharmony_ci	for_each_child_of_node(smu, sensors)
4258c2ecf20Sopenharmony_ci		if (of_node_name_eq(sensors, "sensors"))
4268c2ecf20Sopenharmony_ci			break;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	of_node_put(smu);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* Create basic sensors */
4318c2ecf20Sopenharmony_ci	for (s = NULL;
4328c2ecf20Sopenharmony_ci	     sensors && (s = of_get_next_child(sensors, s)) != NULL;) {
4338c2ecf20Sopenharmony_ci		struct smu_ad_sensor *ads;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		ads = smu_ads_create(s);
4368c2ecf20Sopenharmony_ci		if (ads == NULL)
4378c2ecf20Sopenharmony_ci			continue;
4388c2ecf20Sopenharmony_ci		list_add(&ads->link, &smu_ads);
4398c2ecf20Sopenharmony_ci		/* keep track of cpu voltage & current */
4408c2ecf20Sopenharmony_ci		if (!strcmp(ads->sens.name, "cpu-voltage"))
4418c2ecf20Sopenharmony_ci			volt_sensor = ads;
4428c2ecf20Sopenharmony_ci		else if (!strcmp(ads->sens.name, "cpu-current"))
4438c2ecf20Sopenharmony_ci			curr_sensor = ads;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	of_node_put(sensors);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Create CPU power sensor if possible */
4498c2ecf20Sopenharmony_ci	if (volt_sensor && curr_sensor)
4508c2ecf20Sopenharmony_ci		smu_cpu_power = smu_cpu_power_create(&volt_sensor->sens,
4518c2ecf20Sopenharmony_ci						     &curr_sensor->sens);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void __exit smu_sensors_exit(void)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct smu_ad_sensor *ads;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* dispose of power sensor */
4618c2ecf20Sopenharmony_ci	if (smu_cpu_power)
4628c2ecf20Sopenharmony_ci		wf_unregister_sensor(&smu_cpu_power->sens);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* dispose of basic sensors */
4658c2ecf20Sopenharmony_ci	while (!list_empty(&smu_ads)) {
4668c2ecf20Sopenharmony_ci		ads = list_entry(smu_ads.next, struct smu_ad_sensor, link);
4678c2ecf20Sopenharmony_ci		list_del(&ads->link);
4688c2ecf20Sopenharmony_ci		wf_unregister_sensor(&ads->sens);
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cimodule_init(smu_sensors_init);
4748c2ecf20Sopenharmony_cimodule_exit(smu_sensors_exit);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
4778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMU sensor objects for PowerMacs thermal control");
4788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4798c2ecf20Sopenharmony_ci
480