brintos

brintos / linux-shallow public Read only

0
0
Text · 16.4 KiB · 0069374 Raw
624 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Device driver for the i2c thermostat found on the iBook G4, Albook G44 *5 * Copyright (C) 2003, 2004 Colin Leroy, Rasmus Rohde, Benjamin Herrenschmidt6 *7 * Documentation from 115254175ADT7467_pra.pdf and 3686221171167ADT7460_b.pdf8 * https://www.onsemi.com/PowerSolutions/product.do?id=ADT74679 * https://www.onsemi.com/PowerSolutions/product.do?id=ADT746010 *11 */12 13#include <linux/types.h>14#include <linux/module.h>15#include <linux/errno.h>16#include <linux/kernel.h>17#include <linux/delay.h>18#include <linux/sched.h>19#include <linux/i2c.h>20#include <linux/slab.h>21#include <linux/init.h>22#include <linux/spinlock.h>23#include <linux/wait.h>24#include <linux/suspend.h>25#include <linux/kthread.h>26#include <linux/moduleparam.h>27#include <linux/freezer.h>28#include <linux/of.h>29#include <linux/of_platform.h>30#include <linux/platform_device.h>31 32#include <asm/machdep.h>33#include <asm/io.h>34#include <asm/sections.h>35 36#undef DEBUG37 38#define CONFIG_REG   0x4039#define MANUAL_MASK  0xe040#define AUTO_MASK    0x2041#define INVERT_MASK  0x1042 43static u8 TEMP_REG[3]    = {0x26, 0x25, 0x27}; /* local, sensor1, sensor2 */44static u8 LIMIT_REG[3]   = {0x6b, 0x6a, 0x6c}; /* local, sensor1, sensor2 */45static u8 MANUAL_MODE[2] = {0x5c, 0x5d};       46static u8 REM_CONTROL[2] = {0x00, 0x40};47static u8 FAN_SPEED[2]   = {0x28, 0x2a};48static u8 FAN_SPD_SET[2] = {0x30, 0x31};49 50static u8 default_limits_local[3] = {70, 50, 70};    /* local, sensor1, sensor2 */51static u8 default_limits_chip[3] = {80, 65, 80};    /* local, sensor1, sensor2 */52static const char *sensor_location[3] = { "?", "?", "?" };53 54static int limit_adjust;55static int fan_speed = -1;56static bool verbose;57 58MODULE_AUTHOR("Colin Leroy <colin@colino.net>");59MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "60		   "Powerbook G4 Alu");61MODULE_LICENSE("GPL");62 63module_param(limit_adjust, int, 0644);64MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 sensor1, 70 sensor2) "65		 "by N degrees.");66 67module_param(fan_speed, int, 0644);68MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) "69		 "(default 64)");70 71module_param(verbose, bool, 0);72MODULE_PARM_DESC(verbose,"Verbose log operations "73		 "(default 0)");74 75struct thermostat {76	struct i2c_client	*clt;77	u8			temps[3];78	u8			cached_temp[3];79	u8			initial_limits[3];80	u8			limits[3];81	int			last_speed[2];82	int			last_var[2];83	int			pwm_inv[2];84	struct task_struct	*thread;85	struct platform_device	*pdev;86	enum {87		ADT7460,88		ADT746789	}			type;90};91 92static void write_both_fan_speed(struct thermostat *th, int speed);93static void write_fan_speed(struct thermostat *th, int speed, int fan);94 95static int96write_reg(struct thermostat* th, int reg, u8 data)97{98	u8 tmp[2];99	int rc;100	101	tmp[0] = reg;102	tmp[1] = data;103	rc = i2c_master_send(th->clt, (const char *)tmp, 2);104	if (rc < 0)105		return rc;106	if (rc != 2)107		return -ENODEV;108	return 0;109}110 111static int112read_reg(struct thermostat* th, int reg)113{114	u8 reg_addr, data;115	int rc;116 117	reg_addr = (u8)reg;118	rc = i2c_master_send(th->clt, &reg_addr, 1);119	if (rc < 0)120		return rc;121	if (rc != 1)122		return -ENODEV;123	rc = i2c_master_recv(th->clt, (char *)&data, 1);124	if (rc < 0)125		return rc;126	return data;127}128 129static int read_fan_speed(struct thermostat *th, u8 addr)130{131	u8 tmp[2];132	u16 res;133	134	/* should start with low byte */135	tmp[1] = read_reg(th, addr);136	tmp[0] = read_reg(th, addr + 1);137	138	res = tmp[1] + (tmp[0] << 8);139	/* "a value of 0xffff means that the fan has stopped" */140	return (res == 0xffff ? 0 : (90000*60)/res);141}142 143static void write_both_fan_speed(struct thermostat *th, int speed)144{145	write_fan_speed(th, speed, 0);146	if (th->type == ADT7460)147		write_fan_speed(th, speed, 1);148}149 150static void write_fan_speed(struct thermostat *th, int speed, int fan)151{152	u8 manual;153	154	if (speed > 0xff) 155		speed = 0xff;156	else if (speed < -1) 157		speed = 0;158	159	if (th->type == ADT7467 && fan == 1)160		return;161	162	if (th->last_speed[fan] != speed) {163		if (verbose) {164			if (speed == -1)165				printk(KERN_DEBUG "adt746x: Setting speed to automatic "166					"for %s fan.\n", sensor_location[fan+1]);167			else168				printk(KERN_DEBUG "adt746x: Setting speed to %d "169					"for %s fan.\n", speed, sensor_location[fan+1]);170		}171	} else172		return;173	174	if (speed >= 0) {175		manual = read_reg(th, MANUAL_MODE[fan]);176		manual &= ~INVERT_MASK;177		write_reg(th, MANUAL_MODE[fan],178			manual | MANUAL_MASK | th->pwm_inv[fan]);179		write_reg(th, FAN_SPD_SET[fan], speed);180	} else {181		/* back to automatic */182		if(th->type == ADT7460) {183			manual = read_reg(th,184				MANUAL_MODE[fan]) & (~MANUAL_MASK);185			manual &= ~INVERT_MASK;186			manual |= th->pwm_inv[fan];187			write_reg(th,188				MANUAL_MODE[fan], manual|REM_CONTROL[fan]);189		} else {190			manual = read_reg(th, MANUAL_MODE[fan]);191			manual &= ~INVERT_MASK;192			manual |= th->pwm_inv[fan];193			write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));194		}195	}196	197	th->last_speed[fan] = speed;			198}199 200static void read_sensors(struct thermostat *th)201{202	int i = 0;203 204	for (i = 0; i < 3; i++)205		th->temps[i]  = read_reg(th, TEMP_REG[i]);206}207 208#ifdef DEBUG209static void display_stats(struct thermostat *th)210{211	if (th->temps[0] != th->cached_temp[0]212	||  th->temps[1] != th->cached_temp[1]213	||  th->temps[2] != th->cached_temp[2]) {214		printk(KERN_INFO "adt746x: Temperature infos:"215				 " thermostats: %d,%d,%d;"216				 " limits: %d,%d,%d;"217				 " fan speed: %d RPM\n",218				 th->temps[0], th->temps[1], th->temps[2],219				 th->limits[0],  th->limits[1],  th->limits[2],220				 read_fan_speed(th, FAN_SPEED[0]));221	}222	th->cached_temp[0] = th->temps[0];223	th->cached_temp[1] = th->temps[1];224	th->cached_temp[2] = th->temps[2];225}226#endif227 228static void update_fans_speed (struct thermostat *th)229{230	int lastvar = 0; /* last variation, for iBook */231	int i = 0;232 233	/* we don't care about local sensor, so we start at sensor 1 */234	for (i = 1; i < 3; i++) {235		bool started = false;236		int fan_number = (th->type == ADT7460 && i == 2);237		int var = th->temps[i] - th->limits[i];238 239		if (var > -1) {240			int step = (255 - fan_speed) / 7;241			int new_speed = 0;242 243			/* hysteresis : change fan speed only if variation is244			 * more than two degrees */245			if (abs(var - th->last_var[fan_number]) < 2)246				continue;247 248			started = true;249			new_speed = fan_speed + ((var-1)*step);250 251			if (new_speed < fan_speed)252				new_speed = fan_speed;253			if (new_speed > 255)254				new_speed = 255;255 256			if (verbose)257				printk(KERN_DEBUG "adt746x: Setting fans speed to %d "258						 "(limit exceeded by %d on %s)\n",259						new_speed, var,260						sensor_location[fan_number+1]);261			write_both_fan_speed(th, new_speed);262			th->last_var[fan_number] = var;263		} else if (var < -2) {264			/* don't stop fan if sensor2 is cold and sensor1 is not265			 * so cold (lastvar >= -1) */266			if (i == 2 && lastvar < -1) {267				if (th->last_speed[fan_number] != 0)268					if (verbose)269						printk(KERN_DEBUG "adt746x: Stopping "270							"fans.\n");271				write_both_fan_speed(th, 0);272			}273		}274 275		lastvar = var;276 277		if (started)278			return; /* we don't want to re-stop the fan279				* if sensor1 is heating and sensor2 is not */280	}281}282 283static int monitor_task(void *arg)284{285	struct thermostat* th = arg;286 287	set_freezable();288	while(!kthread_should_stop()) {289		try_to_freeze();290		msleep_interruptible(2000);291 292#ifndef DEBUG293		if (fan_speed != -1)294			read_sensors(th);295#else296		read_sensors(th);297#endif		298 299		if (fan_speed != -1)300			update_fans_speed(th);301 302#ifdef DEBUG303		display_stats(th);304#endif305 306	}307 308	return 0;309}310 311static void set_limit(struct thermostat *th, int i)312{313	/* Set sensor1 limit higher to avoid powerdowns */314	th->limits[i] = default_limits_chip[i] + limit_adjust;315	write_reg(th, LIMIT_REG[i], th->limits[i]);316		317	/* set our limits to normal */318	th->limits[i] = default_limits_local[i] + limit_adjust;319}320 321#define BUILD_SHOW_FUNC_INT(name, data)				\322static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\323{								\324	struct thermostat *th = dev_get_drvdata(dev);		\325	return sprintf(buf, "%d\n", data);			\326}327 328#define BUILD_SHOW_FUNC_INT_LITE(name, data)				\329static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\330{								\331	return sprintf(buf, "%d\n", data);			\332}333 334#define BUILD_SHOW_FUNC_STR(name, data)				\335static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)       \336{								\337	return sprintf(buf, "%s\n", data);			\338}339 340#define BUILD_SHOW_FUNC_FAN(name, data)				\341static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)       \342{								\343	struct thermostat *th = dev_get_drvdata(dev);		\344	return sprintf(buf, "%d (%d rpm)\n", 			\345		th->last_speed[data],				\346		read_fan_speed(th, FAN_SPEED[data])		\347		);						\348}349 350#define BUILD_STORE_FUNC_DEG(name, data)			\351static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \352{								\353	struct thermostat *th = dev_get_drvdata(dev);		\354	int val;						\355	int i;							\356	val = simple_strtol(buf, NULL, 10);			\357	printk(KERN_INFO "Adjusting limits by %d degrees\n", val);	\358	limit_adjust = val;					\359	for (i=0; i < 3; i++)					\360		set_limit(th, i);				\361	return n;						\362}363 364#define BUILD_STORE_FUNC_INT(name, data)			\365static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \366{								\367	int val;						\368	val = simple_strtol(buf, NULL, 10);			\369	if (val < 0 || val > 255)				\370		return -EINVAL;					\371	printk(KERN_INFO "Setting specified fan speed to %d\n", val);	\372	data = val;						\373	return n;						\374}375 376BUILD_SHOW_FUNC_INT(sensor1_temperature,	 (read_reg(th, TEMP_REG[1])))377BUILD_SHOW_FUNC_INT(sensor2_temperature,	 (read_reg(th, TEMP_REG[2])))378BUILD_SHOW_FUNC_INT(sensor1_limit,		 th->limits[1])379BUILD_SHOW_FUNC_INT(sensor2_limit,		 th->limits[2])380BUILD_SHOW_FUNC_STR(sensor1_location,		 sensor_location[1])381BUILD_SHOW_FUNC_STR(sensor2_location,		 sensor_location[2])382 383BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed)384BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)385 386BUILD_SHOW_FUNC_FAN(sensor1_fan_speed,	 0)387BUILD_SHOW_FUNC_FAN(sensor2_fan_speed,	 1)388 389BUILD_SHOW_FUNC_INT_LITE(limit_adjust,	 limit_adjust)390BUILD_STORE_FUNC_DEG(limit_adjust,	 th)391		392static DEVICE_ATTR(sensor1_temperature,	S_IRUGO,393		   show_sensor1_temperature,NULL);394static DEVICE_ATTR(sensor2_temperature,	S_IRUGO,395		   show_sensor2_temperature,NULL);396static DEVICE_ATTR(sensor1_limit, S_IRUGO,397		   show_sensor1_limit,	NULL);398static DEVICE_ATTR(sensor2_limit, S_IRUGO,399		   show_sensor2_limit,	NULL);400static DEVICE_ATTR(sensor1_location, S_IRUGO,401		   show_sensor1_location, NULL);402static DEVICE_ATTR(sensor2_location, S_IRUGO,403		   show_sensor2_location, NULL);404 405static DEVICE_ATTR(specified_fan_speed,	S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,406		   show_specified_fan_speed,store_specified_fan_speed);407 408static DEVICE_ATTR(sensor1_fan_speed,	S_IRUGO,409		   show_sensor1_fan_speed,	NULL);410static DEVICE_ATTR(sensor2_fan_speed,	S_IRUGO,411		   show_sensor2_fan_speed,	NULL);412 413static DEVICE_ATTR(limit_adjust,	S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,414		   show_limit_adjust,	store_limit_adjust);415 416static void thermostat_create_files(struct thermostat *th)417{418	struct device_node *np = th->clt->dev.of_node;419	struct device *dev;420	int err;421 422	/* To maintain ABI compatibility with userspace, create423	 * the old style platform driver and attach the attributes424	 * to it here425	 */426	th->pdev = of_platform_device_create(np, "temperatures", NULL);427	if (!th->pdev)428		return;429	dev = &th->pdev->dev;430	dev_set_drvdata(dev, th);431	err = device_create_file(dev, &dev_attr_sensor1_temperature);432	err |= device_create_file(dev, &dev_attr_sensor2_temperature);433	err |= device_create_file(dev, &dev_attr_sensor1_limit);434	err |= device_create_file(dev, &dev_attr_sensor2_limit);435	err |= device_create_file(dev, &dev_attr_sensor1_location);436	err |= device_create_file(dev, &dev_attr_sensor2_location);437	err |= device_create_file(dev, &dev_attr_limit_adjust);438	err |= device_create_file(dev, &dev_attr_specified_fan_speed);439	err |= device_create_file(dev, &dev_attr_sensor1_fan_speed);440	if(th->type == ADT7460)441		err |= device_create_file(dev, &dev_attr_sensor2_fan_speed);442	if (err)443		printk(KERN_WARNING444			"Failed to create temperature attribute file(s).\n");445}446 447static void thermostat_remove_files(struct thermostat *th)448{449	struct device *dev;450 451	if (!th->pdev)452		return;453	dev = &th->pdev->dev;454	device_remove_file(dev, &dev_attr_sensor1_temperature);455	device_remove_file(dev, &dev_attr_sensor2_temperature);456	device_remove_file(dev, &dev_attr_sensor1_limit);457	device_remove_file(dev, &dev_attr_sensor2_limit);458	device_remove_file(dev, &dev_attr_sensor1_location);459	device_remove_file(dev, &dev_attr_sensor2_location);460	device_remove_file(dev, &dev_attr_limit_adjust);461	device_remove_file(dev, &dev_attr_specified_fan_speed);462	device_remove_file(dev, &dev_attr_sensor1_fan_speed);	463	if (th->type == ADT7460)464		device_remove_file(dev, &dev_attr_sensor2_fan_speed);465	of_device_unregister(th->pdev);466 467}468 469static int probe_thermostat(struct i2c_client *client)470{471	const struct i2c_device_id *id = i2c_client_get_device_id(client);472	struct device_node *np = client->dev.of_node;473	struct thermostat* th;474	const __be32 *prop;475	int i, rc, vers, offset = 0;476 477	if (!np)478		return -ENXIO;479	prop = of_get_property(np, "hwsensor-params-version", NULL);480	if (!prop)481		return -ENXIO;482	vers = be32_to_cpup(prop);483	printk(KERN_INFO "adt746x: version %d (%ssupported)\n",484	       vers, vers == 1 ? "" : "un");485	if (vers != 1)486		return -ENXIO;487 488	if (of_property_present(np, "hwsensor-location")) {489		for (i = 0; i < 3; i++) {490			sensor_location[i] = of_get_property(np,491					"hwsensor-location", NULL) + offset;492 493			if (sensor_location[i] == NULL)494				sensor_location[i] = "";495 496			printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]);497			offset += strlen(sensor_location[i]) + 1;498		}499	}500 501	th = kzalloc(sizeof(struct thermostat), GFP_KERNEL);502	if (!th)503		return -ENOMEM;504 505	i2c_set_clientdata(client, th);506	th->clt = client;507	th->type = id->driver_data;508 509	rc = read_reg(th, CONFIG_REG);510	if (rc < 0) {511		dev_err(&client->dev, "Thermostat failed to read config!\n");512		kfree(th);513		return -ENODEV;514	}515 516	/* force manual control to start the fan quieter */517	if (fan_speed == -1)518		fan_speed = 64;519	520	if (th->type == ADT7460) {521		printk(KERN_INFO "adt746x: ADT7460 initializing\n");522		/* The 7460 needs to be started explicitly */523		write_reg(th, CONFIG_REG, 1);524	} else525		printk(KERN_INFO "adt746x: ADT7467 initializing\n");526 527	for (i = 0; i < 3; i++) {528		th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);529		set_limit(th, i);530	}531 532	printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"533			 " to %d, %d, %d\n",534			 th->initial_limits[0], th->initial_limits[1],535			 th->initial_limits[2], th->limits[0], th->limits[1],536			 th->limits[2]);537 538	/* record invert bit status because fw can corrupt it after suspend */539	th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK;540	th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK;541 542	/* be sure to really write fan speed the first time */543	th->last_speed[0] = -2;544	th->last_speed[1] = -2;545	th->last_var[0] = -80;546	th->last_var[1] = -80;547 548	if (fan_speed != -1) {549		/* manual mode, stop fans */550		write_both_fan_speed(th, 0);551	} else {552		/* automatic mode */553		write_both_fan_speed(th, -1);554	}555	556	th->thread = kthread_run(monitor_task, th, "kfand");557	if (th->thread == ERR_PTR(-ENOMEM)) {558		printk(KERN_INFO "adt746x: Kthread creation failed\n");559		th->thread = NULL;560		return -ENOMEM;561	}562 563	thermostat_create_files(th);564 565	return 0;566}567 568static void remove_thermostat(struct i2c_client *client)569{570	struct thermostat *th = i2c_get_clientdata(client);571	int i;572	573	thermostat_remove_files(th);574 575	if (th->thread != NULL)576		kthread_stop(th->thread);577 578	printk(KERN_INFO "adt746x: Putting max temperatures back from "579			 "%d, %d, %d to %d, %d, %d\n",580		th->limits[0], th->limits[1], th->limits[2],581		th->initial_limits[0], th->initial_limits[1],582		th->initial_limits[2]);583 584	for (i = 0; i < 3; i++)585		write_reg(th, LIMIT_REG[i], th->initial_limits[i]);586 587	write_both_fan_speed(th, -1);588 589	kfree(th);590}591 592static const struct i2c_device_id therm_adt746x_id[] = {593	{ "MAC,adt7460", ADT7460 },594	{ "MAC,adt7467", ADT7467 },595	{ }596};597MODULE_DEVICE_TABLE(i2c, therm_adt746x_id);598 599static struct i2c_driver thermostat_driver = {600	.driver = {601		.name	= "therm_adt746x",602	},603	.probe = probe_thermostat,604	.remove = remove_thermostat,605	.id_table = therm_adt746x_id,606};607 608static int __init thermostat_init(void)609{610#ifndef CONFIG_I2C_POWERMAC611	request_module("i2c-powermac");612#endif613 614	return i2c_add_driver(&thermostat_driver);615}616 617static void __exit thermostat_exit(void)618{619	i2c_del_driver(&thermostat_driver);620}621 622module_init(thermostat_init);623module_exit(thermostat_exit);624