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