xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision d466a353052582ea784d9f5352c822d828d00244)
18d5d45fbSJean Delvare /*
2f24d548bSGuenter Roeck  * atxp1.c - kernel module for setting CPU VID and general purpose
3f24d548bSGuenter Roeck  *	     I/Os using the Attansic ATXP1 chip.
4f24d548bSGuenter Roeck  *
5f24d548bSGuenter Roeck  * This program is free software; you can redistribute it and/or modify
6f24d548bSGuenter Roeck  * it under the terms of the GNU General Public License as published by
7f24d548bSGuenter Roeck  * the Free Software Foundation; either version 2 of the License, or
8f24d548bSGuenter Roeck  * (at your option) any later version.
9f24d548bSGuenter Roeck  *
10f24d548bSGuenter Roeck  * This program is distributed in the hope that it will be useful,
11f24d548bSGuenter Roeck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12f24d548bSGuenter Roeck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13f24d548bSGuenter Roeck  * GNU General Public License for more details.
14f24d548bSGuenter Roeck  *
15f24d548bSGuenter Roeck  * You should have received a copy of the GNU General Public License
16f24d548bSGuenter Roeck  * along with this program; if not, write to the Free Software
17f24d548bSGuenter Roeck  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18f24d548bSGuenter Roeck  *
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);
100f24d548bSGuenter Roeck 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
101f24d548bSGuenter Roeck 							     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 
1107fe83ad8SFrans Meulenbroeks 	return data;
1118d5d45fbSJean Delvare }
1128d5d45fbSJean Delvare 
1138d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
114f24d548bSGuenter Roeck static ssize_t atxp1_showvcore(struct device *dev,
115f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
1168d5d45fbSJean Delvare {
1178d5d45fbSJean Delvare 	int size;
1188d5d45fbSJean Delvare 	struct atxp1_data *data;
1198d5d45fbSJean Delvare 
1208d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1218d5d45fbSJean Delvare 
122f24d548bSGuenter Roeck 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
123f24d548bSGuenter Roeck 						 data->vrm));
1248d5d45fbSJean Delvare 
1258d5d45fbSJean Delvare 	return size;
1268d5d45fbSJean Delvare }
1278d5d45fbSJean Delvare 
128f24d548bSGuenter Roeck static ssize_t atxp1_storevcore(struct device *dev,
129f24d548bSGuenter Roeck 				struct device_attribute *attr,
130f24d548bSGuenter Roeck 				const char *buf, size_t count)
1318d5d45fbSJean Delvare {
1328d5d45fbSJean Delvare 	struct atxp1_data *data;
1338d5d45fbSJean Delvare 	struct i2c_client *client;
134c41bdb52SAlexey Dobriyan 	int vid, cvid;
135f24d548bSGuenter Roeck 	unsigned long vcore;
136f24d548bSGuenter Roeck 	int err;
1378d5d45fbSJean Delvare 
1388d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1398d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1408d5d45fbSJean Delvare 
141f24d548bSGuenter Roeck 	err = kstrtoul(buf, 10, &vcore);
142f24d548bSGuenter Roeck 	if (err)
143f24d548bSGuenter Roeck 		return err;
144f24d548bSGuenter Roeck 
1458d5d45fbSJean Delvare 	vcore /= 25;
1468d5d45fbSJean Delvare 	vcore *= 25;
1478d5d45fbSJean Delvare 
1488d5d45fbSJean Delvare 	/* Calculate VID */
1498d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1508d5d45fbSJean Delvare 
1518d5d45fbSJean Delvare 	if (vid < 0) {
1528d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
1538d5d45fbSJean Delvare 		return -1;
1548d5d45fbSJean Delvare 	}
1558d5d45fbSJean Delvare 
156f24d548bSGuenter Roeck 	/*
157f24d548bSGuenter Roeck 	 * If output enabled, use control register value.
158f24d548bSGuenter Roeck 	 * Otherwise original CPU VID
159f24d548bSGuenter Roeck 	 */
1608d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1618d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1628d5d45fbSJean Delvare 	else
1638d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1648d5d45fbSJean Delvare 
1658d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1668d5d45fbSJean Delvare 	if (vid == cvid)
1678d5d45fbSJean Delvare 		return count;
1688d5d45fbSJean Delvare 
169f24d548bSGuenter Roeck 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
1708d5d45fbSJean Delvare 
1718d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1728d5d45fbSJean Delvare 	if (cvid > vid) {
173f24d548bSGuenter Roeck 		for (; cvid >= vid; cvid--)
174f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
175f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
176f24d548bSGuenter Roeck 	} else {
177f24d548bSGuenter Roeck 		for (; cvid <= vid; cvid++)
178f24d548bSGuenter Roeck 			i2c_smbus_write_byte_data(client,
179f24d548bSGuenter Roeck 						ATXP1_VID, cvid | ATXP1_VIDENA);
1808d5d45fbSJean Delvare 	}
1818d5d45fbSJean Delvare 
1828d5d45fbSJean Delvare 	data->valid = 0;
1838d5d45fbSJean Delvare 
1848d5d45fbSJean Delvare 	return count;
1858d5d45fbSJean Delvare }
1868d5d45fbSJean Delvare 
187f24d548bSGuenter Roeck /*
188f24d548bSGuenter Roeck  * CPU core reference voltage
189f24d548bSGuenter Roeck  * unit: millivolt
1908d5d45fbSJean Delvare  */
191f24d548bSGuenter Roeck static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore,
192f24d548bSGuenter Roeck 		   atxp1_storevcore);
1938d5d45fbSJean Delvare 
1948d5d45fbSJean Delvare /* sys file functions for GPIO1 */
195f24d548bSGuenter Roeck static ssize_t atxp1_showgpio1(struct device *dev,
196f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
1978d5d45fbSJean Delvare {
1988d5d45fbSJean Delvare 	int size;
1998d5d45fbSJean Delvare 	struct atxp1_data *data;
2008d5d45fbSJean Delvare 
2018d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2028d5d45fbSJean Delvare 
2038d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
2048d5d45fbSJean Delvare 
2058d5d45fbSJean Delvare 	return size;
2068d5d45fbSJean Delvare }
2078d5d45fbSJean Delvare 
208f24d548bSGuenter Roeck static ssize_t atxp1_storegpio1(struct device *dev,
209f24d548bSGuenter Roeck 				struct device_attribute *attr, const char *buf,
210f24d548bSGuenter Roeck 				size_t count)
2118d5d45fbSJean Delvare {
2128d5d45fbSJean Delvare 	struct atxp1_data *data;
2138d5d45fbSJean Delvare 	struct i2c_client *client;
214f24d548bSGuenter Roeck 	unsigned long value;
215f24d548bSGuenter Roeck 	int err;
2168d5d45fbSJean Delvare 
2178d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2188d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2198d5d45fbSJean Delvare 
220f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
221f24d548bSGuenter Roeck 	if (err)
222f24d548bSGuenter Roeck 		return err;
2238d5d45fbSJean Delvare 
2248d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
2258d5d45fbSJean Delvare 
2268d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
227f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
2288d5d45fbSJean Delvare 
2298d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
2308d5d45fbSJean Delvare 
2318d5d45fbSJean Delvare 		data->valid = 0;
2328d5d45fbSJean Delvare 	}
2338d5d45fbSJean Delvare 
2348d5d45fbSJean Delvare 	return count;
2358d5d45fbSJean Delvare }
2368d5d45fbSJean Delvare 
237f24d548bSGuenter Roeck /*
238f24d548bSGuenter Roeck  * GPIO1 data register
239f24d548bSGuenter Roeck  * unit: Four bit as hex (e.g. 0x0f)
2408d5d45fbSJean Delvare  */
2418d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2428d5d45fbSJean Delvare 
2438d5d45fbSJean Delvare /* sys file functions for GPIO2 */
244f24d548bSGuenter Roeck static ssize_t atxp1_showgpio2(struct device *dev,
245f24d548bSGuenter Roeck 			       struct device_attribute *attr, char *buf)
2468d5d45fbSJean Delvare {
2478d5d45fbSJean Delvare 	int size;
2488d5d45fbSJean Delvare 	struct atxp1_data *data;
2498d5d45fbSJean Delvare 
2508d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2518d5d45fbSJean Delvare 
2528d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2538d5d45fbSJean Delvare 
2548d5d45fbSJean Delvare 	return size;
2558d5d45fbSJean Delvare }
2568d5d45fbSJean Delvare 
257f24d548bSGuenter Roeck static ssize_t atxp1_storegpio2(struct device *dev,
258f24d548bSGuenter Roeck 				struct device_attribute *attr,
259f24d548bSGuenter Roeck 				const char *buf, size_t count)
2608d5d45fbSJean Delvare {
261f24d548bSGuenter Roeck 	struct atxp1_data *data = atxp1_update_device(dev);
262f24d548bSGuenter Roeck 	struct i2c_client *client = to_i2c_client(dev);
263f24d548bSGuenter Roeck 	unsigned long value;
264f24d548bSGuenter Roeck 	int err;
2658d5d45fbSJean Delvare 
266f24d548bSGuenter Roeck 	err = kstrtoul(buf, 16, &value);
267f24d548bSGuenter Roeck 	if (err)
268f24d548bSGuenter Roeck 		return err;
269f24d548bSGuenter Roeck 	value &= 0xff;
2708d5d45fbSJean Delvare 
2718d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
272f24d548bSGuenter Roeck 		dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
2738d5d45fbSJean Delvare 
2748d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2758d5d45fbSJean Delvare 
2768d5d45fbSJean Delvare 		data->valid = 0;
2778d5d45fbSJean Delvare 	}
2788d5d45fbSJean Delvare 
2798d5d45fbSJean Delvare 	return count;
2808d5d45fbSJean Delvare }
2818d5d45fbSJean Delvare 
282f24d548bSGuenter Roeck /*
283f24d548bSGuenter Roeck  * GPIO2 data register
284f24d548bSGuenter Roeck  * unit: Eight bit as hex (e.g. 0xff)
2858d5d45fbSJean Delvare  */
2868d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2878d5d45fbSJean Delvare 
288a5ebe668SJean Delvare static struct attribute *atxp1_attributes[] = {
289a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
290a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
291a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
292a5ebe668SJean Delvare 	NULL
293a5ebe668SJean Delvare };
294a5ebe668SJean Delvare 
295a5ebe668SJean Delvare static const struct attribute_group atxp1_group = {
296a5ebe668SJean Delvare 	.attrs = atxp1_attributes,
297a5ebe668SJean Delvare };
298a5ebe668SJean Delvare 
2998d5d45fbSJean Delvare 
30071163c7cSJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
301310ec792SJean Delvare static int atxp1_detect(struct i2c_client *new_client,
30271163c7cSJean Delvare 			struct i2c_board_info *info)
3038d5d45fbSJean Delvare {
30471163c7cSJean Delvare 	struct i2c_adapter *adapter = new_client->adapter;
3058d5d45fbSJean Delvare 
3068d5d45fbSJean Delvare 	u8 temp;
3078d5d45fbSJean Delvare 
3088d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
30971163c7cSJean Delvare 		return -ENODEV;
3108d5d45fbSJean Delvare 
3118d5d45fbSJean Delvare 	/* Detect ATXP1, checking if vendor ID registers are all zero */
3128d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
3138d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
3148d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
31513b3c3faSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
31613b3c3faSJean Delvare 		return -ENODEV;
3178d5d45fbSJean Delvare 
318f24d548bSGuenter Roeck 	/*
319f24d548bSGuenter Roeck 	 * No vendor ID, now checking if registers 0x10,0x11 (non-existent)
320f24d548bSGuenter Roeck 	 * showing the same as register 0x00
321f24d548bSGuenter Roeck 	 */
3228d5d45fbSJean Delvare 	temp = i2c_smbus_read_byte_data(new_client, 0x00);
3238d5d45fbSJean Delvare 
3248d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
3258d5d45fbSJean Delvare 	      (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
32671163c7cSJean Delvare 		return -ENODEV;
32771163c7cSJean Delvare 
32871163c7cSJean Delvare 	/* Get VRM */
32971163c7cSJean Delvare 	temp = vid_which_vrm();
33071163c7cSJean Delvare 
33171163c7cSJean Delvare 	if ((temp != 90) && (temp != 91)) {
33271163c7cSJean Delvare 		dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
33371163c7cSJean Delvare 				temp / 10, temp % 10);
33471163c7cSJean Delvare 		return -ENODEV;
33571163c7cSJean Delvare 	}
33671163c7cSJean Delvare 
33771163c7cSJean Delvare 	strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
33871163c7cSJean Delvare 
33971163c7cSJean Delvare 	return 0;
34071163c7cSJean Delvare }
34171163c7cSJean Delvare 
34271163c7cSJean Delvare static int atxp1_probe(struct i2c_client *new_client,
34371163c7cSJean Delvare 		       const struct i2c_device_id *id)
34471163c7cSJean Delvare {
34571163c7cSJean Delvare 	struct atxp1_data *data;
34671163c7cSJean Delvare 	int err;
34771163c7cSJean Delvare 
348*d466a353SGuenter Roeck 	data = devm_kzalloc(&new_client->dev, sizeof(struct atxp1_data),
349*d466a353SGuenter Roeck 			    GFP_KERNEL);
350*d466a353SGuenter Roeck 	if (!data)
351*d466a353SGuenter Roeck 		return -ENOMEM;
3528d5d45fbSJean Delvare 
3538d5d45fbSJean Delvare 	/* Get VRM */
354303760b4SJean Delvare 	data->vrm = vid_which_vrm();
3558d5d45fbSJean Delvare 
35671163c7cSJean Delvare 	i2c_set_clientdata(new_client, data);
3578d5d45fbSJean Delvare 	data->valid = 0;
3588d5d45fbSJean Delvare 
3599a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
3608d5d45fbSJean Delvare 
361a5ebe668SJean Delvare 	/* Register sysfs hooks */
362f24d548bSGuenter Roeck 	err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group);
363f24d548bSGuenter Roeck 	if (err)
364*d466a353SGuenter Roeck 		return err;
365a5ebe668SJean Delvare 
3661beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&new_client->dev);
3671beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
3681beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
369a5ebe668SJean Delvare 		goto exit_remove_files;
370943b0830SMark M. Hoffman 	}
371943b0830SMark M. Hoffman 
3728d5d45fbSJean Delvare 	dev_info(&new_client->dev, "Using VRM: %d.%d\n",
3738d5d45fbSJean Delvare 			 data->vrm / 10, data->vrm % 10);
3748d5d45fbSJean Delvare 
3758d5d45fbSJean Delvare 	return 0;
3768d5d45fbSJean Delvare 
377a5ebe668SJean Delvare exit_remove_files:
378a5ebe668SJean Delvare 	sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
3798d5d45fbSJean Delvare 	return err;
3808d5d45fbSJean Delvare };
3818d5d45fbSJean Delvare 
38271163c7cSJean Delvare static int atxp1_remove(struct i2c_client *client)
3838d5d45fbSJean Delvare {
384943b0830SMark M. Hoffman 	struct atxp1_data *data = i2c_get_clientdata(client);
3858d5d45fbSJean Delvare 
3861beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
387a5ebe668SJean Delvare 	sysfs_remove_group(&client->dev.kobj, &atxp1_group);
388943b0830SMark M. Hoffman 
38971163c7cSJean Delvare 	return 0;
3908d5d45fbSJean Delvare };
3918d5d45fbSJean Delvare 
392f0967eeaSAxel Lin module_i2c_driver(atxp1_driver);
393