18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Windfarm PowerMac thermal control.  MAX6690 sensor.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <asm/prom.h>
148c2ecf20Sopenharmony_ci#include <asm/pmac_low_i2c.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "windfarm.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define VERSION "1.0"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* This currently only exports the external temperature sensor,
218c2ecf20Sopenharmony_ci   since that's all the control loops need. */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Some MAX6690 register numbers */
248c2ecf20Sopenharmony_ci#define MAX6690_INTERNAL_TEMP	0
258c2ecf20Sopenharmony_ci#define MAX6690_EXTERNAL_TEMP	1
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct wf_6690_sensor {
288c2ecf20Sopenharmony_ci	struct i2c_client	*i2c;
298c2ecf20Sopenharmony_ci	struct wf_sensor	sens;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define wf_to_6690(x)	container_of((x), struct wf_6690_sensor, sens)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int wf_max6690_get(struct wf_sensor *sr, s32 *value)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct wf_6690_sensor *max = wf_to_6690(sr);
378c2ecf20Sopenharmony_ci	s32 data;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (max->i2c == NULL)
408c2ecf20Sopenharmony_ci		return -ENODEV;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* chip gets initialized by firmware */
438c2ecf20Sopenharmony_ci	data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP);
448c2ecf20Sopenharmony_ci	if (data < 0)
458c2ecf20Sopenharmony_ci		return data;
468c2ecf20Sopenharmony_ci	*value = data << 16;
478c2ecf20Sopenharmony_ci	return 0;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void wf_max6690_release(struct wf_sensor *sr)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct wf_6690_sensor *max = wf_to_6690(sr);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	kfree(max);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops wf_max6690_ops = {
588c2ecf20Sopenharmony_ci	.get_value	= wf_max6690_get,
598c2ecf20Sopenharmony_ci	.release	= wf_max6690_release,
608c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int wf_max6690_probe(struct i2c_client *client,
648c2ecf20Sopenharmony_ci			    const struct i2c_device_id *id)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	const char *name, *loc;
678c2ecf20Sopenharmony_ci	struct wf_6690_sensor *max;
688c2ecf20Sopenharmony_ci	int rc;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
718c2ecf20Sopenharmony_ci	if (!loc) {
728c2ecf20Sopenharmony_ci		dev_warn(&client->dev, "Missing hwsensor-location property!\n");
738c2ecf20Sopenharmony_ci		return -ENXIO;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/*
778c2ecf20Sopenharmony_ci	 * We only expose the external temperature register for
788c2ecf20Sopenharmony_ci	 * now as this is all we need for our control loops
798c2ecf20Sopenharmony_ci	 */
808c2ecf20Sopenharmony_ci	if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT"))
818c2ecf20Sopenharmony_ci		name = "backside-temp";
828c2ecf20Sopenharmony_ci	else if (!strcmp(loc, "NB Ambient"))
838c2ecf20Sopenharmony_ci		name = "north-bridge-temp";
848c2ecf20Sopenharmony_ci	else if (!strcmp(loc, "GPU Ambient"))
858c2ecf20Sopenharmony_ci		name = "gpu-temp";
868c2ecf20Sopenharmony_ci	else
878c2ecf20Sopenharmony_ci		return -ENXIO;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
908c2ecf20Sopenharmony_ci	if (max == NULL) {
918c2ecf20Sopenharmony_ci		printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: "
928c2ecf20Sopenharmony_ci		       "no memory\n");
938c2ecf20Sopenharmony_ci		return -ENOMEM;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	max->i2c = client;
978c2ecf20Sopenharmony_ci	max->sens.name = name;
988c2ecf20Sopenharmony_ci	max->sens.ops = &wf_max6690_ops;
998c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, max);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	rc = wf_register_sensor(&max->sens);
1028c2ecf20Sopenharmony_ci	if (rc)
1038c2ecf20Sopenharmony_ci		kfree(max);
1048c2ecf20Sopenharmony_ci	return rc;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int wf_max6690_remove(struct i2c_client *client)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct wf_6690_sensor *max = i2c_get_clientdata(client);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	max->i2c = NULL;
1128c2ecf20Sopenharmony_ci	wf_unregister_sensor(&max->sens);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic const struct i2c_device_id wf_max6690_id[] = {
1188c2ecf20Sopenharmony_ci	{ "MAC,max6690", 0 },
1198c2ecf20Sopenharmony_ci	{ }
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wf_max6690_id);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic const struct of_device_id wf_max6690_of_id[] = {
1248c2ecf20Sopenharmony_ci	{ .compatible = "max6690", },
1258c2ecf20Sopenharmony_ci	{ }
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, wf_max6690_of_id);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic struct i2c_driver wf_max6690_driver = {
1308c2ecf20Sopenharmony_ci	.driver = {
1318c2ecf20Sopenharmony_ci		.name		= "wf_max6690",
1328c2ecf20Sopenharmony_ci		.of_match_table = wf_max6690_of_id,
1338c2ecf20Sopenharmony_ci	},
1348c2ecf20Sopenharmony_ci	.probe		= wf_max6690_probe,
1358c2ecf20Sopenharmony_ci	.remove		= wf_max6690_remove,
1368c2ecf20Sopenharmony_ci	.id_table	= wf_max6690_id,
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cimodule_i2c_driver(wf_max6690_driver);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
1428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control");
1438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
144