xref: /openbmc/linux/drivers/input/mouse/psmouse-smbus.c (revision 727fb83765049981e342db4c5a8b51aca72201d8)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28eb92e5cSBenjamin Tissoires /*
38eb92e5cSBenjamin Tissoires  * Copyright (c) 2017 Red Hat, Inc
48eb92e5cSBenjamin Tissoires  */
58eb92e5cSBenjamin Tissoires 
68eb92e5cSBenjamin Tissoires #define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
78eb92e5cSBenjamin Tissoires 
88eb92e5cSBenjamin Tissoires #include <linux/kernel.h>
98eb92e5cSBenjamin Tissoires #include <linux/module.h>
108eb92e5cSBenjamin Tissoires #include <linux/libps2.h>
118eb92e5cSBenjamin Tissoires #include <linux/i2c.h>
128eb92e5cSBenjamin Tissoires #include <linux/serio.h>
138eb92e5cSBenjamin Tissoires #include <linux/slab.h>
148eb92e5cSBenjamin Tissoires #include <linux/workqueue.h>
158eb92e5cSBenjamin Tissoires #include "psmouse.h"
168eb92e5cSBenjamin Tissoires 
178eb92e5cSBenjamin Tissoires struct psmouse_smbus_dev {
188eb92e5cSBenjamin Tissoires 	struct i2c_board_info board;
198eb92e5cSBenjamin Tissoires 	struct psmouse *psmouse;
208eb92e5cSBenjamin Tissoires 	struct i2c_client *client;
218eb92e5cSBenjamin Tissoires 	struct list_head node;
228eb92e5cSBenjamin Tissoires 	bool dead;
23bf232e46SBenjamin Tissoires 	bool need_deactivate;
248eb92e5cSBenjamin Tissoires };
258eb92e5cSBenjamin Tissoires 
268eb92e5cSBenjamin Tissoires static LIST_HEAD(psmouse_smbus_list);
278eb92e5cSBenjamin Tissoires static DEFINE_MUTEX(psmouse_smbus_mutex);
288eb92e5cSBenjamin Tissoires 
291b3ce51dSTetsuo Handa static struct workqueue_struct *psmouse_smbus_wq;
301b3ce51dSTetsuo Handa 
psmouse_smbus_check_adapter(struct i2c_adapter * adapter)318eb92e5cSBenjamin Tissoires static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
328eb92e5cSBenjamin Tissoires {
338eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_dev *smbdev;
348eb92e5cSBenjamin Tissoires 
358eb92e5cSBenjamin Tissoires 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
368eb92e5cSBenjamin Tissoires 		return;
378eb92e5cSBenjamin Tissoires 
388eb92e5cSBenjamin Tissoires 	mutex_lock(&psmouse_smbus_mutex);
398eb92e5cSBenjamin Tissoires 
408eb92e5cSBenjamin Tissoires 	list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
418eb92e5cSBenjamin Tissoires 		if (smbdev->dead)
428eb92e5cSBenjamin Tissoires 			continue;
438eb92e5cSBenjamin Tissoires 
448eb92e5cSBenjamin Tissoires 		if (smbdev->client)
458eb92e5cSBenjamin Tissoires 			continue;
468eb92e5cSBenjamin Tissoires 
478eb92e5cSBenjamin Tissoires 		/*
488eb92e5cSBenjamin Tissoires 		 * Here would be a good place to check if device is actually
498eb92e5cSBenjamin Tissoires 		 * present, but it seems that SMBus will not respond unless we
508eb92e5cSBenjamin Tissoires 		 * fully reset PS/2 connection.  So cross our fingers, and try
518eb92e5cSBenjamin Tissoires 		 * to switch over, hopefully our system will not have too many
528eb92e5cSBenjamin Tissoires 		 * "host notify" I2C adapters.
538eb92e5cSBenjamin Tissoires 		 */
548eb92e5cSBenjamin Tissoires 		psmouse_dbg(smbdev->psmouse,
558eb92e5cSBenjamin Tissoires 			    "SMBus candidate adapter appeared, triggering rescan\n");
568eb92e5cSBenjamin Tissoires 		serio_rescan(smbdev->psmouse->ps2dev.serio);
578eb92e5cSBenjamin Tissoires 	}
588eb92e5cSBenjamin Tissoires 
598eb92e5cSBenjamin Tissoires 	mutex_unlock(&psmouse_smbus_mutex);
608eb92e5cSBenjamin Tissoires }
618eb92e5cSBenjamin Tissoires 
psmouse_smbus_detach_i2c_client(struct i2c_client * client)628eb92e5cSBenjamin Tissoires static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
638eb92e5cSBenjamin Tissoires {
64f6f08c55SDmitry Torokhov 	struct psmouse_smbus_dev *smbdev, *tmp;
658eb92e5cSBenjamin Tissoires 
668eb92e5cSBenjamin Tissoires 	mutex_lock(&psmouse_smbus_mutex);
678eb92e5cSBenjamin Tissoires 
68f6f08c55SDmitry Torokhov 	list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
69f6f08c55SDmitry Torokhov 		if (smbdev->client != client)
70f6f08c55SDmitry Torokhov 			continue;
718eb92e5cSBenjamin Tissoires 
728eb92e5cSBenjamin Tissoires 		kfree(client->dev.platform_data);
738eb92e5cSBenjamin Tissoires 		client->dev.platform_data = NULL;
748eb92e5cSBenjamin Tissoires 
75f6f08c55SDmitry Torokhov 		if (!smbdev->dead) {
76f6f08c55SDmitry Torokhov 			psmouse_dbg(smbdev->psmouse,
77f6f08c55SDmitry Torokhov 				    "Marking SMBus companion %s as gone\n",
78f6f08c55SDmitry Torokhov 				    dev_name(&smbdev->client->dev));
79f6f08c55SDmitry Torokhov 			smbdev->dead = true;
807b1f781fSDmitry Torokhov 			device_link_remove(&smbdev->client->dev,
817b1f781fSDmitry Torokhov 					   &smbdev->psmouse->ps2dev.serio->dev);
82f6f08c55SDmitry Torokhov 			serio_rescan(smbdev->psmouse->ps2dev.serio);
83f6f08c55SDmitry Torokhov 		} else {
84f6f08c55SDmitry Torokhov 			list_del(&smbdev->node);
85f6f08c55SDmitry Torokhov 			kfree(smbdev);
86f6f08c55SDmitry Torokhov 		}
87f6f08c55SDmitry Torokhov 	}
88f6f08c55SDmitry Torokhov 
898eb92e5cSBenjamin Tissoires 	mutex_unlock(&psmouse_smbus_mutex);
908eb92e5cSBenjamin Tissoires }
918eb92e5cSBenjamin Tissoires 
psmouse_smbus_notifier_call(struct notifier_block * nb,unsigned long action,void * data)928eb92e5cSBenjamin Tissoires static int psmouse_smbus_notifier_call(struct notifier_block *nb,
938eb92e5cSBenjamin Tissoires 				       unsigned long action, void *data)
948eb92e5cSBenjamin Tissoires {
958eb92e5cSBenjamin Tissoires 	struct device *dev = data;
968eb92e5cSBenjamin Tissoires 
978eb92e5cSBenjamin Tissoires 	switch (action) {
988eb92e5cSBenjamin Tissoires 	case BUS_NOTIFY_ADD_DEVICE:
998eb92e5cSBenjamin Tissoires 		if (dev->type == &i2c_adapter_type)
1008eb92e5cSBenjamin Tissoires 			psmouse_smbus_check_adapter(to_i2c_adapter(dev));
1018eb92e5cSBenjamin Tissoires 		break;
1028eb92e5cSBenjamin Tissoires 
1038eb92e5cSBenjamin Tissoires 	case BUS_NOTIFY_REMOVED_DEVICE:
104a716a026SDmitry Torokhov 		if (dev->type == &i2c_client_type)
1058eb92e5cSBenjamin Tissoires 			psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
1068eb92e5cSBenjamin Tissoires 		break;
1078eb92e5cSBenjamin Tissoires 	}
1088eb92e5cSBenjamin Tissoires 
1098eb92e5cSBenjamin Tissoires 	return 0;
1108eb92e5cSBenjamin Tissoires }
1118eb92e5cSBenjamin Tissoires 
1128eb92e5cSBenjamin Tissoires static struct notifier_block psmouse_smbus_notifier = {
1138eb92e5cSBenjamin Tissoires 	.notifier_call = psmouse_smbus_notifier_call,
1148eb92e5cSBenjamin Tissoires };
1158eb92e5cSBenjamin Tissoires 
psmouse_smbus_process_byte(struct psmouse * psmouse)1168eb92e5cSBenjamin Tissoires static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
1178eb92e5cSBenjamin Tissoires {
1188eb92e5cSBenjamin Tissoires 	return PSMOUSE_FULL_PACKET;
1198eb92e5cSBenjamin Tissoires }
1208eb92e5cSBenjamin Tissoires 
psmouse_smbus_reconnect(struct psmouse * psmouse)1218eb92e5cSBenjamin Tissoires static int psmouse_smbus_reconnect(struct psmouse *psmouse)
1228eb92e5cSBenjamin Tissoires {
123*b3572639SDmitry Torokhov 	struct psmouse_smbus_dev *smbdev = psmouse->private;
124*b3572639SDmitry Torokhov 
125*b3572639SDmitry Torokhov 	if (smbdev->need_deactivate)
126*b3572639SDmitry Torokhov 		psmouse_deactivate(psmouse);
127*b3572639SDmitry Torokhov 
1288eb92e5cSBenjamin Tissoires 	return 0;
1298eb92e5cSBenjamin Tissoires }
1308eb92e5cSBenjamin Tissoires 
1318eb92e5cSBenjamin Tissoires struct psmouse_smbus_removal_work {
1328eb92e5cSBenjamin Tissoires 	struct work_struct work;
1338eb92e5cSBenjamin Tissoires 	struct i2c_client *client;
1348eb92e5cSBenjamin Tissoires };
1358eb92e5cSBenjamin Tissoires 
psmouse_smbus_remove_i2c_device(struct work_struct * work)1368eb92e5cSBenjamin Tissoires static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
1378eb92e5cSBenjamin Tissoires {
1388eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_removal_work *rwork =
1398eb92e5cSBenjamin Tissoires 		container_of(work, struct psmouse_smbus_removal_work, work);
1408eb92e5cSBenjamin Tissoires 
1418eb92e5cSBenjamin Tissoires 	dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
1428eb92e5cSBenjamin Tissoires 	i2c_unregister_device(rwork->client);
1438eb92e5cSBenjamin Tissoires 
1448eb92e5cSBenjamin Tissoires 	kfree(rwork);
1458eb92e5cSBenjamin Tissoires }
1468eb92e5cSBenjamin Tissoires 
1478eb92e5cSBenjamin Tissoires /*
1488eb92e5cSBenjamin Tissoires  * This schedules removal of SMBus companion device. We have to do
1498eb92e5cSBenjamin Tissoires  * it in a separate tread to avoid deadlocking on psmouse_mutex in
1508eb92e5cSBenjamin Tissoires  * case the device has a trackstick (which is also driven by psmouse).
1518eb92e5cSBenjamin Tissoires  *
1528eb92e5cSBenjamin Tissoires  * Note that this may be racing with i2c adapter removal, but we
1538eb92e5cSBenjamin Tissoires  * can't do anything about that: i2c automatically destroys clients
1548eb92e5cSBenjamin Tissoires  * attached to an adapter that is being removed. This has to be
1558eb92e5cSBenjamin Tissoires  * fixed in i2c core.
1568eb92e5cSBenjamin Tissoires  */
psmouse_smbus_schedule_remove(struct i2c_client * client)1578eb92e5cSBenjamin Tissoires static void psmouse_smbus_schedule_remove(struct i2c_client *client)
1588eb92e5cSBenjamin Tissoires {
1598eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_removal_work *rwork;
1608eb92e5cSBenjamin Tissoires 
1618eb92e5cSBenjamin Tissoires 	rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
1628eb92e5cSBenjamin Tissoires 	if (rwork) {
1638eb92e5cSBenjamin Tissoires 		INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
1648eb92e5cSBenjamin Tissoires 		rwork->client = client;
1658eb92e5cSBenjamin Tissoires 
1661b3ce51dSTetsuo Handa 		queue_work(psmouse_smbus_wq, &rwork->work);
1678eb92e5cSBenjamin Tissoires 	}
1688eb92e5cSBenjamin Tissoires }
1698eb92e5cSBenjamin Tissoires 
psmouse_smbus_disconnect(struct psmouse * psmouse)1708eb92e5cSBenjamin Tissoires static void psmouse_smbus_disconnect(struct psmouse *psmouse)
1718eb92e5cSBenjamin Tissoires {
1728eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_dev *smbdev = psmouse->private;
1738eb92e5cSBenjamin Tissoires 
1748eb92e5cSBenjamin Tissoires 	mutex_lock(&psmouse_smbus_mutex);
1758eb92e5cSBenjamin Tissoires 
176f6f08c55SDmitry Torokhov 	if (smbdev->dead) {
177f6f08c55SDmitry Torokhov 		list_del(&smbdev->node);
178f6f08c55SDmitry Torokhov 		kfree(smbdev);
179f6f08c55SDmitry Torokhov 	} else {
180f6f08c55SDmitry Torokhov 		smbdev->dead = true;
1817b1f781fSDmitry Torokhov 		device_link_remove(&smbdev->client->dev,
1827b1f781fSDmitry Torokhov 				   &psmouse->ps2dev.serio->dev);
1838eb92e5cSBenjamin Tissoires 		psmouse_dbg(smbdev->psmouse,
1848eb92e5cSBenjamin Tissoires 			    "posting removal request for SMBus companion %s\n",
1858eb92e5cSBenjamin Tissoires 			    dev_name(&smbdev->client->dev));
1868eb92e5cSBenjamin Tissoires 		psmouse_smbus_schedule_remove(smbdev->client);
1878eb92e5cSBenjamin Tissoires 	}
1888eb92e5cSBenjamin Tissoires 
189f6f08c55SDmitry Torokhov 	mutex_unlock(&psmouse_smbus_mutex);
190f6f08c55SDmitry Torokhov 
1918eb92e5cSBenjamin Tissoires 	psmouse->private = NULL;
1928eb92e5cSBenjamin Tissoires }
1938eb92e5cSBenjamin Tissoires 
psmouse_smbus_create_companion(struct device * dev,void * data)1948eb92e5cSBenjamin Tissoires static int psmouse_smbus_create_companion(struct device *dev, void *data)
1958eb92e5cSBenjamin Tissoires {
1968eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_dev *smbdev = data;
1978eb92e5cSBenjamin Tissoires 	unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
1988eb92e5cSBenjamin Tissoires 	struct i2c_adapter *adapter;
199557d0841SWolfram Sang 	struct i2c_client *client;
2008eb92e5cSBenjamin Tissoires 
2018eb92e5cSBenjamin Tissoires 	adapter = i2c_verify_adapter(dev);
2028eb92e5cSBenjamin Tissoires 	if (!adapter)
2038eb92e5cSBenjamin Tissoires 		return 0;
2048eb92e5cSBenjamin Tissoires 
2058eb92e5cSBenjamin Tissoires 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
2068eb92e5cSBenjamin Tissoires 		return 0;
2078eb92e5cSBenjamin Tissoires 
208557d0841SWolfram Sang 	client = i2c_new_scanned_device(adapter, &smbdev->board,
2098eb92e5cSBenjamin Tissoires 					addr_list, NULL);
210557d0841SWolfram Sang 	if (IS_ERR(client))
2118eb92e5cSBenjamin Tissoires 		return 0;
2128eb92e5cSBenjamin Tissoires 
2138eb92e5cSBenjamin Tissoires 	/* We have our(?) device, stop iterating i2c bus. */
214557d0841SWolfram Sang 	smbdev->client = client;
2158eb92e5cSBenjamin Tissoires 	return 1;
2168eb92e5cSBenjamin Tissoires }
2178eb92e5cSBenjamin Tissoires 
psmouse_smbus_cleanup(struct psmouse * psmouse)2188eb92e5cSBenjamin Tissoires void psmouse_smbus_cleanup(struct psmouse *psmouse)
2198eb92e5cSBenjamin Tissoires {
2208eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_dev *smbdev, *tmp;
2218eb92e5cSBenjamin Tissoires 
2228eb92e5cSBenjamin Tissoires 	mutex_lock(&psmouse_smbus_mutex);
2238eb92e5cSBenjamin Tissoires 
2248eb92e5cSBenjamin Tissoires 	list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
2258eb92e5cSBenjamin Tissoires 		if (psmouse == smbdev->psmouse) {
2268eb92e5cSBenjamin Tissoires 			list_del(&smbdev->node);
2278eb92e5cSBenjamin Tissoires 			kfree(smbdev);
2288eb92e5cSBenjamin Tissoires 		}
2298eb92e5cSBenjamin Tissoires 	}
2308eb92e5cSBenjamin Tissoires 
2318eb92e5cSBenjamin Tissoires 	mutex_unlock(&psmouse_smbus_mutex);
2328eb92e5cSBenjamin Tissoires }
2338eb92e5cSBenjamin Tissoires 
psmouse_smbus_init(struct psmouse * psmouse,const struct i2c_board_info * board,const void * pdata,size_t pdata_size,bool need_deactivate,bool leave_breadcrumbs)2348eb92e5cSBenjamin Tissoires int psmouse_smbus_init(struct psmouse *psmouse,
2358eb92e5cSBenjamin Tissoires 		       const struct i2c_board_info *board,
2368eb92e5cSBenjamin Tissoires 		       const void *pdata, size_t pdata_size,
237bf232e46SBenjamin Tissoires 		       bool need_deactivate,
2388eb92e5cSBenjamin Tissoires 		       bool leave_breadcrumbs)
2398eb92e5cSBenjamin Tissoires {
2408eb92e5cSBenjamin Tissoires 	struct psmouse_smbus_dev *smbdev;
2418eb92e5cSBenjamin Tissoires 	int error;
2428eb92e5cSBenjamin Tissoires 
2438eb92e5cSBenjamin Tissoires 	smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
2448eb92e5cSBenjamin Tissoires 	if (!smbdev)
2458eb92e5cSBenjamin Tissoires 		return -ENOMEM;
2468eb92e5cSBenjamin Tissoires 
2478eb92e5cSBenjamin Tissoires 	smbdev->psmouse = psmouse;
2488eb92e5cSBenjamin Tissoires 	smbdev->board = *board;
249bf232e46SBenjamin Tissoires 	smbdev->need_deactivate = need_deactivate;
2508eb92e5cSBenjamin Tissoires 
25121c48dbdSBenjamin Tissoires 	if (pdata) {
25221c48dbdSBenjamin Tissoires 		smbdev->board.platform_data = kmemdup(pdata, pdata_size,
25321c48dbdSBenjamin Tissoires 						      GFP_KERNEL);
2548eb92e5cSBenjamin Tissoires 		if (!smbdev->board.platform_data) {
2558eb92e5cSBenjamin Tissoires 			kfree(smbdev);
2568eb92e5cSBenjamin Tissoires 			return -ENOMEM;
2578eb92e5cSBenjamin Tissoires 		}
25821c48dbdSBenjamin Tissoires 	}
2598eb92e5cSBenjamin Tissoires 
260*b3572639SDmitry Torokhov 	if (need_deactivate)
261*b3572639SDmitry Torokhov 		psmouse_deactivate(psmouse);
262bf232e46SBenjamin Tissoires 
2638eb92e5cSBenjamin Tissoires 	psmouse->private = smbdev;
2648eb92e5cSBenjamin Tissoires 	psmouse->protocol_handler = psmouse_smbus_process_byte;
2658eb92e5cSBenjamin Tissoires 	psmouse->reconnect = psmouse_smbus_reconnect;
2668eb92e5cSBenjamin Tissoires 	psmouse->fast_reconnect = psmouse_smbus_reconnect;
2678eb92e5cSBenjamin Tissoires 	psmouse->disconnect = psmouse_smbus_disconnect;
2688eb92e5cSBenjamin Tissoires 	psmouse->resync_time = 0;
2698eb92e5cSBenjamin Tissoires 
2708eb92e5cSBenjamin Tissoires 	mutex_lock(&psmouse_smbus_mutex);
2718eb92e5cSBenjamin Tissoires 	list_add_tail(&smbdev->node, &psmouse_smbus_list);
2728eb92e5cSBenjamin Tissoires 	mutex_unlock(&psmouse_smbus_mutex);
2738eb92e5cSBenjamin Tissoires 
2748eb92e5cSBenjamin Tissoires 	/* Bind to already existing adapters right away */
2758eb92e5cSBenjamin Tissoires 	error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
2768eb92e5cSBenjamin Tissoires 
2778eb92e5cSBenjamin Tissoires 	if (smbdev->client) {
2788eb92e5cSBenjamin Tissoires 		/* We have our companion device */
2797b1f781fSDmitry Torokhov 		if (!device_link_add(&smbdev->client->dev,
2807b1f781fSDmitry Torokhov 				     &psmouse->ps2dev.serio->dev,
2817b1f781fSDmitry Torokhov 				     DL_FLAG_STATELESS))
2827b1f781fSDmitry Torokhov 			psmouse_warn(psmouse,
2837b1f781fSDmitry Torokhov 				     "failed to set up link with iSMBus companion %s\n",
2847b1f781fSDmitry Torokhov 				     dev_name(&smbdev->client->dev));
2858eb92e5cSBenjamin Tissoires 		return 0;
2868eb92e5cSBenjamin Tissoires 	}
2878eb92e5cSBenjamin Tissoires 
2888eb92e5cSBenjamin Tissoires 	/*
2898eb92e5cSBenjamin Tissoires 	 * If we did not create i2c device we will not need platform
2908eb92e5cSBenjamin Tissoires 	 * data even if we are leaving breadcrumbs.
2918eb92e5cSBenjamin Tissoires 	 */
2928eb92e5cSBenjamin Tissoires 	kfree(smbdev->board.platform_data);
2938eb92e5cSBenjamin Tissoires 	smbdev->board.platform_data = NULL;
2948eb92e5cSBenjamin Tissoires 
2958eb92e5cSBenjamin Tissoires 	if (error < 0 || !leave_breadcrumbs) {
2968eb92e5cSBenjamin Tissoires 		mutex_lock(&psmouse_smbus_mutex);
2978eb92e5cSBenjamin Tissoires 		list_del(&smbdev->node);
2988eb92e5cSBenjamin Tissoires 		mutex_unlock(&psmouse_smbus_mutex);
2998eb92e5cSBenjamin Tissoires 
3008eb92e5cSBenjamin Tissoires 		kfree(smbdev);
3018eb92e5cSBenjamin Tissoires 	}
3028eb92e5cSBenjamin Tissoires 
3038eb92e5cSBenjamin Tissoires 	return error < 0 ? error : -EAGAIN;
3048eb92e5cSBenjamin Tissoires }
3058eb92e5cSBenjamin Tissoires 
psmouse_smbus_module_init(void)3068eb92e5cSBenjamin Tissoires int __init psmouse_smbus_module_init(void)
3078eb92e5cSBenjamin Tissoires {
3088eb92e5cSBenjamin Tissoires 	int error;
3098eb92e5cSBenjamin Tissoires 
3101b3ce51dSTetsuo Handa 	psmouse_smbus_wq = alloc_workqueue("psmouse-smbus", 0, 0);
3111b3ce51dSTetsuo Handa 	if (!psmouse_smbus_wq)
3121b3ce51dSTetsuo Handa 		return -ENOMEM;
3131b3ce51dSTetsuo Handa 
3148eb92e5cSBenjamin Tissoires 	error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
3158eb92e5cSBenjamin Tissoires 	if (error) {
3168eb92e5cSBenjamin Tissoires 		pr_err("failed to register i2c bus notifier: %d\n", error);
3171b3ce51dSTetsuo Handa 		destroy_workqueue(psmouse_smbus_wq);
3188eb92e5cSBenjamin Tissoires 		return error;
3198eb92e5cSBenjamin Tissoires 	}
3208eb92e5cSBenjamin Tissoires 
3218eb92e5cSBenjamin Tissoires 	return 0;
3228eb92e5cSBenjamin Tissoires }
3238eb92e5cSBenjamin Tissoires 
psmouse_smbus_module_exit(void)3248eb92e5cSBenjamin Tissoires void psmouse_smbus_module_exit(void)
3258eb92e5cSBenjamin Tissoires {
3268eb92e5cSBenjamin Tissoires 	bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
3271b3ce51dSTetsuo Handa 	destroy_workqueue(psmouse_smbus_wq);
3288eb92e5cSBenjamin Tissoires }
329