xref: /openbmc/linux/drivers/hwmon/atxp1.c (revision 303760b44a7a142cb9f4c9df4609fb63bbda98db)
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>
27*303760b4SJean Delvare #include <linux/hwmon-vid.h>
28943b0830SMark M. Hoffman #include <linux/err.h>
298d5d45fbSJean Delvare 
308d5d45fbSJean Delvare MODULE_LICENSE("GPL");
318d5d45fbSJean Delvare MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
328d5d45fbSJean Delvare MODULE_VERSION("0.6.2");
338d5d45fbSJean Delvare MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
348d5d45fbSJean Delvare 
358d5d45fbSJean Delvare #define ATXP1_VID	0x00
368d5d45fbSJean Delvare #define ATXP1_CVID	0x01
378d5d45fbSJean Delvare #define ATXP1_GPIO1	0x06
388d5d45fbSJean Delvare #define ATXP1_GPIO2	0x0a
398d5d45fbSJean Delvare #define ATXP1_VIDENA	0x20
408d5d45fbSJean Delvare #define ATXP1_VIDMASK	0x1f
418d5d45fbSJean Delvare #define ATXP1_GPIO1MASK	0x0f
428d5d45fbSJean Delvare 
438d5d45fbSJean Delvare static unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
448d5d45fbSJean Delvare 
45f4b50261SJean Delvare I2C_CLIENT_INSMOD_1(atxp1);
468d5d45fbSJean Delvare 
478d5d45fbSJean Delvare static int atxp1_attach_adapter(struct i2c_adapter * adapter);
488d5d45fbSJean Delvare static int atxp1_detach_client(struct i2c_client * client);
498d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev);
508d5d45fbSJean Delvare static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind);
518d5d45fbSJean Delvare 
528d5d45fbSJean Delvare static struct i2c_driver atxp1_driver = {
538d5d45fbSJean Delvare 	.owner		= THIS_MODULE,
548d5d45fbSJean Delvare 	.name		= "atxp1",
558d5d45fbSJean Delvare 	.flags		= I2C_DF_NOTIFY,
568d5d45fbSJean Delvare 	.attach_adapter = atxp1_attach_adapter,
578d5d45fbSJean Delvare 	.detach_client	= atxp1_detach_client,
588d5d45fbSJean Delvare };
598d5d45fbSJean Delvare 
608d5d45fbSJean Delvare struct atxp1_data {
618d5d45fbSJean Delvare 	struct i2c_client client;
62943b0830SMark M. Hoffman 	struct class_device *class_dev;
638d5d45fbSJean Delvare 	struct semaphore update_lock;
648d5d45fbSJean Delvare 	unsigned long last_updated;
658d5d45fbSJean Delvare 	u8 valid;
668d5d45fbSJean Delvare 	struct {
678d5d45fbSJean Delvare 		u8 vid;		/* VID output register */
688d5d45fbSJean Delvare 		u8 cpu_vid; /* VID input from CPU */
698d5d45fbSJean Delvare 		u8 gpio1;   /* General purpose I/O register 1 */
708d5d45fbSJean Delvare 		u8 gpio2;   /* General purpose I/O register 2 */
718d5d45fbSJean Delvare 	} reg;
728d5d45fbSJean Delvare 	u8 vrm;			/* Detected CPU VRM */
738d5d45fbSJean Delvare };
748d5d45fbSJean Delvare 
758d5d45fbSJean Delvare static struct atxp1_data * atxp1_update_device(struct device *dev)
768d5d45fbSJean Delvare {
778d5d45fbSJean Delvare 	struct i2c_client *client;
788d5d45fbSJean Delvare 	struct atxp1_data *data;
798d5d45fbSJean Delvare 
808d5d45fbSJean Delvare 	client = to_i2c_client(dev);
818d5d45fbSJean Delvare 	data = i2c_get_clientdata(client);
828d5d45fbSJean Delvare 
838d5d45fbSJean Delvare 	down(&data->update_lock);
848d5d45fbSJean Delvare 
850cacdf29SJean Delvare 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
868d5d45fbSJean Delvare 
878d5d45fbSJean Delvare 		/* Update local register data */
888d5d45fbSJean Delvare 		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
898d5d45fbSJean Delvare 		data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
908d5d45fbSJean Delvare 		data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
918d5d45fbSJean Delvare 		data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
928d5d45fbSJean Delvare 
938d5d45fbSJean Delvare 		data->valid = 1;
948d5d45fbSJean Delvare 	}
958d5d45fbSJean Delvare 
968d5d45fbSJean Delvare 	up(&data->update_lock);
978d5d45fbSJean Delvare 
988d5d45fbSJean Delvare 	return(data);
998d5d45fbSJean Delvare }
1008d5d45fbSJean Delvare 
1018d5d45fbSJean Delvare /* sys file functions for cpu0_vid */
1028d5d45fbSJean Delvare static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
1038d5d45fbSJean Delvare {
1048d5d45fbSJean Delvare 	int size;
1058d5d45fbSJean Delvare 	struct atxp1_data *data;
1068d5d45fbSJean Delvare 
1078d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1088d5d45fbSJean Delvare 
1098d5d45fbSJean Delvare 	size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
1108d5d45fbSJean Delvare 
1118d5d45fbSJean Delvare 	return size;
1128d5d45fbSJean Delvare }
1138d5d45fbSJean Delvare 
1148d5d45fbSJean Delvare static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1158d5d45fbSJean Delvare {
1168d5d45fbSJean Delvare 	struct atxp1_data *data;
1178d5d45fbSJean Delvare 	struct i2c_client *client;
1188d5d45fbSJean Delvare 	char vid;
1198d5d45fbSJean Delvare 	char cvid;
1208d5d45fbSJean Delvare 	unsigned int vcore;
1218d5d45fbSJean Delvare 
1228d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1238d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1248d5d45fbSJean Delvare 
1258d5d45fbSJean Delvare 	vcore = simple_strtoul(buf, NULL, 10);
1268d5d45fbSJean Delvare 	vcore /= 25;
1278d5d45fbSJean Delvare 	vcore *= 25;
1288d5d45fbSJean Delvare 
1298d5d45fbSJean Delvare 	/* Calculate VID */
1308d5d45fbSJean Delvare 	vid = vid_to_reg(vcore, data->vrm);
1318d5d45fbSJean Delvare 
1328d5d45fbSJean Delvare 	if (vid < 0) {
1338d5d45fbSJean Delvare 		dev_err(dev, "VID calculation failed.\n");
1348d5d45fbSJean Delvare 		return -1;
1358d5d45fbSJean Delvare 	}
1368d5d45fbSJean Delvare 
1378d5d45fbSJean Delvare 	/* If output enabled, use control register value. Otherwise original CPU VID */
1388d5d45fbSJean Delvare 	if (data->reg.vid & ATXP1_VIDENA)
1398d5d45fbSJean Delvare 		cvid = data->reg.vid & ATXP1_VIDMASK;
1408d5d45fbSJean Delvare 	else
1418d5d45fbSJean Delvare 		cvid = data->reg.cpu_vid;
1428d5d45fbSJean Delvare 
1438d5d45fbSJean Delvare 	/* Nothing changed, aborting */
1448d5d45fbSJean Delvare 	if (vid == cvid)
1458d5d45fbSJean Delvare 		return count;
1468d5d45fbSJean Delvare 
1478d5d45fbSJean Delvare 	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid);
1488d5d45fbSJean Delvare 
1498d5d45fbSJean Delvare 	/* Write every 25 mV step to increase stability */
1508d5d45fbSJean Delvare 	if (cvid > vid) {
1518d5d45fbSJean Delvare 		for (; cvid >= vid; cvid--) {
1528d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1538d5d45fbSJean Delvare 		}
1548d5d45fbSJean Delvare 	}
1558d5d45fbSJean Delvare 	else {
1568d5d45fbSJean Delvare 		for (; cvid <= vid; cvid++) {
1578d5d45fbSJean Delvare         		i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
1588d5d45fbSJean Delvare 		}
1598d5d45fbSJean Delvare 	}
1608d5d45fbSJean Delvare 
1618d5d45fbSJean Delvare 	data->valid = 0;
1628d5d45fbSJean Delvare 
1638d5d45fbSJean Delvare 	return count;
1648d5d45fbSJean Delvare }
1658d5d45fbSJean Delvare 
1668d5d45fbSJean Delvare /* CPU core reference voltage
1678d5d45fbSJean Delvare     unit: millivolt
1688d5d45fbSJean Delvare */
1698d5d45fbSJean Delvare static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
1708d5d45fbSJean Delvare 
1718d5d45fbSJean Delvare /* sys file functions for GPIO1 */
1728d5d45fbSJean Delvare static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
1738d5d45fbSJean Delvare {
1748d5d45fbSJean Delvare 	int size;
1758d5d45fbSJean Delvare 	struct atxp1_data *data;
1768d5d45fbSJean Delvare 
1778d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1788d5d45fbSJean Delvare 
1798d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
1808d5d45fbSJean Delvare 
1818d5d45fbSJean Delvare 	return size;
1828d5d45fbSJean Delvare }
1838d5d45fbSJean Delvare 
1848d5d45fbSJean Delvare static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
1858d5d45fbSJean Delvare {
1868d5d45fbSJean Delvare 	struct atxp1_data *data;
1878d5d45fbSJean Delvare 	struct i2c_client *client;
1888d5d45fbSJean Delvare 	unsigned int value;
1898d5d45fbSJean Delvare 
1908d5d45fbSJean Delvare 	client = to_i2c_client(dev);
1918d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
1928d5d45fbSJean Delvare 
1938d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16);
1948d5d45fbSJean Delvare 
1958d5d45fbSJean Delvare 	value &= ATXP1_GPIO1MASK;
1968d5d45fbSJean Delvare 
1978d5d45fbSJean Delvare 	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
1988d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
1998d5d45fbSJean Delvare 
2008d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
2018d5d45fbSJean Delvare 
2028d5d45fbSJean Delvare 		data->valid = 0;
2038d5d45fbSJean Delvare 	}
2048d5d45fbSJean Delvare 
2058d5d45fbSJean Delvare 	return count;
2068d5d45fbSJean Delvare }
2078d5d45fbSJean Delvare 
2088d5d45fbSJean Delvare /* GPIO1 data register
2098d5d45fbSJean Delvare     unit: Four bit as hex (e.g. 0x0f)
2108d5d45fbSJean Delvare */
2118d5d45fbSJean Delvare static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
2128d5d45fbSJean Delvare 
2138d5d45fbSJean Delvare /* sys file functions for GPIO2 */
2148d5d45fbSJean Delvare static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
2158d5d45fbSJean Delvare {
2168d5d45fbSJean Delvare 	int size;
2178d5d45fbSJean Delvare 	struct atxp1_data *data;
2188d5d45fbSJean Delvare 
2198d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2208d5d45fbSJean Delvare 
2218d5d45fbSJean Delvare 	size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
2228d5d45fbSJean Delvare 
2238d5d45fbSJean Delvare 	return size;
2248d5d45fbSJean Delvare }
2258d5d45fbSJean Delvare 
2268d5d45fbSJean Delvare static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
2278d5d45fbSJean Delvare {
2288d5d45fbSJean Delvare 	struct atxp1_data *data;
2298d5d45fbSJean Delvare 	struct i2c_client *client;
2308d5d45fbSJean Delvare 	unsigned int value;
2318d5d45fbSJean Delvare 
2328d5d45fbSJean Delvare 	client = to_i2c_client(dev);
2338d5d45fbSJean Delvare 	data = atxp1_update_device(dev);
2348d5d45fbSJean Delvare 
2358d5d45fbSJean Delvare 	value = simple_strtoul(buf, NULL, 16) & 0xff;
2368d5d45fbSJean Delvare 
2378d5d45fbSJean Delvare 	if (value != data->reg.gpio2) {
2388d5d45fbSJean Delvare 		dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
2398d5d45fbSJean Delvare 
2408d5d45fbSJean Delvare 		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
2418d5d45fbSJean Delvare 
2428d5d45fbSJean Delvare 		data->valid = 0;
2438d5d45fbSJean Delvare 	}
2448d5d45fbSJean Delvare 
2458d5d45fbSJean Delvare 	return count;
2468d5d45fbSJean Delvare }
2478d5d45fbSJean Delvare 
2488d5d45fbSJean Delvare /* GPIO2 data register
2498d5d45fbSJean Delvare     unit: Eight bit as hex (e.g. 0xff)
2508d5d45fbSJean Delvare */
2518d5d45fbSJean Delvare static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
2528d5d45fbSJean Delvare 
2538d5d45fbSJean Delvare 
2548d5d45fbSJean Delvare static int atxp1_attach_adapter(struct i2c_adapter *adapter)
2558d5d45fbSJean Delvare {
2562ed2dc3cSJean Delvare 	return i2c_probe(adapter, &addr_data, &atxp1_detect);
2578d5d45fbSJean Delvare };
2588d5d45fbSJean Delvare 
2598d5d45fbSJean Delvare static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
2608d5d45fbSJean Delvare {
2618d5d45fbSJean Delvare 	struct i2c_client * new_client;
2628d5d45fbSJean Delvare 	struct atxp1_data * data;
2638d5d45fbSJean Delvare 	int err = 0;
2648d5d45fbSJean Delvare 	u8 temp;
2658d5d45fbSJean Delvare 
2668d5d45fbSJean Delvare 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
2678d5d45fbSJean Delvare 		goto exit;
2688d5d45fbSJean Delvare 
2698d5d45fbSJean Delvare 	if (!(data = kmalloc(sizeof(struct atxp1_data), GFP_KERNEL))) {
2708d5d45fbSJean Delvare 		err = -ENOMEM;
2718d5d45fbSJean Delvare 		goto exit;
2728d5d45fbSJean Delvare 	}
2738d5d45fbSJean Delvare 
2748d5d45fbSJean Delvare 	memset(data, 0, sizeof(struct atxp1_data));
2758d5d45fbSJean Delvare 	new_client = &data->client;
2768d5d45fbSJean Delvare 	i2c_set_clientdata(new_client, data);
2778d5d45fbSJean Delvare 
2788d5d45fbSJean Delvare 	new_client->addr = address;
2798d5d45fbSJean Delvare 	new_client->adapter = adapter;
2808d5d45fbSJean Delvare 	new_client->driver = &atxp1_driver;
2818d5d45fbSJean Delvare 	new_client->flags = 0;
2828d5d45fbSJean Delvare 
2838d5d45fbSJean Delvare 	/* Detect ATXP1, checking if vendor ID registers are all zero */
2848d5d45fbSJean Delvare 	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
2858d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
2868d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
2878d5d45fbSJean Delvare 	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) {
2888d5d45fbSJean Delvare 
2898d5d45fbSJean Delvare 		/* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
2908d5d45fbSJean Delvare 		 * showing the same as register 0x00 */
2918d5d45fbSJean Delvare 		temp = i2c_smbus_read_byte_data(new_client, 0x00);
2928d5d45fbSJean Delvare 
2938d5d45fbSJean Delvare 		if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
2948d5d45fbSJean Delvare 			 (i2c_smbus_read_byte_data(new_client, 0x11) == temp) ))
2958d5d45fbSJean Delvare 			goto exit_free;
2968d5d45fbSJean Delvare 	}
2978d5d45fbSJean Delvare 
2988d5d45fbSJean Delvare 	/* Get VRM */
299*303760b4SJean Delvare 	data->vrm = vid_which_vrm();
3008d5d45fbSJean Delvare 
3018d5d45fbSJean Delvare 	if ((data->vrm != 90) && (data->vrm != 91)) {
3028d5d45fbSJean Delvare 		dev_err(&new_client->dev, "Not supporting VRM %d.%d\n",
3038d5d45fbSJean Delvare 				data->vrm / 10, data->vrm % 10);
3048d5d45fbSJean Delvare 		goto exit_free;
3058d5d45fbSJean Delvare 	}
3068d5d45fbSJean Delvare 
3078d5d45fbSJean Delvare 	strncpy(new_client->name, "atxp1", I2C_NAME_SIZE);
3088d5d45fbSJean Delvare 
3098d5d45fbSJean Delvare 	data->valid = 0;
3108d5d45fbSJean Delvare 
3118d5d45fbSJean Delvare 	init_MUTEX(&data->update_lock);
3128d5d45fbSJean Delvare 
3138d5d45fbSJean Delvare 	err = i2c_attach_client(new_client);
3148d5d45fbSJean Delvare 
3158d5d45fbSJean Delvare 	if (err)
3168d5d45fbSJean Delvare 	{
3178d5d45fbSJean Delvare 		dev_err(&new_client->dev, "Attach client error.\n");
3188d5d45fbSJean Delvare 		goto exit_free;
3198d5d45fbSJean Delvare 	}
3208d5d45fbSJean Delvare 
321943b0830SMark M. Hoffman 	data->class_dev = hwmon_device_register(&new_client->dev);
322943b0830SMark M. Hoffman 	if (IS_ERR(data->class_dev)) {
323943b0830SMark M. Hoffman 		err = PTR_ERR(data->class_dev);
324943b0830SMark M. Hoffman 		goto exit_detach;
325943b0830SMark M. Hoffman 	}
326943b0830SMark M. Hoffman 
3278d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_gpio1);
3288d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_gpio2);
3298d5d45fbSJean Delvare 	device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
3308d5d45fbSJean Delvare 
3318d5d45fbSJean Delvare 	dev_info(&new_client->dev, "Using VRM: %d.%d\n",
3328d5d45fbSJean Delvare 			 data->vrm / 10, data->vrm % 10);
3338d5d45fbSJean Delvare 
3348d5d45fbSJean Delvare 	return 0;
3358d5d45fbSJean Delvare 
336943b0830SMark M. Hoffman exit_detach:
337943b0830SMark M. Hoffman 	i2c_detach_client(new_client);
3388d5d45fbSJean Delvare exit_free:
3398d5d45fbSJean Delvare 	kfree(data);
3408d5d45fbSJean Delvare exit:
3418d5d45fbSJean Delvare 	return err;
3428d5d45fbSJean Delvare };
3438d5d45fbSJean Delvare 
3448d5d45fbSJean Delvare static int atxp1_detach_client(struct i2c_client * client)
3458d5d45fbSJean Delvare {
346943b0830SMark M. Hoffman 	struct atxp1_data * data = i2c_get_clientdata(client);
3478d5d45fbSJean Delvare 	int err;
3488d5d45fbSJean Delvare 
349943b0830SMark M. Hoffman 	hwmon_device_unregister(data->class_dev);
350943b0830SMark M. Hoffman 
3518d5d45fbSJean Delvare 	err = i2c_detach_client(client);
3528d5d45fbSJean Delvare 
3538d5d45fbSJean Delvare 	if (err)
3548d5d45fbSJean Delvare 		dev_err(&client->dev, "Failed to detach client.\n");
3558d5d45fbSJean Delvare 	else
356943b0830SMark M. Hoffman 		kfree(data);
3578d5d45fbSJean Delvare 
3588d5d45fbSJean Delvare 	return err;
3598d5d45fbSJean Delvare };
3608d5d45fbSJean Delvare 
3618d5d45fbSJean Delvare static int __init atxp1_init(void)
3628d5d45fbSJean Delvare {
3638d5d45fbSJean Delvare 	return i2c_add_driver(&atxp1_driver);
3648d5d45fbSJean Delvare };
3658d5d45fbSJean Delvare 
3668d5d45fbSJean Delvare static void __exit atxp1_exit(void)
3678d5d45fbSJean Delvare {
3688d5d45fbSJean Delvare 	i2c_del_driver(&atxp1_driver);
3698d5d45fbSJean Delvare };
3708d5d45fbSJean Delvare 
3718d5d45fbSJean Delvare module_init(atxp1_init);
3728d5d45fbSJean Delvare module_exit(atxp1_exit);
373