Skip to content

Commit 3a3d2f6

Browse files
amboarcminyard
authored andcommitted
ipmi: kcs_bmc: Add serio adaptor
kcs_bmc_serio acts as a bridge between the KCS drivers in the IPMI subsystem and the existing userspace interfaces available through the serio subsystem. This is useful when userspace would like to make use of the BMC KCS devices for purposes that aren't IPMI. Signed-off-by: Andrew Jeffery <[email protected]> Message-Id: <[email protected]> Reviewed-by: Zev Weiss <[email protected]> Signed-off-by: Corey Minyard <[email protected]>
1 parent fb6379f commit 3a3d2f6

File tree

3 files changed

+172
-0
lines changed

3 files changed

+172
-0
lines changed

drivers/char/ipmi/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ config IPMI_KCS_BMC_CDEV_IPMI
137137
This support is also available as a module. The module will be
138138
called kcs_bmc_cdev_ipmi.
139139

140+
config IPMI_KCS_BMC_SERIO
141+
depends on IPMI_KCS_BMC && SERIO
142+
tristate "SerIO adaptor for BMC KCS devices"
143+
help
144+
Adapts the BMC KCS device for the SerIO subsystem. This allows users
145+
to take advantage of userspace interfaces provided by SerIO where
146+
appropriate.
147+
148+
Say YES if you wish to expose KCS devices on the BMC via SerIO
149+
interfaces.
150+
151+
This support is also available as a module. The module will be
152+
called kcs_bmc_serio.
153+
140154
config ASPEED_BT_IPMI_BMC
141155
depends on ARCH_ASPEED || COMPILE_TEST
142156
depends on REGMAP && REGMAP_MMIO && MFD_SYSCON

drivers/char/ipmi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
2323
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
2424
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
2525
obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
26+
obj-$(CONFIG_IPMI_KCS_BMC_SERIO) += kcs_bmc_serio.o
2627
obj-$(CONFIG_IPMI_KCS_BMC_CDEV_IPMI) += kcs_bmc_cdev_ipmi.o
2728
obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
2829
obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o

drivers/char/ipmi/kcs_bmc_serio.c

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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 <[email protected]>");
157+
MODULE_DESCRIPTION("Adapter driver for serio access to BMC KCS devices");

0 commit comments

Comments
 (0)