brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · ff8805e Raw
362 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Windfarm PowerMac thermal control.  SMU "satellite" controller sensors.4 *5 * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>6 */7 8#include <linux/types.h>9#include <linux/errno.h>10#include <linux/kernel.h>11#include <linux/slab.h>12#include <linux/init.h>13#include <linux/wait.h>14#include <linux/i2c.h>15#include <linux/mutex.h>16 17#include <asm/smu.h>18#include <asm/pmac_low_i2c.h>19 20#include "windfarm.h"21 22#define VERSION "1.0"23 24/* If the cache is older than 800ms we'll refetch it */25#define MAX_AGE		msecs_to_jiffies(800)26 27struct wf_sat {28	struct kref		ref;29	int			nr;30	struct mutex		mutex;31	unsigned long		last_read; /* jiffies when cache last updated */32	u8			cache[16];33	struct list_head	sensors;34	struct i2c_client	*i2c;35	struct device_node	*node;36};37 38static struct wf_sat *sats[2];39 40struct wf_sat_sensor {41	struct list_head	link;42	int			index;43	int			index2;		/* used for power sensors */44	int			shift;45	struct wf_sat		*sat;46	struct wf_sensor 	sens;47};48 49#define wf_to_sat(c)	container_of(c, struct wf_sat_sensor, sens)50 51struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,52						  unsigned int *size)53{54	struct wf_sat *sat;55	int err;56	unsigned int i, len;57	u8 *buf;58	u8 data[4];59 60	/* TODO: Add the resulting partition to the device-tree */61 62	if (sat_id > 1 || (sat = sats[sat_id]) == NULL)63		return NULL;64 65	err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);66	if (err) {67		printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);68		return NULL;69	}70 71	err = i2c_smbus_read_word_data(sat->i2c, 9);72	if (err < 0) {73		printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");74		return NULL;75	}76	len = err;77	if (len == 0) {78		printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);79		return NULL;80	}81 82	len = le16_to_cpu(len);83	len = (len + 3) & ~3;84	buf = kmalloc(len, GFP_KERNEL);85	if (buf == NULL)86		return NULL;87 88	for (i = 0; i < len; i += 4) {89		err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);90		if (err < 0) {91			printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",92			       err);93			goto fail;94		}95		buf[i] = data[1];96		buf[i+1] = data[0];97		buf[i+2] = data[3];98		buf[i+3] = data[2];99	}100 101	printk(KERN_DEBUG "sat %d partition %x:", sat_id, id);102	print_hex_dump(KERN_DEBUG, "  ", DUMP_PREFIX_OFFSET,103		       16, 1, buf, len, false);104	if (size)105		*size = len;106	return (struct smu_sdbp_header *) buf;107 108 fail:109	kfree(buf);110	return NULL;111}112EXPORT_SYMBOL_GPL(smu_sat_get_sdb_partition);113 114/* refresh the cache */115static int wf_sat_read_cache(struct wf_sat *sat)116{117	int err;118 119	err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);120	if (err < 0)121		return err;122	sat->last_read = jiffies;123 124#ifdef LOTSA_DEBUG125	{126		int i;127		printk(KERN_DEBUG "wf_sat_get: data is");128		print_hex_dump(KERN_DEBUG, "  ", DUMP_PREFIX_OFFSET,129			       16, 1, sat->cache, 16, false);130	}131#endif132	return 0;133}134 135static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value)136{137	struct wf_sat_sensor *sens = wf_to_sat(sr);138	struct wf_sat *sat = sens->sat;139	int i, err;140	s32 val;141 142	if (sat->i2c == NULL)143		return -ENODEV;144 145	mutex_lock(&sat->mutex);146	if (time_after(jiffies, (sat->last_read + MAX_AGE))) {147		err = wf_sat_read_cache(sat);148		if (err)149			goto fail;150	}151 152	i = sens->index * 2;153	val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift;154	if (sens->index2 >= 0) {155		i = sens->index2 * 2;156		/* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */157		val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4;158	}159 160	*value = val;161	err = 0;162 163 fail:164	mutex_unlock(&sat->mutex);165	return err;166}167 168static void wf_sat_release(struct kref *ref)169{170	struct wf_sat *sat = container_of(ref, struct wf_sat, ref);171 172	if (sat->nr >= 0)173		sats[sat->nr] = NULL;174	of_node_put(sat->node);175	kfree(sat);176}177 178static void wf_sat_sensor_release(struct wf_sensor *sr)179{180	struct wf_sat_sensor *sens = wf_to_sat(sr);181	struct wf_sat *sat = sens->sat;182 183	kfree(sens);184	kref_put(&sat->ref, wf_sat_release);185}186 187static const struct wf_sensor_ops wf_sat_ops = {188	.get_value	= wf_sat_sensor_get,189	.release	= wf_sat_sensor_release,190	.owner		= THIS_MODULE,191};192 193static int wf_sat_probe(struct i2c_client *client)194{195	struct device_node *dev = client->dev.of_node;196	struct wf_sat *sat;197	struct wf_sat_sensor *sens;198	const u32 *reg;199	const char *loc;200	u8 chip, core;201	struct device_node *child;202	int shift, cpu, index;203	char *name;204	int vsens[2], isens[2];205 206	sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);207	if (sat == NULL)208		return -ENOMEM;209	sat->nr = -1;210	sat->node = of_node_get(dev);211	kref_init(&sat->ref);212	mutex_init(&sat->mutex);213	sat->i2c = client;214	INIT_LIST_HEAD(&sat->sensors);215	i2c_set_clientdata(client, sat);216 217	vsens[0] = vsens[1] = -1;218	isens[0] = isens[1] = -1;219	for_each_child_of_node(dev, child) {220		reg = of_get_property(child, "reg", NULL);221		loc = of_get_property(child, "location", NULL);222		if (reg == NULL || loc == NULL)223			continue;224 225		/* the cooked sensors are between 0x30 and 0x37 */226		if (*reg < 0x30 || *reg > 0x37)227			continue;228		index = *reg - 0x30;229 230		/* expect location to be CPU [AB][01] ... */231		if (strncmp(loc, "CPU ", 4) != 0)232			continue;233		chip = loc[4] - 'A';234		core = loc[5] - '0';235		if (chip > 1 || core > 1) {236			printk(KERN_ERR "wf_sat_create: don't understand "237			       "location %s for %pOF\n", loc, child);238			continue;239		}240		cpu = 2 * chip + core;241		if (sat->nr < 0)242			sat->nr = chip;243		else if (sat->nr != chip) {244			printk(KERN_ERR "wf_sat_create: can't cope with "245			       "multiple CPU chips on one SAT (%s)\n", loc);246			continue;247		}248 249		if (of_node_is_type(child, "voltage-sensor")) {250			name = "cpu-voltage";251			shift = 4;252			vsens[core] = index;253		} else if (of_node_is_type(child, "current-sensor")) {254			name = "cpu-current";255			shift = 8;256			isens[core] = index;257		} else if (of_node_is_type(child, "temp-sensor")) {258			name = "cpu-temp";259			shift = 10;260		} else261			continue;	/* hmmm shouldn't happen */262 263		/* the +16 is enough for "cpu-voltage-n" */264		sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);265		if (sens == NULL) {266			printk(KERN_ERR "wf_sat_create: couldn't create "267			       "%s sensor %d (no memory)\n", name, cpu);268			continue;269		}270		sens->index = index;271		sens->index2 = -1;272		sens->shift = shift;273		sens->sat = sat;274		sens->sens.ops = &wf_sat_ops;275		sens->sens.name = (char *) (sens + 1);276		snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu);277 278		if (wf_register_sensor(&sens->sens))279			kfree(sens);280		else {281			list_add(&sens->link, &sat->sensors);282			kref_get(&sat->ref);283		}284	}285 286	/* make the power sensors */287	for (core = 0; core < 2; ++core) {288		if (vsens[core] < 0 || isens[core] < 0)289			continue;290		cpu = 2 * sat->nr + core;291		sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);292		if (sens == NULL) {293			printk(KERN_ERR "wf_sat_create: couldn't create power "294			       "sensor %d (no memory)\n", cpu);295			continue;296		}297		sens->index = vsens[core];298		sens->index2 = isens[core];299		sens->shift = 0;300		sens->sat = sat;301		sens->sens.ops = &wf_sat_ops;302		sens->sens.name = (char *) (sens + 1);303		snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu);304 305		if (wf_register_sensor(&sens->sens))306			kfree(sens);307		else {308			list_add(&sens->link, &sat->sensors);309			kref_get(&sat->ref);310		}311	}312 313	if (sat->nr >= 0)314		sats[sat->nr] = sat;315 316	return 0;317}318 319static void wf_sat_remove(struct i2c_client *client)320{321	struct wf_sat *sat = i2c_get_clientdata(client);322	struct wf_sat_sensor *sens;323 324	/* release sensors */325	while(!list_empty(&sat->sensors)) {326		sens = list_first_entry(&sat->sensors,327					struct wf_sat_sensor, link);328		list_del(&sens->link);329		wf_unregister_sensor(&sens->sens);330	}331	sat->i2c = NULL;332	kref_put(&sat->ref, wf_sat_release);333}334 335static const struct i2c_device_id wf_sat_id[] = {336	{ "MAC,smu-sat" },337	{ }338};339MODULE_DEVICE_TABLE(i2c, wf_sat_id);340 341static const struct of_device_id wf_sat_of_id[] = {342	{ .compatible = "smu-sat", },343	{ }344};345MODULE_DEVICE_TABLE(of, wf_sat_of_id);346 347static struct i2c_driver wf_sat_driver = {348	.driver = {349		.name		= "wf_smu_sat",350		.of_match_table = wf_sat_of_id,351	},352	.probe		= wf_sat_probe,353	.remove		= wf_sat_remove,354	.id_table	= wf_sat_id,355};356 357module_i2c_driver(wf_sat_driver);358 359MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");360MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");361MODULE_LICENSE("GPL");362