Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags   |
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 Red Hat, Inc
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/libps2.h>
#include <linux/i2c.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "psmouse.h"
struct psmouse_smbus_dev {
<------>struct i2c_board_info board;
<------>struct psmouse *psmouse;
<------>struct i2c_client *client;
<------>struct list_head node;
<------>bool dead;
<------>bool need_deactivate;
};
static LIST_HEAD(psmouse_smbus_list);
static DEFINE_MUTEX(psmouse_smbus_mutex);
static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
{
<------>struct psmouse_smbus_dev *smbdev;
<------>if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
<------><------>return;
<------>mutex_lock(&psmouse_smbus_mutex);
<------>list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
<------><------>if (smbdev->dead)
<------><------><------>continue;
<------><------>if (smbdev->client)
<------><------><------>continue;
<------><------>/*
<------><------> * Here would be a good place to check if device is actually
<------><------> * present, but it seems that SMBus will not respond unless we
<------><------> * fully reset PS/2 connection. So cross our fingers, and try
<------><------> * to switch over, hopefully our system will not have too many
<------><------> * "host notify" I2C adapters.
<------><------> */
<------><------>psmouse_dbg(smbdev->psmouse,
<------><------><------> "SMBus candidate adapter appeared, triggering rescan\n");
<------><------>serio_rescan(smbdev->psmouse->ps2dev.serio);
<------>}
<------>mutex_unlock(&psmouse_smbus_mutex);
}
static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
{
<------>struct psmouse_smbus_dev *smbdev, *tmp;
<------>mutex_lock(&psmouse_smbus_mutex);
<------>list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
<------><------>if (smbdev->client != client)
<------><------><------>continue;
<------><------>kfree(client->dev.platform_data);
<------><------>client->dev.platform_data = NULL;
<------><------>if (!smbdev->dead) {
<------><------><------>psmouse_dbg(smbdev->psmouse,
<------><------><------><------> "Marking SMBus companion %s as gone\n",
<------><------><------><------> dev_name(&smbdev->client->dev));
<------><------><------>smbdev->dead = true;
<------><------><------>serio_rescan(smbdev->psmouse->ps2dev.serio);
<------><------>} else {
<------><------><------>list_del(&smbdev->node);
<------><------><------>kfree(smbdev);
<------><------>}
<------>}
<------>mutex_unlock(&psmouse_smbus_mutex);
}
static int psmouse_smbus_notifier_call(struct notifier_block *nb,
<------><------><------><------> unsigned long action, void *data)
{
<------>struct device *dev = data;
<------>switch (action) {
<------>case BUS_NOTIFY_ADD_DEVICE:
<------><------>if (dev->type == &i2c_adapter_type)
<------><------><------>psmouse_smbus_check_adapter(to_i2c_adapter(dev));
<------><------>break;
<------>case BUS_NOTIFY_REMOVED_DEVICE:
<------><------>if (dev->type == &i2c_client_type)
<------><------><------>psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
<------><------>break;
<------>}
<------>return 0;
}
static struct notifier_block psmouse_smbus_notifier = {
<------>.notifier_call = psmouse_smbus_notifier_call,
};
static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
{
<------>return PSMOUSE_FULL_PACKET;
}
static int psmouse_smbus_reconnect(struct psmouse *psmouse)
{
<------>struct psmouse_smbus_dev *smbdev = psmouse->private;
<------>if (smbdev->need_deactivate)
<------><------>psmouse_deactivate(psmouse);
<------>return 0;
}
struct psmouse_smbus_removal_work {
<------>struct work_struct work;
<------>struct i2c_client *client;
};
static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
{
<------>struct psmouse_smbus_removal_work *rwork =
<------><------>container_of(work, struct psmouse_smbus_removal_work, work);
<------>dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
<------>i2c_unregister_device(rwork->client);
<------>kfree(rwork);
}
/*
* This schedules removal of SMBus companion device. We have to do
* it in a separate tread to avoid deadlocking on psmouse_mutex in
* case the device has a trackstick (which is also driven by psmouse).
*
* Note that this may be racing with i2c adapter removal, but we
* can't do anything about that: i2c automatically destroys clients
* attached to an adapter that is being removed. This has to be
* fixed in i2c core.
*/
static void psmouse_smbus_schedule_remove(struct i2c_client *client)
{
<------>struct psmouse_smbus_removal_work *rwork;
<------>rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
<------>if (rwork) {
<------><------>INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
<------><------>rwork->client = client;
<------><------>schedule_work(&rwork->work);
<------>}
}
static void psmouse_smbus_disconnect(struct psmouse *psmouse)
{
<------>struct psmouse_smbus_dev *smbdev = psmouse->private;
<------>mutex_lock(&psmouse_smbus_mutex);
<------>if (smbdev->dead) {
<------><------>list_del(&smbdev->node);
<------><------>kfree(smbdev);
<------>} else {
<------><------>smbdev->dead = true;
<------><------>psmouse_dbg(smbdev->psmouse,
<------><------><------> "posting removal request for SMBus companion %s\n",
<------><------><------> dev_name(&smbdev->client->dev));
<------><------>psmouse_smbus_schedule_remove(smbdev->client);
<------>}
<------>mutex_unlock(&psmouse_smbus_mutex);
<------>psmouse->private = NULL;
}
static int psmouse_smbus_create_companion(struct device *dev, void *data)
{
<------>struct psmouse_smbus_dev *smbdev = data;
<------>unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
<------>struct i2c_adapter *adapter;
<------>struct i2c_client *client;
<------>adapter = i2c_verify_adapter(dev);
<------>if (!adapter)
<------><------>return 0;
<------>if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
<------><------>return 0;
<------>client = i2c_new_scanned_device(adapter, &smbdev->board,
<------><------><------><------><------>addr_list, NULL);
<------>if (IS_ERR(client))
<------><------>return 0;
<------>/* We have our(?) device, stop iterating i2c bus. */
<------>smbdev->client = client;
<------>return 1;
}
void psmouse_smbus_cleanup(struct psmouse *psmouse)
{
<------>struct psmouse_smbus_dev *smbdev, *tmp;
<------>mutex_lock(&psmouse_smbus_mutex);
<------>list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
<------><------>if (psmouse == smbdev->psmouse) {
<------><------><------>list_del(&smbdev->node);
<------><------><------>kfree(smbdev);
<------><------>}
<------>}
<------>mutex_unlock(&psmouse_smbus_mutex);
}
int 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)
{
<------>struct psmouse_smbus_dev *smbdev;
<------>int error;
<------>smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
<------>if (!smbdev)
<------><------>return -ENOMEM;
<------>smbdev->psmouse = psmouse;
<------>smbdev->board = *board;
<------>smbdev->need_deactivate = need_deactivate;
<------>if (pdata) {
<------><------>smbdev->board.platform_data = kmemdup(pdata, pdata_size,
<------><------><------><------><------><------> GFP_KERNEL);
<------><------>if (!smbdev->board.platform_data) {
<------><------><------>kfree(smbdev);
<------><------><------>return -ENOMEM;
<------><------>}
<------>}
<------>if (need_deactivate)
<------><------>psmouse_deactivate(psmouse);
<------>psmouse->private = smbdev;
<------>psmouse->protocol_handler = psmouse_smbus_process_byte;
<------>psmouse->reconnect = psmouse_smbus_reconnect;
<------>psmouse->fast_reconnect = psmouse_smbus_reconnect;
<------>psmouse->disconnect = psmouse_smbus_disconnect;
<------>psmouse->resync_time = 0;
<------>mutex_lock(&psmouse_smbus_mutex);
<------>list_add_tail(&smbdev->node, &psmouse_smbus_list);
<------>mutex_unlock(&psmouse_smbus_mutex);
<------>/* Bind to already existing adapters right away */
<------>error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
<------>if (smbdev->client) {
<------><------>/* We have our companion device */
<------><------>return 0;
<------>}
<------>/*
<------> * If we did not create i2c device we will not need platform
<------> * data even if we are leaving breadcrumbs.
<------> */
<------>kfree(smbdev->board.platform_data);
<------>smbdev->board.platform_data = NULL;
<------>if (error < 0 || !leave_breadcrumbs) {
<------><------>mutex_lock(&psmouse_smbus_mutex);
<------><------>list_del(&smbdev->node);
<------><------>mutex_unlock(&psmouse_smbus_mutex);
<------><------>kfree(smbdev);
<------>}
<------>return error < 0 ? error : -EAGAIN;
}
int __init psmouse_smbus_module_init(void)
{
<------>int error;
<------>error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
<------>if (error) {
<------><------>pr_err("failed to register i2c bus notifier: %d\n", error);
<------><------>return error;
<------>}
<------>return 0;
}
void psmouse_smbus_module_exit(void)
{
<------>bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
<------>flush_scheduled_work();
}