162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * atxp1.c - kernel module for setting CPU VID and general purpose
462306a36Sopenharmony_ci *	     I/Os using the Attansic ATXP1 chip.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
762306a36Sopenharmony_ci * not auto-detected by the driver and must be instantiated explicitly.
862306a36Sopenharmony_ci * See Documentation/i2c/instantiating-devices.rst for more information.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/jiffies.h>
1562306a36Sopenharmony_ci#include <linux/i2c.h>
1662306a36Sopenharmony_ci#include <linux/hwmon.h>
1762306a36Sopenharmony_ci#include <linux/hwmon-vid.h>
1862306a36Sopenharmony_ci#include <linux/err.h>
1962306a36Sopenharmony_ci#include <linux/kstrtox.h>
2062306a36Sopenharmony_ci#include <linux/mutex.h>
2162306a36Sopenharmony_ci#include <linux/sysfs.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2562306a36Sopenharmony_ciMODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
2662306a36Sopenharmony_ciMODULE_VERSION("0.6.3");
2762306a36Sopenharmony_ciMODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define ATXP1_VID	0x00
3062306a36Sopenharmony_ci#define ATXP1_CVID	0x01
3162306a36Sopenharmony_ci#define ATXP1_GPIO1	0x06
3262306a36Sopenharmony_ci#define ATXP1_GPIO2	0x0a
3362306a36Sopenharmony_ci#define ATXP1_VIDENA	0x20
3462306a36Sopenharmony_ci#define ATXP1_VIDMASK	0x1f
3562306a36Sopenharmony_ci#define ATXP1_GPIO1MASK	0x0f
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct atxp1_data {
3862306a36Sopenharmony_ci	struct i2c_client *client;
3962306a36Sopenharmony_ci	struct mutex update_lock;
4062306a36Sopenharmony_ci	unsigned long last_updated;
4162306a36Sopenharmony_ci	bool valid;
4262306a36Sopenharmony_ci	struct {
4362306a36Sopenharmony_ci		u8 vid;		/* VID output register */
4462306a36Sopenharmony_ci		u8 cpu_vid; /* VID input from CPU */
4562306a36Sopenharmony_ci		u8 gpio1;   /* General purpose I/O register 1 */
4662306a36Sopenharmony_ci		u8 gpio2;   /* General purpose I/O register 2 */
4762306a36Sopenharmony_ci	} reg;
4862306a36Sopenharmony_ci	u8 vrm;			/* Detected CPU VRM */
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct atxp1_data *atxp1_update_device(struct device *dev)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct atxp1_data *data = dev_get_drvdata(dev);
5462306a36Sopenharmony_ci	struct i2c_client *client = data->client;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		/* Update local register data */
6162306a36Sopenharmony_ci		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
6262306a36Sopenharmony_ci		data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
6362306a36Sopenharmony_ci							     ATXP1_CVID);
6462306a36Sopenharmony_ci		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
6562306a36Sopenharmony_ci		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		data->valid = true;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return data;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* sys file functions for cpu0_vid */
7662306a36Sopenharmony_cistatic ssize_t cpu0_vid_show(struct device *dev,
7762306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int size;
8062306a36Sopenharmony_ci	struct atxp1_data *data;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	data = atxp1_update_device(dev);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
8562306a36Sopenharmony_ci						 data->vrm));
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return size;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic ssize_t cpu0_vid_store(struct device *dev,
9162306a36Sopenharmony_ci			      struct device_attribute *attr, const char *buf,
9262306a36Sopenharmony_ci			      size_t count)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct atxp1_data *data = atxp1_update_device(dev);
9562306a36Sopenharmony_ci	struct i2c_client *client = data->client;
9662306a36Sopenharmony_ci	int vid, cvid;
9762306a36Sopenharmony_ci	unsigned long vcore;
9862306a36Sopenharmony_ci	int err;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	err = kstrtoul(buf, 10, &vcore);
10162306a36Sopenharmony_ci	if (err)
10262306a36Sopenharmony_ci		return err;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	vcore /= 25;
10562306a36Sopenharmony_ci	vcore *= 25;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Calculate VID */
10862306a36Sopenharmony_ci	vid = vid_to_reg(vcore, data->vrm);
10962306a36Sopenharmony_ci	if (vid < 0) {
11062306a36Sopenharmony_ci		dev_err(dev, "VID calculation failed.\n");
11162306a36Sopenharmony_ci		return vid;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * If output enabled, use control register value.
11662306a36Sopenharmony_ci	 * Otherwise original CPU VID
11762306a36Sopenharmony_ci	 */
11862306a36Sopenharmony_ci	if (data->reg.vid & ATXP1_VIDENA)
11962306a36Sopenharmony_ci		cvid = data->reg.vid & ATXP1_VIDMASK;
12062306a36Sopenharmony_ci	else
12162306a36Sopenharmony_ci		cvid = data->reg.cpu_vid;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Nothing changed, aborting */
12462306a36Sopenharmony_ci	if (vid == cvid)
12562306a36Sopenharmony_ci		return count;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* Write every 25 mV step to increase stability */
13062306a36Sopenharmony_ci	if (cvid > vid) {
13162306a36Sopenharmony_ci		for (; cvid >= vid; cvid--)
13262306a36Sopenharmony_ci			i2c_smbus_write_byte_data(client,
13362306a36Sopenharmony_ci						ATXP1_VID, cvid | ATXP1_VIDENA);
13462306a36Sopenharmony_ci	} else {
13562306a36Sopenharmony_ci		for (; cvid <= vid; cvid++)
13662306a36Sopenharmony_ci			i2c_smbus_write_byte_data(client,
13762306a36Sopenharmony_ci						ATXP1_VID, cvid | ATXP1_VIDENA);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	data->valid = false;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return count;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * CPU core reference voltage
14762306a36Sopenharmony_ci * unit: millivolt
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(cpu0_vid);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* sys file functions for GPIO1 */
15262306a36Sopenharmony_cistatic ssize_t gpio1_show(struct device *dev, struct device_attribute *attr,
15362306a36Sopenharmony_ci			  char *buf)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	int size;
15662306a36Sopenharmony_ci	struct atxp1_data *data;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	data = atxp1_update_device(dev);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return size;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic ssize_t gpio1_store(struct device *dev, struct device_attribute *attr,
16662306a36Sopenharmony_ci			   const char *buf, size_t count)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct atxp1_data *data = atxp1_update_device(dev);
16962306a36Sopenharmony_ci	struct i2c_client *client = data->client;
17062306a36Sopenharmony_ci	unsigned long value;
17162306a36Sopenharmony_ci	int err;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	err = kstrtoul(buf, 16, &value);
17462306a36Sopenharmony_ci	if (err)
17562306a36Sopenharmony_ci		return err;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	value &= ATXP1_GPIO1MASK;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
18062306a36Sopenharmony_ci		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		data->valid = false;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return count;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * GPIO1 data register
19262306a36Sopenharmony_ci * unit: Four bit as hex (e.g. 0x0f)
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(gpio1);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/* sys file functions for GPIO2 */
19762306a36Sopenharmony_cistatic ssize_t gpio2_show(struct device *dev, struct device_attribute *attr,
19862306a36Sopenharmony_ci			  char *buf)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int size;
20162306a36Sopenharmony_ci	struct atxp1_data *data;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	data = atxp1_update_device(dev);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return size;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic ssize_t gpio2_store(struct device *dev, struct device_attribute *attr,
21162306a36Sopenharmony_ci			   const char *buf, size_t count)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct atxp1_data *data = atxp1_update_device(dev);
21462306a36Sopenharmony_ci	struct i2c_client *client = data->client;
21562306a36Sopenharmony_ci	unsigned long value;
21662306a36Sopenharmony_ci	int err;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	err = kstrtoul(buf, 16, &value);
21962306a36Sopenharmony_ci	if (err)
22062306a36Sopenharmony_ci		return err;
22162306a36Sopenharmony_ci	value &= 0xff;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (value != data->reg.gpio2) {
22462306a36Sopenharmony_ci		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		data->valid = false;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return count;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * GPIO2 data register
23662306a36Sopenharmony_ci * unit: Eight bit as hex (e.g. 0xff)
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(gpio2);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic struct attribute *atxp1_attrs[] = {
24162306a36Sopenharmony_ci	&dev_attr_gpio1.attr,
24262306a36Sopenharmony_ci	&dev_attr_gpio2.attr,
24362306a36Sopenharmony_ci	&dev_attr_cpu0_vid.attr,
24462306a36Sopenharmony_ci	NULL
24562306a36Sopenharmony_ci};
24662306a36Sopenharmony_ciATTRIBUTE_GROUPS(atxp1);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int atxp1_probe(struct i2c_client *client)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct device *dev = &client->dev;
25162306a36Sopenharmony_ci	struct atxp1_data *data;
25262306a36Sopenharmony_ci	struct device *hwmon_dev;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL);
25562306a36Sopenharmony_ci	if (!data)
25662306a36Sopenharmony_ci		return -ENOMEM;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Get VRM */
25962306a36Sopenharmony_ci	data->vrm = vid_which_vrm();
26062306a36Sopenharmony_ci	if (data->vrm != 90 && data->vrm != 91) {
26162306a36Sopenharmony_ci		dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
26262306a36Sopenharmony_ci			data->vrm / 10, data->vrm % 10);
26362306a36Sopenharmony_ci		return -ENODEV;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	data->client = client;
26762306a36Sopenharmony_ci	mutex_init(&data->update_lock);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
27062306a36Sopenharmony_ci							   data,
27162306a36Sopenharmony_ci							   atxp1_groups);
27262306a36Sopenharmony_ci	if (IS_ERR(hwmon_dev))
27362306a36Sopenharmony_ci		return PTR_ERR(hwmon_dev);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic const struct i2c_device_id atxp1_id[] = {
28162306a36Sopenharmony_ci	{ "atxp1", 0 },
28262306a36Sopenharmony_ci	{ }
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, atxp1_id);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic struct i2c_driver atxp1_driver = {
28762306a36Sopenharmony_ci	.class		= I2C_CLASS_HWMON,
28862306a36Sopenharmony_ci	.driver = {
28962306a36Sopenharmony_ci		.name	= "atxp1",
29062306a36Sopenharmony_ci	},
29162306a36Sopenharmony_ci	.probe		= atxp1_probe,
29262306a36Sopenharmony_ci	.id_table	= atxp1_id,
29362306a36Sopenharmony_ci};
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cimodule_i2c_driver(atxp1_driver);
296