cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

windfarm_fcu_controls.c (14476B)


      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
     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
     55struct 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
     63struct 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
     71static 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
     78static 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
     86static 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
    122static 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
    144static 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
    166static 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
    201static 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
    223static 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
    252static 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
    259static 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
    266static 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
    275static 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
    284static 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
    319static 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
    361static 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
    400static 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_warn("wf_fcu: Can't parse fan ID in device-tree for %pOF\n", np);
    472				break;
    473			}
    474			wf_fcu_add_fan(pv, name, type, id);
    475			break;
    476		}
    477	}
    478}
    479
    480static void wf_fcu_default_fans(struct wf_fcu_priv *pv)
    481{
    482	/* We only support the default fans for PowerMac7,2 */
    483	if (!of_machine_is_compatible("PowerMac7,2"))
    484		return;
    485
    486	wf_fcu_add_fan(pv, "backside-fan",	FCU_FAN_PWM, 1);
    487	wf_fcu_add_fan(pv, "drive-bay-fan",	FCU_FAN_RPM, 2);
    488	wf_fcu_add_fan(pv, "slots-fan",		FCU_FAN_PWM, 2);
    489	wf_fcu_add_fan(pv, "cpu-front-fan-0",	FCU_FAN_RPM, 3);
    490	wf_fcu_add_fan(pv, "cpu-rear-fan-0",	FCU_FAN_RPM, 4);
    491	wf_fcu_add_fan(pv, "cpu-front-fan-1",	FCU_FAN_RPM, 5);
    492	wf_fcu_add_fan(pv, "cpu-rear-fan-1",	FCU_FAN_RPM, 6);
    493}
    494
    495static int wf_fcu_init_chip(struct wf_fcu_priv *pv)
    496{
    497	unsigned char buf = 0xff;
    498	int rc;
    499
    500	rc = wf_fcu_write_reg(pv, 0xe, &buf, 1);
    501	if (rc < 0)
    502		return -EIO;
    503	rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1);
    504	if (rc < 0)
    505		return -EIO;
    506	rc = wf_fcu_read_reg(pv, 0, &buf, 1);
    507	if (rc < 0)
    508		return -EIO;
    509	pv->rpm_shift = (buf == 1) ? 2 : 3;
    510
    511	pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n",
    512		 pv->rpm_shift);
    513
    514	return 0;
    515}
    516
    517static int wf_fcu_probe(struct i2c_client *client,
    518			const struct i2c_device_id *id)
    519{
    520	struct wf_fcu_priv *pv;
    521
    522	pv = kzalloc(sizeof(*pv), GFP_KERNEL);
    523	if (!pv)
    524		return -ENOMEM;
    525
    526	kref_init(&pv->ref);
    527	mutex_init(&pv->lock);
    528	INIT_LIST_HEAD(&pv->fan_list);
    529	pv->i2c = client;
    530
    531	/*
    532	 * First we must start the FCU which will query the
    533	 * shift value to apply to RPMs
    534	 */
    535	if (wf_fcu_init_chip(pv)) {
    536		pr_err("wf_fcu: Initialization failed !\n");
    537		kfree(pv);
    538		return -ENXIO;
    539	}
    540
    541	/* First lookup fans in the device-tree */
    542	wf_fcu_lookup_fans(pv);
    543
    544	/*
    545	 * Older machines don't have the device-tree entries
    546	 * we are looking for, just hard code the list
    547	 */
    548	if (list_empty(&pv->fan_list))
    549		wf_fcu_default_fans(pv);
    550
    551	/* Still no fans ? FAIL */
    552	if (list_empty(&pv->fan_list)) {
    553		pr_err("wf_fcu: Failed to find fans for your machine\n");
    554		kfree(pv);
    555		return -ENODEV;
    556	}
    557
    558	dev_set_drvdata(&client->dev, pv);
    559
    560	return 0;
    561}
    562
    563static int wf_fcu_remove(struct i2c_client *client)
    564{
    565	struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev);
    566	struct wf_fcu_fan *fan;
    567
    568	while (!list_empty(&pv->fan_list)) {
    569		fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link);
    570		list_del(&fan->link);
    571		wf_unregister_control(&fan->ctrl);
    572	}
    573	kref_put(&pv->ref, wf_fcu_release);
    574	return 0;
    575}
    576
    577static const struct i2c_device_id wf_fcu_id[] = {
    578	{ "MAC,fcu", 0 },
    579	{ }
    580};
    581MODULE_DEVICE_TABLE(i2c, wf_fcu_id);
    582
    583static const struct of_device_id wf_fcu_of_id[] = {
    584	{ .compatible = "fcu", },
    585	{ }
    586};
    587MODULE_DEVICE_TABLE(of, wf_fcu_of_id);
    588
    589static struct i2c_driver wf_fcu_driver = {
    590	.driver = {
    591		.name	= "wf_fcu",
    592		.of_match_table = wf_fcu_of_id,
    593	},
    594	.probe		= wf_fcu_probe,
    595	.remove		= wf_fcu_remove,
    596	.id_table	= wf_fcu_id,
    597};
    598
    599module_i2c_driver(wf_fcu_driver);
    600
    601MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
    602MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control");
    603MODULE_LICENSE("GPL");
    604