xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision 1beeffe43311f64df8dd0ab08ff6b1858c58363f)
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");
348d5d45fbSJean Delvare MODULE_VERSION("0.6.2");
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 
458d5d45fbSJean Delvare static unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
468d5d45fbSJean Delvare 
47f4b50261SJean Delvare I2C_CLIENT_INSMOD_1(atxp1);
488d5d45fbSJean Delvare 
498d5d45fbSJean Delvare static int atxp1_attach_adapter(struct i2c_adapter * adapter);
508d5d45fbSJean Delvare static int atxp1_detach_client(struct i2c_client * client);
518d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev);
528d5d45fbSJean Delvare static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind);
538d5d45fbSJean Delvare 
548d5d45fbSJean Delvare static struct i2c_driver atxp1_driver = {
55cdaf7934SLaurent Riffard 	.driver = {
568d5d45fbSJean Delvare 		.name	= "atxp1",
57cdaf7934SLaurent Riffard 	},
588d5d45fbSJean Delvare 	.attach_adapter = atxp1_attach_adapter,
598d5d45fbSJean Delvare 	.detach_client	= atxp1_detach_client,
608d5d45fbSJean Delvare };
618d5d45fbSJean Delvare 
628d5d45fbSJean Delvare struct atxp1_data {
638d5d45fbSJean Delvare 	struct i2c_client client;
64*1beeffe4STony Jones 	struct device *hwmon_dev;
659a61bf63SIngo Molnar 	struct mutex update_lock;
668d5d45fbSJean Delvare 	unsigned long last_updated;
678d5d45fbSJean Delvare 	u8 valid;
688d5d45fbSJean Delvare 	struct {
698d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
708d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
718d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
728d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
738d5d45fbSJean Delvare 	} reg;
748d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
758d5d45fbSJean Delvare };
768d5d45fbSJean Delvare 
778d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev)
788d5d45fbSJean Delvare {
798d5d45fbSJean Delvare 	struct i2c_client *client;
808d5d45fbSJean Delvare 	struct atxp1_data *data;
818d5d45fbSJean Delvare 
828d5d45fbSJean Delvare 	client = to_i2c_client(dev);
838d5d45fbSJean Delvare 	data = i2c_get_clientdata(client);
848d5d45fbSJean Delvare 
859a61bf63SIngo Molnar 	mutex_lock(&data->update_lock);
868d5d45fbSJean Delvare 
870cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
888d5d45fbSJean Delvare 
898d5d45fbSJean Delvare 		/* Update local register data */
908d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
918d5d45fbSJean Delvare 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
928d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
938d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
948d5d45fbSJean Delvare 
958d5d45fbSJean Delvare 		data->valid = 1;
968d5d45fbSJean Delvare 	}
978d5d45fbSJean Delvare 
989a61bf63SIngo Molnar 	mutex_unlock(&data->update_lock);
998d5d45fbSJean Delvare 
1008d5d45fbSJean Delvare 	return(data);
1018d5d45fbSJean Delvare }
1028d5d45fbSJean Delvare 
1038d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
1048d5d45fbSJean Delvare static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
1058d5d45fbSJean Delvare {
1068d5d45fbSJean Delvare 	int size;
1078d5d45fbSJean Delvare 	struct atxp1_data *data;
1088d5d45fbSJean Delvare 
1098d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1108d5d45fbSJean Delvare 
1118d5d45fbSJean Delvare 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
1128d5d45fbSJean Delvare 
1138d5d45fbSJean Delvare 	return size;
1148d5d45fbSJean Delvare }
1158d5d45fbSJean Delvare 
1168d5d45fbSJean Delvare static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1178d5d45fbSJean Delvare {
1188d5d45fbSJean Delvare 	struct atxp1_data *data;
1198d5d45fbSJean Delvare 	struct i2c_client *client;
120c41bdb52SAlexey Dobriyan 	int vid, cvid;
1218d5d45fbSJean Delvare 	unsigned int vcore;
1228d5d45fbSJean Delvare 
1238d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1248d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1258d5d45fbSJean Delvare 
1268d5d45fbSJean Delvare 	vcore = simple_strtoul(buf, NULL, 10);
1278d5d45fbSJean Delvare 	vcore /= 25;
1288d5d45fbSJean Delvare 	vcore *= 25;
1298d5d45fbSJean Delvare 
1308d5d45fbSJean Delvare 	/* Calculate VID */
1318d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1328d5d45fbSJean Delvare 
1338d5d45fbSJean Delvare 	if (vid < 0) {
1348d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
1358d5d45fbSJean Delvare 		return -1;
1368d5d45fbSJean Delvare 	}
1378d5d45fbSJean Delvare 
1388d5d45fbSJean Delvare 	/* If output enabled, use control register value. Otherwise original CPU VID */
1398d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1408d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1418d5d45fbSJean Delvare 	else
1428d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1438d5d45fbSJean Delvare 
1448d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1458d5d45fbSJean Delvare 	if (vid == cvid)
1468d5d45fbSJean Delvare 		return count;
1478d5d45fbSJean Delvare 
1488d5d45fbSJean Delvare 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid);
1498d5d45fbSJean Delvare 
1508d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1518d5d45fbSJean Delvare 	if (cvid > vid) {
1528d5d45fbSJean Delvare 		for (; cvid >= vid; cvid--) {
1538d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1548d5d45fbSJean Delvare 		}
1558d5d45fbSJean Delvare 	}
1568d5d45fbSJean Delvare 	else {
1578d5d45fbSJean Delvare 		for (; cvid <= vid; cvid++) {
1588d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1598d5d45fbSJean Delvare 		}
1608d5d45fbSJean Delvare 	}
1618d5d45fbSJean Delvare 
1628d5d45fbSJean Delvare 	data->valid = 0;
1638d5d45fbSJean Delvare 
1648d5d45fbSJean Delvare 	return count;
1658d5d45fbSJean Delvare }
1668d5d45fbSJean Delvare 
1678d5d45fbSJean Delvare /* CPU core reference voltage
1688d5d45fbSJean Delvare     unit: millivolt
1698d5d45fbSJean Delvare */
1708d5d45fbSJean Delvare static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
1718d5d45fbSJean Delvare 
1728d5d45fbSJean Delvare /* sys file functions for GPIO1 */
1738d5d45fbSJean Delvare static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
1748d5d45fbSJean Delvare {
1758d5d45fbSJean Delvare 	int size;
1768d5d45fbSJean Delvare 	struct atxp1_data *data;
1778d5d45fbSJean Delvare 
1788d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1798d5d45fbSJean Delvare 
1808d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1818d5d45fbSJean Delvare 
1828d5d45fbSJean Delvare 	return size;
1838d5d45fbSJean Delvare }
1848d5d45fbSJean Delvare 
1858d5d45fbSJean Delvare static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
1868d5d45fbSJean Delvare {
1878d5d45fbSJean Delvare 	struct atxp1_data *data;
1888d5d45fbSJean Delvare 	struct i2c_client *client;
1898d5d45fbSJean Delvare 	unsigned int value;
1908d5d45fbSJean Delvare 
1918d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1928d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1938d5d45fbSJean Delvare 
1948d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16);
1958d5d45fbSJean Delvare 
1968d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
1978d5d45fbSJean Delvare 
1988d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
1998d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2008d5d45fbSJean Delvare 
2018d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
2028d5d45fbSJean Delvare 
2038d5d45fbSJean Delvare 		data->valid = 0;
2048d5d45fbSJean Delvare 	}
2058d5d45fbSJean Delvare 
2068d5d45fbSJean Delvare 	return count;
2078d5d45fbSJean Delvare }
2088d5d45fbSJean Delvare 
2098d5d45fbSJean Delvare /* GPIO1 data register
2108d5d45fbSJean Delvare     unit: Four bit as hex (e.g. 0x0f)
2118d5d45fbSJean Delvare */
2128d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2138d5d45fbSJean Delvare 
2148d5d45fbSJean Delvare /* sys file functions for GPIO2 */
2158d5d45fbSJean Delvare static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
2168d5d45fbSJean Delvare {
2178d5d45fbSJean Delvare 	int size;
2188d5d45fbSJean Delvare 	struct atxp1_data *data;
2198d5d45fbSJean Delvare 
2208d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2218d5d45fbSJean Delvare 
2228d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2238d5d45fbSJean Delvare 
2248d5d45fbSJean Delvare 	return size;
2258d5d45fbSJean Delvare }
2268d5d45fbSJean Delvare 
2278d5d45fbSJean Delvare static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
2288d5d45fbSJean Delvare {
2298d5d45fbSJean Delvare 	struct atxp1_data *data;
2308d5d45fbSJean Delvare 	struct i2c_client *client;
2318d5d45fbSJean Delvare 	unsigned int value;
2328d5d45fbSJean Delvare 
2338d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2348d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2358d5d45fbSJean Delvare 
2368d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16) & 0xff;
2378d5d45fbSJean Delvare 
2388d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
2398d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2408d5d45fbSJean Delvare 
2418d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2428d5d45fbSJean Delvare 
2438d5d45fbSJean Delvare 		data->valid = 0;
2448d5d45fbSJean Delvare 	}
2458d5d45fbSJean Delvare 
2468d5d45fbSJean Delvare 	return count;
2478d5d45fbSJean Delvare }
2488d5d45fbSJean Delvare 
2498d5d45fbSJean Delvare /* GPIO2 data register
2508d5d45fbSJean Delvare     unit: Eight bit as hex (e.g. 0xff)
2518d5d45fbSJean Delvare */
2528d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2538d5d45fbSJean Delvare 
254a5ebe668SJean Delvare static struct attribute *atxp1_attributes[] = {
255a5ebe668SJean Delvare 	&dev_attr_gpio1.attr,
256a5ebe668SJean Delvare 	&dev_attr_gpio2.attr,
257a5ebe668SJean Delvare 	&dev_attr_cpu0_vid.attr,
258a5ebe668SJean Delvare 	NULL
259a5ebe668SJean Delvare };
260a5ebe668SJean Delvare 
261a5ebe668SJean Delvare static const struct attribute_group atxp1_group = {
262a5ebe668SJean Delvare 	.attrs = atxp1_attributes,
263a5ebe668SJean Delvare };
264a5ebe668SJean Delvare 
2658d5d45fbSJean Delvare 
2668d5d45fbSJean Delvare static int atxp1_attach_adapter(struct i2c_adapter *adapter)
2678d5d45fbSJean Delvare {
268ddec748fSJean Delvare 	if (!(adapter->class & I2C_CLASS_HWMON))
269ddec748fSJean Delvare 		return 0;
2702ed2dc3cSJean Delvare 	return i2c_probe(adapter, &addr_data, &atxp1_detect);
2718d5d45fbSJean Delvare };
2728d5d45fbSJean Delvare 
2738d5d45fbSJean Delvare static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
2748d5d45fbSJean Delvare {
2758d5d45fbSJean Delvare 	struct i2c_client * new_client;
2768d5d45fbSJean Delvare 	struct atxp1_data * data;
2778d5d45fbSJean Delvare 	int err = 0;
2788d5d45fbSJean Delvare 	u8 temp;
2798d5d45fbSJean Delvare 
2808d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
2818d5d45fbSJean Delvare 		goto exit;
2828d5d45fbSJean Delvare 
283ba9c2e8dSDeepak Saxena 	if (!(data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL))) {
2848d5d45fbSJean Delvare 		err = -ENOMEM;
2858d5d45fbSJean Delvare 		goto exit;
2868d5d45fbSJean Delvare 	}
2878d5d45fbSJean Delvare 
2888d5d45fbSJean Delvare 	new_client = &data->client;
2898d5d45fbSJean Delvare 	i2c_set_clientdata(new_client, data);
2908d5d45fbSJean Delvare 
2918d5d45fbSJean Delvare 	new_client->addr = address;
2928d5d45fbSJean Delvare 	new_client->adapter = adapter;
2938d5d45fbSJean Delvare 	new_client->driver = &atxp1_driver;
2948d5d45fbSJean Delvare 	new_client->flags = 0;
2958d5d45fbSJean Delvare 
2968d5d45fbSJean Delvare 	/* Detect ATXP1, checking if vendor ID registers are all zero */
2978d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
2988d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
2998d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
3008d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) {
3018d5d45fbSJean Delvare 
3028d5d45fbSJean Delvare 		/* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
3038d5d45fbSJean Delvare 		 * showing the same as register 0x00 */
3048d5d45fbSJean Delvare 		temp = i2c_smbus_read_byte_data(new_client, 0x00);
3058d5d45fbSJean Delvare 
3068d5d45fbSJean Delvare 		if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
3078d5d45fbSJean Delvare 			 (i2c_smbus_read_byte_data(new_client, 0x11) == temp) ))
3088d5d45fbSJean Delvare 			goto exit_free;
3098d5d45fbSJean Delvare 	}
3108d5d45fbSJean Delvare 
3118d5d45fbSJean Delvare 	/* Get VRM */
312303760b4SJean Delvare 	data->vrm = vid_which_vrm();
3138d5d45fbSJean Delvare 
3148d5d45fbSJean Delvare 	if ((data->vrm != 90) && (data->vrm != 91)) {
3158d5d45fbSJean Delvare 		dev_err(&new_client->dev, "Not supporting VRM %d.%d\n",
3168d5d45fbSJean Delvare 				data->vrm / 10, data->vrm % 10);
3178d5d45fbSJean Delvare 		goto exit_free;
3188d5d45fbSJean Delvare 	}
3198d5d45fbSJean Delvare 
3208d5d45fbSJean Delvare 	strncpy(new_client->name, "atxp1", I2C_NAME_SIZE);
3218d5d45fbSJean Delvare 
3228d5d45fbSJean Delvare 	data->valid = 0;
3238d5d45fbSJean Delvare 
3249a61bf63SIngo Molnar 	mutex_init(&data->update_lock);
3258d5d45fbSJean Delvare 
3268d5d45fbSJean Delvare 	err = i2c_attach_client(new_client);
3278d5d45fbSJean Delvare 
3288d5d45fbSJean Delvare 	if (err)
3298d5d45fbSJean Delvare 	{
3308d5d45fbSJean Delvare 		dev_err(&new_client->dev, "Attach client error.\n");
3318d5d45fbSJean Delvare 		goto exit_free;
3328d5d45fbSJean Delvare 	}
3338d5d45fbSJean Delvare 
334a5ebe668SJean Delvare 	/* Register sysfs hooks */
335a5ebe668SJean Delvare 	if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
336a5ebe668SJean Delvare 		goto exit_detach;
337a5ebe668SJean Delvare 
338*1beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&new_client->dev);
339*1beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
340*1beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
341a5ebe668SJean Delvare 		goto exit_remove_files;
342943b0830SMark M. Hoffman 	}
343943b0830SMark M. Hoffman 
3448d5d45fbSJean Delvare 	dev_info(&new_client->dev, "Using VRM: %d.%d\n",
3458d5d45fbSJean Delvare 			 data->vrm / 10, data->vrm % 10);
3468d5d45fbSJean Delvare 
3478d5d45fbSJean Delvare 	return 0;
3488d5d45fbSJean Delvare 
349a5ebe668SJean Delvare exit_remove_files:
350a5ebe668SJean Delvare 	sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
351943b0830SMark M. Hoffman exit_detach:
352943b0830SMark M. Hoffman 	i2c_detach_client(new_client);
3538d5d45fbSJean Delvare exit_free:
3548d5d45fbSJean Delvare 	kfree(data);
3558d5d45fbSJean Delvare exit:
3568d5d45fbSJean Delvare 	return err;
3578d5d45fbSJean Delvare };
3588d5d45fbSJean Delvare 
3598d5d45fbSJean Delvare static int atxp1_detach_client(struct i2c_client * client)
3608d5d45fbSJean Delvare {
361943b0830SMark M. Hoffman 	struct atxp1_data * data = i2c_get_clientdata(client);
3628d5d45fbSJean Delvare 	int err;
3638d5d45fbSJean Delvare 
364*1beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
365a5ebe668SJean Delvare 	sysfs_remove_group(&client->dev.kobj, &atxp1_group);
366943b0830SMark M. Hoffman 
3678d5d45fbSJean Delvare 	err = i2c_detach_client(client);
3688d5d45fbSJean Delvare 
3698d5d45fbSJean Delvare 	if (err)
3708d5d45fbSJean Delvare 		dev_err(&client->dev, "Failed to detach client.\n");
3718d5d45fbSJean Delvare 	else
372943b0830SMark M. Hoffman 		kfree(data);
3738d5d45fbSJean Delvare 
3748d5d45fbSJean Delvare 	return err;
3758d5d45fbSJean Delvare };
3768d5d45fbSJean Delvare 
3778d5d45fbSJean Delvare static int __init atxp1_init(void)
3788d5d45fbSJean Delvare {
3798d5d45fbSJean Delvare 	return i2c_add_driver(&atxp1_driver);
3808d5d45fbSJean Delvare };
3818d5d45fbSJean Delvare 
3828d5d45fbSJean Delvare static void __exit atxp1_exit(void)
3838d5d45fbSJean Delvare {
3848d5d45fbSJean Delvare 	i2c_del_driver(&atxp1_driver);
3858d5d45fbSJean Delvare };
3868d5d45fbSJean Delvare 
3878d5d45fbSJean Delvare module_init(atxp1_init);
3888d5d45fbSJean Delvare module_exit(atxp1_exit);
389