xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision f0967eea80ec2a19a4fe1ad27e3ff1b22c79a3c7)
18d5d45fbSJean Delvare /*
28d5d45fbSJean Delvare     atxp1.c - kernel module for setting CPU VID and general purpose
38d5d45fbSJean Delvare                      I/Os using the Attansic ATXP1 chip.
48d5d45fbSJean Delvare 
58d5d45fbSJean Delvare     This program is free software; you can redistribute it and/or modify
68d5d45fbSJean Delvare     it under the terms of the GNU General Public License as published by
78d5d45fbSJean Delvare     the Free Software Foundation; either version 2 of the License, or
88d5d45fbSJean Delvare     (at your option) any later version.
98d5d45fbSJean Delvare 
108d5d45fbSJean Delvare     This program is distributed in the hope that it will be useful,
118d5d45fbSJean Delvare     but WITHOUT ANY WARRANTY; without even the implied warranty of
128d5d45fbSJean Delvare     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
138d5d45fbSJean Delvare     GNU General Public License for more details.
148d5d45fbSJean Delvare 
158d5d45fbSJean Delvare     You should have received a copy of the GNU General Public License
168d5d45fbSJean Delvare     along with this program; if not, write to the Free Software
178d5d45fbSJean Delvare     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
188d5d45fbSJean Delvare 
198d5d45fbSJean Delvare */
208d5d45fbSJean Delvare 
218d5d45fbSJean Delvare #include <linux/kernel.h>
228d5d45fbSJean Delvare #include <linux/init.h>
238d5d45fbSJean Delvare #include <linux/module.h>
240cacdf29SJean Delvare #include <linux/jiffies.h>
258d5d45fbSJean Delvare #include <linux/i2c.h>
26943b0830SMark M. Hoffman #include <linux/hwmon.h>
27303760b4SJean Delvare #include <linux/hwmon-vid.h>
28943b0830SMark M. Hoffman #include <linux/err.h>
299a61bf63SIngo Molnar #include <linux/mutex.h>
30a5ebe668SJean Delvare #include <linux/sysfs.h>
315a0e3ad6STejun Heo #include <linux/slab.h>
328d5d45fbSJean Delvare 
338d5d45fbSJean Delvare MODULE_LICENSE("GPL");
348d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
3513b3c3faSJean Delvare MODULE_VERSION("0.6.3");
368d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
378d5d45fbSJean Delvare 
388d5d45fbSJean Delvare #define ATXP1_VID	0x00
398d5d45fbSJean Delvare #define ATXP1_CVID	0x01
408d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
418d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
428d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
438d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
448d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
458d5d45fbSJean Delvare 
4625e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
478d5d45fbSJean Delvare 
4871163c7cSJean Delvare static int atxp1_probe(struct i2c_client *client,
4971163c7cSJean Delvare 		       const struct i2c_device_id *id);
5071163c7cSJean Delvare static int atxp1_remove(struct i2c_client *client);
518d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev);
52310ec792SJean Delvare static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info);
5371163c7cSJean Delvare 
5471163c7cSJean Delvare static const struct i2c_device_id atxp1_id[] = {
551f86df49SJean Delvare 	{ "atxp1", 0 },
5671163c7cSJean Delvare 	{ }
5771163c7cSJean Delvare };
5871163c7cSJean Delvare MODULE_DEVICE_TABLE(i2c, atxp1_id);
598d5d45fbSJean Delvare 
608d5d45fbSJean Delvare static struct i2c_driver atxp1_driver = {
6171163c7cSJean Delvare 	.class		= I2C_CLASS_HWMON,
62cdaf7934SLaurent Riffard 	.driver = {
638d5d45fbSJean Delvare 		.name	= "atxp1",
64cdaf7934SLaurent Riffard 	},
6571163c7cSJean Delvare 	.probe		= atxp1_probe,
6671163c7cSJean Delvare 	.remove		= atxp1_remove,
6771163c7cSJean Delvare 	.id_table	= atxp1_id,
6871163c7cSJean Delvare 	.detect		= atxp1_detect,
69c3813d6aSJean Delvare 	.address_list	= normal_i2c,
708d5d45fbSJean Delvare };
718d5d45fbSJean Delvare 
728d5d45fbSJean Delvare struct atxp1_data {
731beeffe4STony Jones 	struct device *hwmon_dev;
749a61bf63SIngo Molnar 	struct mutex update_lock;
758d5d45fbSJean Delvare 	unsigned long last_updated;
768d5d45fbSJean Delvare 	u8 valid;
778d5d45fbSJean Delvare 	struct {
788d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
798d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
808d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
818d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
828d5d45fbSJean Delvare 	} reg;
838d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
848d5d45fbSJean Delvare };
858d5d45fbSJean Delvare 
868d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev)
878d5d45fbSJean Delvare {
888d5d45fbSJean Delvare 	struct i2c_client *client;
898d5d45fbSJean Delvare 	struct atxp1_data *data;
908d5d45fbSJean Delvare 
918d5d45fbSJean Delvare 	client = to_i2c_client(dev);
928d5d45fbSJean Delvare 	data = i2c_get_clientdata(client);
938d5d45fbSJean Delvare 
949a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
958d5d45fbSJean Delvare 
960cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
978d5d45fbSJean Delvare 
988d5d45fbSJean Delvare 		/* Update local register data */
998d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
1008d5d45fbSJean Delvare 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
1018d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
1028d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
1038d5d45fbSJean Delvare 
1048d5d45fbSJean Delvare 		data->valid = 1;
1058d5d45fbSJean Delvare 	}
1068d5d45fbSJean Delvare 
1079a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
1088d5d45fbSJean Delvare 
1097fe83ad8SFrans Meulenbroeks 	return data;
1108d5d45fbSJean Delvare }
1118d5d45fbSJean Delvare 
1128d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
1138d5d45fbSJean Delvare static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
1148d5d45fbSJean Delvare {
1158d5d45fbSJean Delvare 	int size;
1168d5d45fbSJean Delvare 	struct atxp1_data *data;
1178d5d45fbSJean Delvare 
1188d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1198d5d45fbSJean Delvare 
1208d5d45fbSJean Delvare 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
1218d5d45fbSJean Delvare 
1228d5d45fbSJean Delvare 	return size;
1238d5d45fbSJean Delvare }
1248d5d45fbSJean Delvare 
1258d5d45fbSJean Delvare static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1268d5d45fbSJean Delvare {
1278d5d45fbSJean Delvare 	struct atxp1_data *data;
1288d5d45fbSJean Delvare 	struct i2c_client *client;
129c41bdb52SAlexey Dobriyan 	int vid, cvid;
1308d5d45fbSJean Delvare 	unsigned int vcore;
1318d5d45fbSJean Delvare 
1328d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1338d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1348d5d45fbSJean Delvare 
1358d5d45fbSJean Delvare 	vcore = simple_strtoul(buf, NULL, 10);
1368d5d45fbSJean Delvare 	vcore /= 25;
1378d5d45fbSJean Delvare 	vcore *= 25;
1388d5d45fbSJean Delvare 
1398d5d45fbSJean Delvare 	/* Calculate VID */
1408d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1418d5d45fbSJean Delvare 
1428d5d45fbSJean Delvare 	if (vid < 0) {
1438d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
1448d5d45fbSJean Delvare 		return -1;
1458d5d45fbSJean Delvare 	}
1468d5d45fbSJean Delvare 
1478d5d45fbSJean Delvare 	/* If output enabled, use control register value. Otherwise original CPU VID */
1488d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1498d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1508d5d45fbSJean Delvare 	else
1518d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1528d5d45fbSJean Delvare 
1538d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1548d5d45fbSJean Delvare 	if (vid == cvid)
1558d5d45fbSJean Delvare 		return count;
1568d5d45fbSJean Delvare 
1578d5d45fbSJean Delvare 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid);
1588d5d45fbSJean Delvare 
1598d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1608d5d45fbSJean Delvare 	if (cvid > vid) {
1618d5d45fbSJean Delvare 		for (; cvid >= vid; cvid--) {
1628d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1638d5d45fbSJean Delvare 		}
1648d5d45fbSJean Delvare 	}
1658d5d45fbSJean Delvare 	else {
1668d5d45fbSJean Delvare 		for (; cvid <= vid; cvid++) {
1678d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1688d5d45fbSJean Delvare 		}
1698d5d45fbSJean Delvare 	}
1708d5d45fbSJean Delvare 
1718d5d45fbSJean Delvare 	data->valid = 0;
1728d5d45fbSJean Delvare 
1738d5d45fbSJean Delvare 	return count;
1748d5d45fbSJean Delvare }
1758d5d45fbSJean Delvare 
1768d5d45fbSJean Delvare /* CPU core reference voltage
1778d5d45fbSJean Delvare     unit: millivolt
1788d5d45fbSJean Delvare */
1798d5d45fbSJean Delvare static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
1808d5d45fbSJean Delvare 
1818d5d45fbSJean Delvare /* sys file functions for GPIO1 */
1828d5d45fbSJean Delvare static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
1838d5d45fbSJean Delvare {
1848d5d45fbSJean Delvare 	int size;
1858d5d45fbSJean Delvare 	struct atxp1_data *data;
1868d5d45fbSJean Delvare 
1878d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1888d5d45fbSJean Delvare 
1898d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1908d5d45fbSJean Delvare 
1918d5d45fbSJean Delvare 	return size;
1928d5d45fbSJean Delvare }
1938d5d45fbSJean Delvare 
1948d5d45fbSJean Delvare static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
1958d5d45fbSJean Delvare {
1968d5d45fbSJean Delvare 	struct atxp1_data *data;
1978d5d45fbSJean Delvare 	struct i2c_client *client;
1988d5d45fbSJean Delvare 	unsigned int value;
1998d5d45fbSJean Delvare 
2008d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2018d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2028d5d45fbSJean Delvare 
2038d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16);
2048d5d45fbSJean Delvare 
2058d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
2068d5d45fbSJean Delvare 
2078d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
2088d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2098d5d45fbSJean Delvare 
2108d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
2118d5d45fbSJean Delvare 
2128d5d45fbSJean Delvare 		data->valid = 0;
2138d5d45fbSJean Delvare 	}
2148d5d45fbSJean Delvare 
2158d5d45fbSJean Delvare 	return count;
2168d5d45fbSJean Delvare }
2178d5d45fbSJean Delvare 
2188d5d45fbSJean Delvare /* GPIO1 data register
2198d5d45fbSJean Delvare     unit: Four bit as hex (e.g. 0x0f)
2208d5d45fbSJean Delvare */
2218d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2228d5d45fbSJean Delvare 
2238d5d45fbSJean Delvare /* sys file functions for GPIO2 */
2248d5d45fbSJean Delvare static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
2258d5d45fbSJean Delvare {
2268d5d45fbSJean Delvare 	int size;
2278d5d45fbSJean Delvare 	struct atxp1_data *data;
2288d5d45fbSJean Delvare 
2298d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2308d5d45fbSJean Delvare 
2318d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2328d5d45fbSJean Delvare 
2338d5d45fbSJean Delvare 	return size;
2348d5d45fbSJean Delvare }
2358d5d45fbSJean Delvare 
2368d5d45fbSJean Delvare static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
2378d5d45fbSJean Delvare {
2388d5d45fbSJean Delvare 	struct atxp1_data *data;
2398d5d45fbSJean Delvare 	struct i2c_client *client;
2408d5d45fbSJean Delvare 	unsigned int value;
2418d5d45fbSJean Delvare 
2428d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2438d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2448d5d45fbSJean Delvare 
2458d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16) & 0xff;
2468d5d45fbSJean Delvare 
2478d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
2488d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2498d5d45fbSJean Delvare 
2508d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2518d5d45fbSJean Delvare 
2528d5d45fbSJean Delvare 		data->valid = 0;
2538d5d45fbSJean Delvare 	}
2548d5d45fbSJean Delvare 
2558d5d45fbSJean Delvare 	return count;
2568d5d45fbSJean Delvare }
2578d5d45fbSJean Delvare 
2588d5d45fbSJean Delvare /* GPIO2 data register
2598d5d45fbSJean Delvare     unit: Eight bit as hex (e.g. 0xff)
2608d5d45fbSJean Delvare */
2618d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2628d5d45fbSJean Delvare 
263a5ebe668SJean Delvare static struct attribute *atxp1_attributes[] = {
264a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
265a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
266a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
267a5ebe668SJean Delvare 	NULL
268a5ebe668SJean Delvare };
269a5ebe668SJean Delvare 
270a5ebe668SJean Delvare static const struct attribute_group atxp1_group = {
271a5ebe668SJean Delvare 	.attrs = atxp1_attributes,
272a5ebe668SJean Delvare };
273a5ebe668SJean Delvare 
2748d5d45fbSJean Delvare 
27571163c7cSJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
276310ec792SJean Delvare static int atxp1_detect(struct i2c_client *new_client,
27771163c7cSJean Delvare 			struct i2c_board_info *info)
2788d5d45fbSJean Delvare {
27971163c7cSJean Delvare 	struct i2c_adapter *adapter = new_client->adapter;
2808d5d45fbSJean Delvare 
2818d5d45fbSJean Delvare 	u8 temp;
2828d5d45fbSJean Delvare 
2838d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
28471163c7cSJean Delvare 		return -ENODEV;
2858d5d45fbSJean Delvare 
2868d5d45fbSJean Delvare 	/* Detect ATXP1, checking if vendor ID registers are all zero */
2878d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
2888d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
2898d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
29013b3c3faSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
29113b3c3faSJean Delvare 		return -ENODEV;
2928d5d45fbSJean Delvare 
2938d5d45fbSJean Delvare 	/* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
2948d5d45fbSJean Delvare 	 * showing the same as register 0x00 */
2958d5d45fbSJean Delvare 	temp = i2c_smbus_read_byte_data(new_client, 0x00);
2968d5d45fbSJean Delvare 
2978d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
2988d5d45fbSJean Delvare 	      (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
29971163c7cSJean Delvare 		return -ENODEV;
30071163c7cSJean Delvare 
30171163c7cSJean Delvare 	/* Get VRM */
30271163c7cSJean Delvare 	temp = vid_which_vrm();
30371163c7cSJean Delvare 
30471163c7cSJean Delvare 	if ((temp != 90) && (temp != 91)) {
30571163c7cSJean Delvare 		dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
30671163c7cSJean Delvare 				temp / 10, temp % 10);
30771163c7cSJean Delvare 		return -ENODEV;
30871163c7cSJean Delvare 	}
30971163c7cSJean Delvare 
31071163c7cSJean Delvare 	strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
31171163c7cSJean Delvare 
31271163c7cSJean Delvare 	return 0;
31371163c7cSJean Delvare }
31471163c7cSJean Delvare 
31571163c7cSJean Delvare static int atxp1_probe(struct i2c_client *new_client,
31671163c7cSJean Delvare 		       const struct i2c_device_id *id)
31771163c7cSJean Delvare {
31871163c7cSJean Delvare 	struct atxp1_data *data;
31971163c7cSJean Delvare 	int err;
32071163c7cSJean Delvare 
32171163c7cSJean Delvare 	data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL);
32271163c7cSJean Delvare 	if (!data) {
32371163c7cSJean Delvare 		err = -ENOMEM;
32471163c7cSJean Delvare 		goto exit;
3258d5d45fbSJean Delvare 	}
3268d5d45fbSJean Delvare 
3278d5d45fbSJean Delvare 	/* Get VRM */
328303760b4SJean Delvare 	data->vrm = vid_which_vrm();
3298d5d45fbSJean Delvare 
33071163c7cSJean Delvare 	i2c_set_clientdata(new_client, data);
3318d5d45fbSJean Delvare 	data->valid = 0;
3328d5d45fbSJean Delvare 
3339a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
3348d5d45fbSJean Delvare 
335a5ebe668SJean Delvare 	/* Register sysfs hooks */
336a5ebe668SJean Delvare 	if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
33771163c7cSJean Delvare 		goto exit_free;
338a5ebe668SJean Delvare 
3391beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&new_client->dev);
3401beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
3411beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
342a5ebe668SJean Delvare 		goto exit_remove_files;
343943b0830SMark M. Hoffman 	}
344943b0830SMark M. Hoffman 
3458d5d45fbSJean Delvare 	dev_info(&new_client->dev, "Using VRM: %d.%d\n",
3468d5d45fbSJean Delvare 			 data->vrm / 10, data->vrm % 10);
3478d5d45fbSJean Delvare 
3488d5d45fbSJean Delvare 	return 0;
3498d5d45fbSJean Delvare 
350a5ebe668SJean Delvare exit_remove_files:
351a5ebe668SJean Delvare 	sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
3528d5d45fbSJean Delvare exit_free:
3538d5d45fbSJean Delvare 	kfree(data);
3548d5d45fbSJean Delvare exit:
3558d5d45fbSJean Delvare 	return err;
3568d5d45fbSJean Delvare };
3578d5d45fbSJean Delvare 
35871163c7cSJean Delvare static int atxp1_remove(struct i2c_client *client)
3598d5d45fbSJean Delvare {
360943b0830SMark M. Hoffman 	struct atxp1_data * data = i2c_get_clientdata(client);
3618d5d45fbSJean Delvare 
3621beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
363a5ebe668SJean Delvare 	sysfs_remove_group(&client->dev.kobj, &atxp1_group);
364943b0830SMark M. Hoffman 
365943b0830SMark M. Hoffman 	kfree(data);
3668d5d45fbSJean Delvare 
36771163c7cSJean Delvare 	return 0;
3688d5d45fbSJean Delvare };
3698d5d45fbSJean Delvare 
370*f0967eeaSAxel Lin module_i2c_driver(atxp1_driver);
371