162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Windfarm PowerMac thermal control.  MAX6690 sensor.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/i2c.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/pmac_low_i2c.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "windfarm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define VERSION "1.0"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* This currently only exports the external temperature sensor,
2162306a36Sopenharmony_ci   since that's all the control loops need. */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Some MAX6690 register numbers */
2462306a36Sopenharmony_ci#define MAX6690_INTERNAL_TEMP	0
2562306a36Sopenharmony_ci#define MAX6690_EXTERNAL_TEMP	1
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct wf_6690_sensor {
2862306a36Sopenharmony_ci	struct i2c_client	*i2c;
2962306a36Sopenharmony_ci	struct wf_sensor	sens;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define wf_to_6690(x)	container_of((x), struct wf_6690_sensor, sens)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int wf_max6690_get(struct wf_sensor *sr, s32 *value)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct wf_6690_sensor *max = wf_to_6690(sr);
3762306a36Sopenharmony_ci	s32 data;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (max->i2c == NULL)
4062306a36Sopenharmony_ci		return -ENODEV;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* chip gets initialized by firmware */
4362306a36Sopenharmony_ci	data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP);
4462306a36Sopenharmony_ci	if (data < 0)
4562306a36Sopenharmony_ci		return data;
4662306a36Sopenharmony_ci	*value = data << 16;
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void wf_max6690_release(struct wf_sensor *sr)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct wf_6690_sensor *max = wf_to_6690(sr);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	kfree(max);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct wf_sensor_ops wf_max6690_ops = {
5862306a36Sopenharmony_ci	.get_value	= wf_max6690_get,
5962306a36Sopenharmony_ci	.release	= wf_max6690_release,
6062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int wf_max6690_probe(struct i2c_client *client)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	const char *name, *loc;
6662306a36Sopenharmony_ci	struct wf_6690_sensor *max;
6762306a36Sopenharmony_ci	int rc;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
7062306a36Sopenharmony_ci	if (!loc) {
7162306a36Sopenharmony_ci		dev_warn(&client->dev, "Missing hwsensor-location property!\n");
7262306a36Sopenharmony_ci		return -ENXIO;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/*
7662306a36Sopenharmony_ci	 * We only expose the external temperature register for
7762306a36Sopenharmony_ci	 * now as this is all we need for our control loops
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci	if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT"))
8062306a36Sopenharmony_ci		name = "backside-temp";
8162306a36Sopenharmony_ci	else if (!strcmp(loc, "NB Ambient"))
8262306a36Sopenharmony_ci		name = "north-bridge-temp";
8362306a36Sopenharmony_ci	else if (!strcmp(loc, "GPU Ambient"))
8462306a36Sopenharmony_ci		name = "gpu-temp";
8562306a36Sopenharmony_ci	else
8662306a36Sopenharmony_ci		return -ENXIO;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
8962306a36Sopenharmony_ci	if (max == NULL) {
9062306a36Sopenharmony_ci		printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: "
9162306a36Sopenharmony_ci		       "no memory\n");
9262306a36Sopenharmony_ci		return -ENOMEM;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	max->i2c = client;
9662306a36Sopenharmony_ci	max->sens.name = name;
9762306a36Sopenharmony_ci	max->sens.ops = &wf_max6690_ops;
9862306a36Sopenharmony_ci	i2c_set_clientdata(client, max);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	rc = wf_register_sensor(&max->sens);
10162306a36Sopenharmony_ci	if (rc)
10262306a36Sopenharmony_ci		kfree(max);
10362306a36Sopenharmony_ci	return rc;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void wf_max6690_remove(struct i2c_client *client)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct wf_6690_sensor *max = i2c_get_clientdata(client);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	max->i2c = NULL;
11162306a36Sopenharmony_ci	wf_unregister_sensor(&max->sens);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic const struct i2c_device_id wf_max6690_id[] = {
11562306a36Sopenharmony_ci	{ "MAC,max6690", 0 },
11662306a36Sopenharmony_ci	{ }
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wf_max6690_id);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic const struct of_device_id wf_max6690_of_id[] = {
12162306a36Sopenharmony_ci	{ .compatible = "max6690", },
12262306a36Sopenharmony_ci	{ }
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wf_max6690_of_id);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic struct i2c_driver wf_max6690_driver = {
12762306a36Sopenharmony_ci	.driver = {
12862306a36Sopenharmony_ci		.name		= "wf_max6690",
12962306a36Sopenharmony_ci		.of_match_table = wf_max6690_of_id,
13062306a36Sopenharmony_ci	},
13162306a36Sopenharmony_ci	.probe		= wf_max6690_probe,
13262306a36Sopenharmony_ci	.remove		= wf_max6690_remove,
13362306a36Sopenharmony_ci	.id_table	= wf_max6690_id,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cimodule_i2c_driver(wf_max6690_driver);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
13962306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control");
14062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
141