xref: /linux/drivers/input/mouse/psmouse-smbus.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2017 Red Hat, Inc
4  */
5 
6 #define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
7 
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/libps2.h>
11 #include <linux/i2c.h>
12 #include <linux/serio.h>
13 #include <linux/slab.h>
14 #include <linux/workqueue.h>
15 #include "psmouse.h"
16 
17 struct psmouse_smbus_dev {
18 	struct i2c_board_info board;
19 	struct psmouse *psmouse;
20 	struct i2c_client *client;
21 	struct list_head node;
22 	bool dead;
23 	bool need_deactivate;
24 };
25 
26 static LIST_HEAD(psmouse_smbus_list);
27 static DEFINE_MUTEX(psmouse_smbus_mutex);
28 
29 static struct workqueue_struct *psmouse_smbus_wq;
30 
psmouse_smbus_check_adapter(struct i2c_adapter * adapter)31 static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
32 {
33 	struct psmouse_smbus_dev *smbdev;
34 
35 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
36 		return;
37 
38 	mutex_lock(&psmouse_smbus_mutex);
39 
40 	list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
41 		if (smbdev->dead)
42 			continue;
43 
44 		if (smbdev->client)
45 			continue;
46 
47 		/*
48 		 * Here would be a good place to check if device is actually
49 		 * present, but it seems that SMBus will not respond unless we
50 		 * fully reset PS/2 connection.  So cross our fingers, and try
51 		 * to switch over, hopefully our system will not have too many
52 		 * "host notify" I2C adapters.
53 		 */
54 		psmouse_dbg(smbdev->psmouse,
55 			    "SMBus candidate adapter appeared, triggering rescan\n");
56 		serio_rescan(smbdev->psmouse->ps2dev.serio);
57 	}
58 
59 	mutex_unlock(&psmouse_smbus_mutex);
60 }
61 
psmouse_smbus_detach_i2c_client(struct i2c_client * client)62 static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
63 {
64 	struct psmouse_smbus_dev *smbdev, *tmp;
65 
66 	mutex_lock(&psmouse_smbus_mutex);
67 
68 	list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
69 		if (smbdev->client != client)
70 			continue;
71 
72 		kfree(client->dev.platform_data);
73 		client->dev.platform_data = NULL;
74 
75 		if (!smbdev->dead) {
76 			psmouse_dbg(smbdev->psmouse,
77 				    "Marking SMBus companion %s as gone\n",
78 				    dev_name(&smbdev->client->dev));
79 			smbdev->dead = true;
80 			device_link_remove(&smbdev->client->dev,
81 					   &smbdev->psmouse->ps2dev.serio->dev);
82 			serio_rescan(smbdev->psmouse->ps2dev.serio);
83 		} else {
84 			list_del(&smbdev->node);
85 			kfree(smbdev);
86 		}
87 	}
88 
89 	mutex_unlock(&psmouse_smbus_mutex);
90 }
91 
psmouse_smbus_notifier_call(struct notifier_block * nb,unsigned long action,void * data)92 static int psmouse_smbus_notifier_call(struct notifier_block *nb,
93 				       unsigned long action, void *data)
94 {
95 	struct device *dev = data;
96 
97 	switch (action) {
98 	case BUS_NOTIFY_ADD_DEVICE:
99 		if (dev->type == &i2c_adapter_type)
100 			psmouse_smbus_check_adapter(to_i2c_adapter(dev));
101 		break;
102 
103 	case BUS_NOTIFY_REMOVED_DEVICE:
104 		if (dev->type == &i2c_client_type)
105 			psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
106 		break;
107 	}
108 
109 	return 0;
110 }
111 
112 static struct notifier_block psmouse_smbus_notifier = {
113 	.notifier_call = psmouse_smbus_notifier_call,
114 };
115 
psmouse_smbus_process_byte(struct psmouse * psmouse)116 static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
117 {
118 	return PSMOUSE_FULL_PACKET;
119 }
120 
psmouse_smbus_reconnect(struct psmouse * psmouse)121 static int psmouse_smbus_reconnect(struct psmouse *psmouse)
122 {
123 	struct psmouse_smbus_dev *smbdev = psmouse->private;
124 
125 	if (smbdev->need_deactivate)
126 		psmouse_deactivate(psmouse);
127 
128 	return 0;
129 }
130 
131 struct psmouse_smbus_removal_work {
132 	struct work_struct work;
133 	struct i2c_client *client;
134 };
135 
psmouse_smbus_remove_i2c_device(struct work_struct * work)136 static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
137 {
138 	struct psmouse_smbus_removal_work *rwork =
139 		container_of(work, struct psmouse_smbus_removal_work, work);
140 
141 	dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
142 	i2c_unregister_device(rwork->client);
143 
144 	kfree(rwork);
145 }
146 
147 /*
148  * This schedules removal of SMBus companion device. We have to do
149  * it in a separate tread to avoid deadlocking on psmouse_mutex in
150  * case the device has a trackstick (which is also driven by psmouse).
151  *
152  * Note that this may be racing with i2c adapter removal, but we
153  * can't do anything about that: i2c automatically destroys clients
154  * attached to an adapter that is being removed. This has to be
155  * fixed in i2c core.
156  */
psmouse_smbus_schedule_remove(struct i2c_client * client)157 static void psmouse_smbus_schedule_remove(struct i2c_client *client)
158 {
159 	struct psmouse_smbus_removal_work *rwork;
160 
161 	rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
162 	if (rwork) {
163 		INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
164 		rwork->client = client;
165 
166 		queue_work(psmouse_smbus_wq, &rwork->work);
167 	}
168 }
169 
psmouse_smbus_disconnect(struct psmouse * psmouse)170 static void psmouse_smbus_disconnect(struct psmouse *psmouse)
171 {
172 	struct psmouse_smbus_dev *smbdev = psmouse->private;
173 
174 	mutex_lock(&psmouse_smbus_mutex);
175 
176 	if (smbdev->dead) {
177 		list_del(&smbdev->node);
178 		kfree(smbdev);
179 	} else {
180 		smbdev->dead = true;
181 		device_link_remove(&smbdev->client->dev,
182 				   &psmouse->ps2dev.serio->dev);
183 		psmouse_dbg(smbdev->psmouse,
184 			    "posting removal request for SMBus companion %s\n",
185 			    dev_name(&smbdev->client->dev));
186 		psmouse_smbus_schedule_remove(smbdev->client);
187 	}
188 
189 	mutex_unlock(&psmouse_smbus_mutex);
190 
191 	psmouse->private = NULL;
192 }
193 
psmouse_smbus_create_companion(struct device * dev,void * data)194 static int psmouse_smbus_create_companion(struct device *dev, void *data)
195 {
196 	struct psmouse_smbus_dev *smbdev = data;
197 	unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
198 	struct i2c_adapter *adapter;
199 	struct i2c_client *client;
200 
201 	adapter = i2c_verify_adapter(dev);
202 	if (!adapter)
203 		return 0;
204 
205 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
206 		return 0;
207 
208 	client = i2c_new_scanned_device(adapter, &smbdev->board,
209 					addr_list, NULL);
210 	if (IS_ERR(client))
211 		return 0;
212 
213 	/* We have our(?) device, stop iterating i2c bus. */
214 	smbdev->client = client;
215 	return 1;
216 }
217 
psmouse_smbus_cleanup(struct psmouse * psmouse)218 void psmouse_smbus_cleanup(struct psmouse *psmouse)
219 {
220 	struct psmouse_smbus_dev *smbdev, *tmp;
221 
222 	mutex_lock(&psmouse_smbus_mutex);
223 
224 	list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
225 		if (psmouse == smbdev->psmouse) {
226 			list_del(&smbdev->node);
227 			kfree(smbdev);
228 		}
229 	}
230 
231 	mutex_unlock(&psmouse_smbus_mutex);
232 }
233 
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)234 int psmouse_smbus_init(struct psmouse *psmouse,
235 		       const struct i2c_board_info *board,
236 		       const void *pdata, size_t pdata_size,
237 		       bool need_deactivate,
238 		       bool leave_breadcrumbs)
239 {
240 	struct psmouse_smbus_dev *smbdev;
241 	int error;
242 
243 	smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
244 	if (!smbdev)
245 		return -ENOMEM;
246 
247 	smbdev->psmouse = psmouse;
248 	smbdev->board = *board;
249 	smbdev->need_deactivate = need_deactivate;
250 
251 	if (pdata) {
252 		smbdev->board.platform_data = kmemdup(pdata, pdata_size,
253 						      GFP_KERNEL);
254 		if (!smbdev->board.platform_data) {
255 			kfree(smbdev);
256 			return -ENOMEM;
257 		}
258 	}
259 
260 	if (need_deactivate)
261 		psmouse_deactivate(psmouse);
262 
263 	psmouse->private = smbdev;
264 	psmouse->protocol_handler = psmouse_smbus_process_byte;
265 	psmouse->reconnect = psmouse_smbus_reconnect;
266 	psmouse->fast_reconnect = psmouse_smbus_reconnect;
267 	psmouse->disconnect = psmouse_smbus_disconnect;
268 	psmouse->resync_time = 0;
269 
270 	mutex_lock(&psmouse_smbus_mutex);
271 	list_add_tail(&smbdev->node, &psmouse_smbus_list);
272 	mutex_unlock(&psmouse_smbus_mutex);
273 
274 	/* Bind to already existing adapters right away */
275 	error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
276 
277 	if (smbdev->client) {
278 		/* We have our companion device */
279 		if (!device_link_add(&smbdev->client->dev,
280 				     &psmouse->ps2dev.serio->dev,
281 				     DL_FLAG_STATELESS))
282 			psmouse_warn(psmouse,
283 				     "failed to set up link with iSMBus companion %s\n",
284 				     dev_name(&smbdev->client->dev));
285 		return 0;
286 	}
287 
288 	/*
289 	 * If we did not create i2c device we will not need platform
290 	 * data even if we are leaving breadcrumbs.
291 	 */
292 	kfree(smbdev->board.platform_data);
293 	smbdev->board.platform_data = NULL;
294 
295 	if (error < 0 || !leave_breadcrumbs) {
296 		mutex_lock(&psmouse_smbus_mutex);
297 		list_del(&smbdev->node);
298 		mutex_unlock(&psmouse_smbus_mutex);
299 
300 		kfree(smbdev);
301 	}
302 
303 	return error < 0 ? error : -EAGAIN;
304 }
305 
psmouse_smbus_module_init(void)306 int __init psmouse_smbus_module_init(void)
307 {
308 	int error;
309 
310 	psmouse_smbus_wq = alloc_workqueue("psmouse-smbus", 0, 0);
311 	if (!psmouse_smbus_wq)
312 		return -ENOMEM;
313 
314 	error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
315 	if (error) {
316 		pr_err("failed to register i2c bus notifier: %d\n", error);
317 		destroy_workqueue(psmouse_smbus_wq);
318 		return error;
319 	}
320 
321 	return 0;
322 }
323 
psmouse_smbus_module_exit(void)324 void psmouse_smbus_module_exit(void)
325 {
326 	bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
327 	destroy_workqueue(psmouse_smbus_wq);
328 }
329