18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2017 Red Hat, Inc 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/libps2.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/serio.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 158c2ecf20Sopenharmony_ci#include "psmouse.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct psmouse_smbus_dev { 188c2ecf20Sopenharmony_ci struct i2c_board_info board; 198c2ecf20Sopenharmony_ci struct psmouse *psmouse; 208c2ecf20Sopenharmony_ci struct i2c_client *client; 218c2ecf20Sopenharmony_ci struct list_head node; 228c2ecf20Sopenharmony_ci bool dead; 238c2ecf20Sopenharmony_ci bool need_deactivate; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic LIST_HEAD(psmouse_smbus_list); 278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(psmouse_smbus_mutex); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void psmouse_smbus_check_adapter(struct i2c_adapter *adapter) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) 348c2ecf20Sopenharmony_ci return; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci mutex_lock(&psmouse_smbus_mutex); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci list_for_each_entry(smbdev, &psmouse_smbus_list, node) { 398c2ecf20Sopenharmony_ci if (smbdev->dead) 408c2ecf20Sopenharmony_ci continue; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (smbdev->client) 438c2ecf20Sopenharmony_ci continue; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * Here would be a good place to check if device is actually 478c2ecf20Sopenharmony_ci * present, but it seems that SMBus will not respond unless we 488c2ecf20Sopenharmony_ci * fully reset PS/2 connection. So cross our fingers, and try 498c2ecf20Sopenharmony_ci * to switch over, hopefully our system will not have too many 508c2ecf20Sopenharmony_ci * "host notify" I2C adapters. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci psmouse_dbg(smbdev->psmouse, 538c2ecf20Sopenharmony_ci "SMBus candidate adapter appeared, triggering rescan\n"); 548c2ecf20Sopenharmony_ci serio_rescan(smbdev->psmouse->ps2dev.serio); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci mutex_unlock(&psmouse_smbus_mutex); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void psmouse_smbus_detach_i2c_client(struct i2c_client *client) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev, *tmp; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci mutex_lock(&psmouse_smbus_mutex); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { 678c2ecf20Sopenharmony_ci if (smbdev->client != client) 688c2ecf20Sopenharmony_ci continue; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci kfree(client->dev.platform_data); 718c2ecf20Sopenharmony_ci client->dev.platform_data = NULL; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!smbdev->dead) { 748c2ecf20Sopenharmony_ci psmouse_dbg(smbdev->psmouse, 758c2ecf20Sopenharmony_ci "Marking SMBus companion %s as gone\n", 768c2ecf20Sopenharmony_ci dev_name(&smbdev->client->dev)); 778c2ecf20Sopenharmony_ci smbdev->dead = true; 788c2ecf20Sopenharmony_ci serio_rescan(smbdev->psmouse->ps2dev.serio); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci list_del(&smbdev->node); 818c2ecf20Sopenharmony_ci kfree(smbdev); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_unlock(&psmouse_smbus_mutex); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int psmouse_smbus_notifier_call(struct notifier_block *nb, 898c2ecf20Sopenharmony_ci unsigned long action, void *data) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct device *dev = data; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci switch (action) { 948c2ecf20Sopenharmony_ci case BUS_NOTIFY_ADD_DEVICE: 958c2ecf20Sopenharmony_ci if (dev->type == &i2c_adapter_type) 968c2ecf20Sopenharmony_ci psmouse_smbus_check_adapter(to_i2c_adapter(dev)); 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci case BUS_NOTIFY_REMOVED_DEVICE: 1008c2ecf20Sopenharmony_ci if (dev->type == &i2c_client_type) 1018c2ecf20Sopenharmony_ci psmouse_smbus_detach_i2c_client(to_i2c_client(dev)); 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic struct notifier_block psmouse_smbus_notifier = { 1098c2ecf20Sopenharmony_ci .notifier_call = psmouse_smbus_notifier_call, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return PSMOUSE_FULL_PACKET; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int psmouse_smbus_reconnect(struct psmouse *psmouse) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev = psmouse->private; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (smbdev->need_deactivate) 1228c2ecf20Sopenharmony_ci psmouse_deactivate(psmouse); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistruct psmouse_smbus_removal_work { 1288c2ecf20Sopenharmony_ci struct work_struct work; 1298c2ecf20Sopenharmony_ci struct i2c_client *client; 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void psmouse_smbus_remove_i2c_device(struct work_struct *work) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct psmouse_smbus_removal_work *rwork = 1358c2ecf20Sopenharmony_ci container_of(work, struct psmouse_smbus_removal_work, work); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n"); 1388c2ecf20Sopenharmony_ci i2c_unregister_device(rwork->client); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci kfree(rwork); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * This schedules removal of SMBus companion device. We have to do 1458c2ecf20Sopenharmony_ci * it in a separate tread to avoid deadlocking on psmouse_mutex in 1468c2ecf20Sopenharmony_ci * case the device has a trackstick (which is also driven by psmouse). 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * Note that this may be racing with i2c adapter removal, but we 1498c2ecf20Sopenharmony_ci * can't do anything about that: i2c automatically destroys clients 1508c2ecf20Sopenharmony_ci * attached to an adapter that is being removed. This has to be 1518c2ecf20Sopenharmony_ci * fixed in i2c core. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic void psmouse_smbus_schedule_remove(struct i2c_client *client) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct psmouse_smbus_removal_work *rwork; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci rwork = kzalloc(sizeof(*rwork), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (rwork) { 1598c2ecf20Sopenharmony_ci INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device); 1608c2ecf20Sopenharmony_ci rwork->client = client; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci schedule_work(&rwork->work); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void psmouse_smbus_disconnect(struct psmouse *psmouse) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev = psmouse->private; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci mutex_lock(&psmouse_smbus_mutex); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (smbdev->dead) { 1738c2ecf20Sopenharmony_ci list_del(&smbdev->node); 1748c2ecf20Sopenharmony_ci kfree(smbdev); 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci smbdev->dead = true; 1778c2ecf20Sopenharmony_ci psmouse_dbg(smbdev->psmouse, 1788c2ecf20Sopenharmony_ci "posting removal request for SMBus companion %s\n", 1798c2ecf20Sopenharmony_ci dev_name(&smbdev->client->dev)); 1808c2ecf20Sopenharmony_ci psmouse_smbus_schedule_remove(smbdev->client); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci mutex_unlock(&psmouse_smbus_mutex); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci psmouse->private = NULL; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int psmouse_smbus_create_companion(struct device *dev, void *data) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev = data; 1918c2ecf20Sopenharmony_ci unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END }; 1928c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 1938c2ecf20Sopenharmony_ci struct i2c_client *client; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci adapter = i2c_verify_adapter(dev); 1968c2ecf20Sopenharmony_ci if (!adapter) 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci client = i2c_new_scanned_device(adapter, &smbdev->board, 2038c2ecf20Sopenharmony_ci addr_list, NULL); 2048c2ecf20Sopenharmony_ci if (IS_ERR(client)) 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* We have our(?) device, stop iterating i2c bus. */ 2088c2ecf20Sopenharmony_ci smbdev->client = client; 2098c2ecf20Sopenharmony_ci return 1; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid psmouse_smbus_cleanup(struct psmouse *psmouse) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev, *tmp; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci mutex_lock(&psmouse_smbus_mutex); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { 2198c2ecf20Sopenharmony_ci if (psmouse == smbdev->psmouse) { 2208c2ecf20Sopenharmony_ci list_del(&smbdev->node); 2218c2ecf20Sopenharmony_ci kfree(smbdev); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci mutex_unlock(&psmouse_smbus_mutex); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciint psmouse_smbus_init(struct psmouse *psmouse, 2298c2ecf20Sopenharmony_ci const struct i2c_board_info *board, 2308c2ecf20Sopenharmony_ci const void *pdata, size_t pdata_size, 2318c2ecf20Sopenharmony_ci bool need_deactivate, 2328c2ecf20Sopenharmony_ci bool leave_breadcrumbs) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct psmouse_smbus_dev *smbdev; 2358c2ecf20Sopenharmony_ci int error; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!smbdev) 2398c2ecf20Sopenharmony_ci return -ENOMEM; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci smbdev->psmouse = psmouse; 2428c2ecf20Sopenharmony_ci smbdev->board = *board; 2438c2ecf20Sopenharmony_ci smbdev->need_deactivate = need_deactivate; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (pdata) { 2468c2ecf20Sopenharmony_ci smbdev->board.platform_data = kmemdup(pdata, pdata_size, 2478c2ecf20Sopenharmony_ci GFP_KERNEL); 2488c2ecf20Sopenharmony_ci if (!smbdev->board.platform_data) { 2498c2ecf20Sopenharmony_ci kfree(smbdev); 2508c2ecf20Sopenharmony_ci return -ENOMEM; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (need_deactivate) 2558c2ecf20Sopenharmony_ci psmouse_deactivate(psmouse); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci psmouse->private = smbdev; 2588c2ecf20Sopenharmony_ci psmouse->protocol_handler = psmouse_smbus_process_byte; 2598c2ecf20Sopenharmony_ci psmouse->reconnect = psmouse_smbus_reconnect; 2608c2ecf20Sopenharmony_ci psmouse->fast_reconnect = psmouse_smbus_reconnect; 2618c2ecf20Sopenharmony_ci psmouse->disconnect = psmouse_smbus_disconnect; 2628c2ecf20Sopenharmony_ci psmouse->resync_time = 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_lock(&psmouse_smbus_mutex); 2658c2ecf20Sopenharmony_ci list_add_tail(&smbdev->node, &psmouse_smbus_list); 2668c2ecf20Sopenharmony_ci mutex_unlock(&psmouse_smbus_mutex); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Bind to already existing adapters right away */ 2698c2ecf20Sopenharmony_ci error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (smbdev->client) { 2728c2ecf20Sopenharmony_ci /* We have our companion device */ 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * If we did not create i2c device we will not need platform 2788c2ecf20Sopenharmony_ci * data even if we are leaving breadcrumbs. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci kfree(smbdev->board.platform_data); 2818c2ecf20Sopenharmony_ci smbdev->board.platform_data = NULL; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (error < 0 || !leave_breadcrumbs) { 2848c2ecf20Sopenharmony_ci mutex_lock(&psmouse_smbus_mutex); 2858c2ecf20Sopenharmony_ci list_del(&smbdev->node); 2868c2ecf20Sopenharmony_ci mutex_unlock(&psmouse_smbus_mutex); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci kfree(smbdev); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return error < 0 ? error : -EAGAIN; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciint __init psmouse_smbus_module_init(void) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int error; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier); 2998c2ecf20Sopenharmony_ci if (error) { 3008c2ecf20Sopenharmony_ci pr_err("failed to register i2c bus notifier: %d\n", error); 3018c2ecf20Sopenharmony_ci return error; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_civoid psmouse_smbus_module_exit(void) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier); 3108c2ecf20Sopenharmony_ci flush_scheduled_work(); 3118c2ecf20Sopenharmony_ci} 312