1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* Copyright (c) 2021 IBM Corp. */ 3 4 #include <linux/delay.h> 5 #include <linux/device.h> 6 #include <linux/errno.h> 7 #include <linux/list.h> 8 #include <linux/module.h> 9 #include <linux/sched/signal.h> 10 #include <linux/serio.h> 11 #include <linux/slab.h> 12 13 #include "kcs_bmc_client.h" 14 15 struct kcs_bmc_serio { 16 struct list_head entry; 17 18 struct kcs_bmc_client client; 19 struct serio *port; 20 21 spinlock_t lock; 22 }; 23 24 static inline struct kcs_bmc_serio *client_to_kcs_bmc_serio(struct kcs_bmc_client *client) 25 { 26 return container_of(client, struct kcs_bmc_serio, client); 27 } 28 29 static irqreturn_t kcs_bmc_serio_event(struct kcs_bmc_client *client) 30 { 31 struct kcs_bmc_serio *priv; 32 u8 handled = IRQ_NONE; 33 u8 status; 34 35 priv = client_to_kcs_bmc_serio(client); 36 37 spin_lock(&priv->lock); 38 39 status = kcs_bmc_read_status(client->dev); 40 41 if (status & KCS_BMC_STR_IBF) 42 handled = serio_interrupt(priv->port, kcs_bmc_read_data(client->dev), 0); 43 44 spin_unlock(&priv->lock); 45 46 return handled; 47 } 48 49 static const struct kcs_bmc_client_ops kcs_bmc_serio_client_ops = { 50 .event = kcs_bmc_serio_event, 51 }; 52 53 static int kcs_bmc_serio_open(struct serio *port) 54 { 55 struct kcs_bmc_serio *priv = port->port_data; 56 57 return kcs_bmc_enable_device(priv->client.dev, &priv->client); 58 } 59 60 static void kcs_bmc_serio_close(struct serio *port) 61 { 62 struct kcs_bmc_serio *priv = port->port_data; 63 64 kcs_bmc_disable_device(priv->client.dev, &priv->client); 65 } 66 67 static DEFINE_SPINLOCK(kcs_bmc_serio_instances_lock); 68 static LIST_HEAD(kcs_bmc_serio_instances); 69 70 static int kcs_bmc_serio_add_device(struct kcs_bmc_device *kcs_bmc) 71 { 72 struct kcs_bmc_serio *priv; 73 struct serio *port; 74 75 priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL); 76 77 /* Use kzalloc() as the allocation is cleaned up with kfree() via serio_unregister_port() */ 78 port = kzalloc(sizeof(*port), GFP_KERNEL); 79 if (!(priv && port)) 80 return -ENOMEM; 81 82 port->id.type = SERIO_8042; 83 port->open = kcs_bmc_serio_open; 84 port->close = kcs_bmc_serio_close; 85 port->port_data = priv; 86 port->dev.parent = kcs_bmc->dev; 87 88 spin_lock_init(&priv->lock); 89 priv->port = port; 90 priv->client.dev = kcs_bmc; 91 priv->client.ops = &kcs_bmc_serio_client_ops; 92 93 spin_lock_irq(&kcs_bmc_serio_instances_lock); 94 list_add(&priv->entry, &kcs_bmc_serio_instances); 95 spin_unlock_irq(&kcs_bmc_serio_instances_lock); 96 97 serio_register_port(port); 98 99 dev_info(kcs_bmc->dev, "Initialised serio client for channel %d", kcs_bmc->channel); 100 101 return 0; 102 } 103 104 static int kcs_bmc_serio_remove_device(struct kcs_bmc_device *kcs_bmc) 105 { 106 struct kcs_bmc_serio *priv = NULL, *pos; 107 108 spin_lock_irq(&kcs_bmc_serio_instances_lock); 109 list_for_each_entry(pos, &kcs_bmc_serio_instances, entry) { 110 if (pos->client.dev == kcs_bmc) { 111 priv = pos; 112 list_del(&pos->entry); 113 break; 114 } 115 } 116 spin_unlock_irq(&kcs_bmc_serio_instances_lock); 117 118 if (!priv) 119 return -ENODEV; 120 121 /* kfree()s priv->port via put_device() */ 122 serio_unregister_port(priv->port); 123 124 /* Ensure the IBF IRQ is disabled if we were the active client */ 125 kcs_bmc_disable_device(kcs_bmc, &priv->client); 126 127 devm_kfree(priv->client.dev->dev, priv); 128 129 return 0; 130 } 131 132 static const struct kcs_bmc_driver_ops kcs_bmc_serio_driver_ops = { 133 .add_device = kcs_bmc_serio_add_device, 134 .remove_device = kcs_bmc_serio_remove_device, 135 }; 136 137 static struct kcs_bmc_driver kcs_bmc_serio_driver = { 138 .ops = &kcs_bmc_serio_driver_ops, 139 }; 140 141 static int kcs_bmc_serio_init(void) 142 { 143 kcs_bmc_register_driver(&kcs_bmc_serio_driver); 144 145 return 0; 146 } 147 module_init(kcs_bmc_serio_init); 148 149 static void kcs_bmc_serio_exit(void) 150 { 151 kcs_bmc_unregister_driver(&kcs_bmc_serio_driver); 152 } 153 module_exit(kcs_bmc_serio_exit); 154 155 MODULE_LICENSE("GPL v2"); 156 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 157 MODULE_DESCRIPTION("Adapter driver for serio access to BMC KCS devices"); 158