xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision 310ec79210d754afe51e2e4a983e846b60179abd)
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>
318d5d45fbSJean Delvare 
328d5d45fbSJean Delvare MODULE_LICENSE("GPL");
338d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
3413b3c3faSJean Delvare MODULE_VERSION("0.6.3");
358d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
368d5d45fbSJean Delvare 
378d5d45fbSJean Delvare #define ATXP1_VID	0x00
388d5d45fbSJean Delvare #define ATXP1_CVID	0x01
398d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
408d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
418d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
428d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
438d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
448d5d45fbSJean Delvare 
4525e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
468d5d45fbSJean Delvare 
47f4b50261SJean Delvare I2C_CLIENT_INSMOD_1(atxp1);
488d5d45fbSJean Delvare 
4971163c7cSJean Delvare static int atxp1_probe(struct i2c_client *client,
5071163c7cSJean Delvare 		       const struct i2c_device_id *id);
5171163c7cSJean Delvare static int atxp1_remove(struct i2c_client *client);
528d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev);
53*310ec792SJean Delvare static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info);
5471163c7cSJean Delvare 
5571163c7cSJean Delvare static const struct i2c_device_id atxp1_id[] = {
5671163c7cSJean Delvare 	{ "atxp1", atxp1 },
5771163c7cSJean Delvare 	{ }
5871163c7cSJean Delvare };
5971163c7cSJean Delvare MODULE_DEVICE_TABLE(i2c, atxp1_id);
608d5d45fbSJean Delvare 
618d5d45fbSJean Delvare static struct i2c_driver atxp1_driver = {
6271163c7cSJean Delvare 	.class		= I2C_CLASS_HWMON,
63cdaf7934SLaurent Riffard 	.driver = {
648d5d45fbSJean Delvare 		.name	= "atxp1",
65cdaf7934SLaurent Riffard 	},
6671163c7cSJean Delvare 	.probe		= atxp1_probe,
6771163c7cSJean Delvare 	.remove		= atxp1_remove,
6871163c7cSJean Delvare 	.id_table	= atxp1_id,
6971163c7cSJean Delvare 	.detect		= atxp1_detect,
7071163c7cSJean Delvare 	.address_data	= &addr_data,
718d5d45fbSJean Delvare };
728d5d45fbSJean Delvare 
738d5d45fbSJean Delvare struct atxp1_data {
741beeffe4STony Jones 	struct device *hwmon_dev;
759a61bf63SIngo Molnar 	struct mutex update_lock;
768d5d45fbSJean Delvare 	unsigned long last_updated;
778d5d45fbSJean Delvare 	u8 valid;
788d5d45fbSJean Delvare 	struct {
798d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
808d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
818d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
828d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
838d5d45fbSJean Delvare 	} reg;
848d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
858d5d45fbSJean Delvare };
868d5d45fbSJean Delvare 
878d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev)
888d5d45fbSJean Delvare {
898d5d45fbSJean Delvare 	struct i2c_client *client;
908d5d45fbSJean Delvare 	struct atxp1_data *data;
918d5d45fbSJean Delvare 
928d5d45fbSJean Delvare 	client = to_i2c_client(dev);
938d5d45fbSJean Delvare 	data = i2c_get_clientdata(client);
948d5d45fbSJean Delvare 
959a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
968d5d45fbSJean Delvare 
970cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
988d5d45fbSJean Delvare 
998d5d45fbSJean Delvare 		/* Update local register data */
1008d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
1018d5d45fbSJean Delvare 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
1028d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
1038d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
1048d5d45fbSJean Delvare 
1058d5d45fbSJean Delvare 		data->valid = 1;
1068d5d45fbSJean Delvare 	}
1078d5d45fbSJean Delvare 
1089a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
1098d5d45fbSJean Delvare 
1108d5d45fbSJean Delvare 	return(data);
1118d5d45fbSJean Delvare }
1128d5d45fbSJean Delvare 
1138d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
1148d5d45fbSJean Delvare static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
1158d5d45fbSJean Delvare {
1168d5d45fbSJean Delvare 	int size;
1178d5d45fbSJean Delvare 	struct atxp1_data *data;
1188d5d45fbSJean Delvare 
1198d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1208d5d45fbSJean Delvare 
1218d5d45fbSJean Delvare 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
1228d5d45fbSJean Delvare 
1238d5d45fbSJean Delvare 	return size;
1248d5d45fbSJean Delvare }
1258d5d45fbSJean Delvare 
1268d5d45fbSJean Delvare static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1278d5d45fbSJean Delvare {
1288d5d45fbSJean Delvare 	struct atxp1_data *data;
1298d5d45fbSJean Delvare 	struct i2c_client *client;
130c41bdb52SAlexey Dobriyan 	int vid, cvid;
1318d5d45fbSJean Delvare 	unsigned int vcore;
1328d5d45fbSJean Delvare 
1338d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1348d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1358d5d45fbSJean Delvare 
1368d5d45fbSJean Delvare 	vcore = simple_strtoul(buf, NULL, 10);
1378d5d45fbSJean Delvare 	vcore /= 25;
1388d5d45fbSJean Delvare 	vcore *= 25;
1398d5d45fbSJean Delvare 
1408d5d45fbSJean Delvare 	/* Calculate VID */
1418d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1428d5d45fbSJean Delvare 
1438d5d45fbSJean Delvare 	if (vid < 0) {
1448d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
1458d5d45fbSJean Delvare 		return -1;
1468d5d45fbSJean Delvare 	}
1478d5d45fbSJean Delvare 
1488d5d45fbSJean Delvare 	/* If output enabled, use control register value. Otherwise original CPU VID */
1498d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1508d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1518d5d45fbSJean Delvare 	else
1528d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1538d5d45fbSJean Delvare 
1548d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1558d5d45fbSJean Delvare 	if (vid == cvid)
1568d5d45fbSJean Delvare 		return count;
1578d5d45fbSJean Delvare 
1588d5d45fbSJean Delvare 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid);
1598d5d45fbSJean Delvare 
1608d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1618d5d45fbSJean Delvare 	if (cvid > vid) {
1628d5d45fbSJean Delvare 		for (; cvid >= vid; cvid--) {
1638d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1648d5d45fbSJean Delvare 		}
1658d5d45fbSJean Delvare 	}
1668d5d45fbSJean Delvare 	else {
1678d5d45fbSJean Delvare 		for (; cvid <= vid; cvid++) {
1688d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1698d5d45fbSJean Delvare 		}
1708d5d45fbSJean Delvare 	}
1718d5d45fbSJean Delvare 
1728d5d45fbSJean Delvare 	data->valid = 0;
1738d5d45fbSJean Delvare 
1748d5d45fbSJean Delvare 	return count;
1758d5d45fbSJean Delvare }
1768d5d45fbSJean Delvare 
1778d5d45fbSJean Delvare /* CPU core reference voltage
1788d5d45fbSJean Delvare     unit: millivolt
1798d5d45fbSJean Delvare */
1808d5d45fbSJean Delvare static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
1818d5d45fbSJean Delvare 
1828d5d45fbSJean Delvare /* sys file functions for GPIO1 */
1838d5d45fbSJean Delvare static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
1848d5d45fbSJean Delvare {
1858d5d45fbSJean Delvare 	int size;
1868d5d45fbSJean Delvare 	struct atxp1_data *data;
1878d5d45fbSJean Delvare 
1888d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1898d5d45fbSJean Delvare 
1908d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1918d5d45fbSJean Delvare 
1928d5d45fbSJean Delvare 	return size;
1938d5d45fbSJean Delvare }
1948d5d45fbSJean Delvare 
1958d5d45fbSJean Delvare static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
1968d5d45fbSJean Delvare {
1978d5d45fbSJean Delvare 	struct atxp1_data *data;
1988d5d45fbSJean Delvare 	struct i2c_client *client;
1998d5d45fbSJean Delvare 	unsigned int value;
2008d5d45fbSJean Delvare 
2018d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2028d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2038d5d45fbSJean Delvare 
2048d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16);
2058d5d45fbSJean Delvare 
2068d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
2078d5d45fbSJean Delvare 
2088d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
2098d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2108d5d45fbSJean Delvare 
2118d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
2128d5d45fbSJean Delvare 
2138d5d45fbSJean Delvare 		data->valid = 0;
2148d5d45fbSJean Delvare 	}
2158d5d45fbSJean Delvare 
2168d5d45fbSJean Delvare 	return count;
2178d5d45fbSJean Delvare }
2188d5d45fbSJean Delvare 
2198d5d45fbSJean Delvare /* GPIO1 data register
2208d5d45fbSJean Delvare     unit: Four bit as hex (e.g. 0x0f)
2218d5d45fbSJean Delvare */
2228d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2238d5d45fbSJean Delvare 
2248d5d45fbSJean Delvare /* sys file functions for GPIO2 */
2258d5d45fbSJean Delvare static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
2268d5d45fbSJean Delvare {
2278d5d45fbSJean Delvare 	int size;
2288d5d45fbSJean Delvare 	struct atxp1_data *data;
2298d5d45fbSJean Delvare 
2308d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2318d5d45fbSJean Delvare 
2328d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2338d5d45fbSJean Delvare 
2348d5d45fbSJean Delvare 	return size;
2358d5d45fbSJean Delvare }
2368d5d45fbSJean Delvare 
2378d5d45fbSJean Delvare static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
2388d5d45fbSJean Delvare {
2398d5d45fbSJean Delvare 	struct atxp1_data *data;
2408d5d45fbSJean Delvare 	struct i2c_client *client;
2418d5d45fbSJean Delvare 	unsigned int value;
2428d5d45fbSJean Delvare 
2438d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2448d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2458d5d45fbSJean Delvare 
2468d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16) & 0xff;
2478d5d45fbSJean Delvare 
2488d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
2498d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2508d5d45fbSJean Delvare 
2518d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2528d5d45fbSJean Delvare 
2538d5d45fbSJean Delvare 		data->valid = 0;
2548d5d45fbSJean Delvare 	}
2558d5d45fbSJean Delvare 
2568d5d45fbSJean Delvare 	return count;
2578d5d45fbSJean Delvare }
2588d5d45fbSJean Delvare 
2598d5d45fbSJean Delvare /* GPIO2 data register
2608d5d45fbSJean Delvare     unit: Eight bit as hex (e.g. 0xff)
2618d5d45fbSJean Delvare */
2628d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2638d5d45fbSJean Delvare 
264a5ebe668SJean Delvare static struct attribute *atxp1_attributes[] = {
265a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
266a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
267a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
268a5ebe668SJean Delvare 	NULL
269a5ebe668SJean Delvare };
270a5ebe668SJean Delvare 
271a5ebe668SJean Delvare static const struct attribute_group atxp1_group = {
272a5ebe668SJean Delvare 	.attrs = atxp1_attributes,
273a5ebe668SJean Delvare };
274a5ebe668SJean Delvare 
2758d5d45fbSJean Delvare 
27671163c7cSJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
277*310ec792SJean Delvare static int atxp1_detect(struct i2c_client *new_client,
27871163c7cSJean Delvare 			struct i2c_board_info *info)
2798d5d45fbSJean Delvare {
28071163c7cSJean Delvare 	struct i2c_adapter *adapter = new_client->adapter;
2818d5d45fbSJean Delvare 
2828d5d45fbSJean Delvare 	u8 temp;
2838d5d45fbSJean Delvare 
2848d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
28571163c7cSJean Delvare 		return -ENODEV;
2868d5d45fbSJean Delvare 
2878d5d45fbSJean Delvare 	/* Detect ATXP1, checking if vendor ID registers are all zero */
2888d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
2898d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
2908d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
29113b3c3faSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
29213b3c3faSJean Delvare 		return -ENODEV;
2938d5d45fbSJean Delvare 
2948d5d45fbSJean Delvare 	/* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
2958d5d45fbSJean Delvare 	 * showing the same as register 0x00 */
2968d5d45fbSJean Delvare 	temp = i2c_smbus_read_byte_data(new_client, 0x00);
2978d5d45fbSJean Delvare 
2988d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
2998d5d45fbSJean Delvare 	      (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
30071163c7cSJean Delvare 		return -ENODEV;
30171163c7cSJean Delvare 
30271163c7cSJean Delvare 	/* Get VRM */
30371163c7cSJean Delvare 	temp = vid_which_vrm();
30471163c7cSJean Delvare 
30571163c7cSJean Delvare 	if ((temp != 90) && (temp != 91)) {
30671163c7cSJean Delvare 		dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
30771163c7cSJean Delvare 				temp / 10, temp % 10);
30871163c7cSJean Delvare 		return -ENODEV;
30971163c7cSJean Delvare 	}
31071163c7cSJean Delvare 
31171163c7cSJean Delvare 	strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
31271163c7cSJean Delvare 
31371163c7cSJean Delvare 	return 0;
31471163c7cSJean Delvare }
31571163c7cSJean Delvare 
31671163c7cSJean Delvare static int atxp1_probe(struct i2c_client *new_client,
31771163c7cSJean Delvare 		       const struct i2c_device_id *id)
31871163c7cSJean Delvare {
31971163c7cSJean Delvare 	struct atxp1_data *data;
32071163c7cSJean Delvare 	int err;
32171163c7cSJean Delvare 
32271163c7cSJean Delvare 	data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL);
32371163c7cSJean Delvare 	if (!data) {
32471163c7cSJean Delvare 		err = -ENOMEM;
32571163c7cSJean Delvare 		goto exit;
3268d5d45fbSJean Delvare 	}
3278d5d45fbSJean Delvare 
3288d5d45fbSJean Delvare 	/* Get VRM */
329303760b4SJean Delvare 	data->vrm = vid_which_vrm();
3308d5d45fbSJean Delvare 
33171163c7cSJean Delvare 	i2c_set_clientdata(new_client, data);
3328d5d45fbSJean Delvare 	data->valid = 0;
3338d5d45fbSJean Delvare 
3349a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
3358d5d45fbSJean Delvare 
336a5ebe668SJean Delvare 	/* Register sysfs hooks */
337a5ebe668SJean Delvare 	if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
33871163c7cSJean Delvare 		goto exit_free;
339a5ebe668SJean Delvare 
3401beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&new_client->dev);
3411beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
3421beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
343a5ebe668SJean Delvare 		goto exit_remove_files;
344943b0830SMark M. Hoffman 	}
345943b0830SMark M. Hoffman 
3468d5d45fbSJean Delvare 	dev_info(&new_client->dev, "Using VRM: %d.%d\n",
3478d5d45fbSJean Delvare 			 data->vrm / 10, data->vrm % 10);
3488d5d45fbSJean Delvare 
3498d5d45fbSJean Delvare 	return 0;
3508d5d45fbSJean Delvare 
351a5ebe668SJean Delvare exit_remove_files:
352a5ebe668SJean Delvare 	sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
3538d5d45fbSJean Delvare exit_free:
3548d5d45fbSJean Delvare 	kfree(data);
3558d5d45fbSJean Delvare exit:
3568d5d45fbSJean Delvare 	return err;
3578d5d45fbSJean Delvare };
3588d5d45fbSJean Delvare 
35971163c7cSJean Delvare static int atxp1_remove(struct i2c_client *client)
3608d5d45fbSJean Delvare {
361943b0830SMark M. Hoffman 	struct atxp1_data * data = i2c_get_clientdata(client);
3628d5d45fbSJean Delvare 
3631beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
364a5ebe668SJean Delvare 	sysfs_remove_group(&client->dev.kobj, &atxp1_group);
365943b0830SMark M. Hoffman 
366943b0830SMark M. Hoffman 	kfree(data);
3678d5d45fbSJean Delvare 
36871163c7cSJean Delvare 	return 0;
3698d5d45fbSJean Delvare };
3708d5d45fbSJean Delvare 
3718d5d45fbSJean Delvare static int __init atxp1_init(void)
3728d5d45fbSJean Delvare {
3738d5d45fbSJean Delvare 	return i2c_add_driver(&atxp1_driver);
3748d5d45fbSJean Delvare };
3758d5d45fbSJean Delvare 
3768d5d45fbSJean Delvare static void __exit atxp1_exit(void)
3778d5d45fbSJean Delvare {
3788d5d45fbSJean Delvare 	i2c_del_driver(&atxp1_driver);
3798d5d45fbSJean Delvare };
3808d5d45fbSJean Delvare 
3818d5d45fbSJean Delvare module_init(atxp1_init);
3828d5d45fbSJean Delvare module_exit(atxp1_exit);
383