Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/ipmi/kcs_bmc.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (c) 2015-2018, Intel Corporation.
4
* Copyright (c) 2021, IBM Corp.
5
*/
6
7
#include <linux/device.h>
8
#include <linux/list.h>
9
#include <linux/module.h>
10
#include <linux/mutex.h>
11
12
#include "kcs_bmc.h"
13
14
/* Implement both the device and client interfaces here */
15
#include "kcs_bmc_device.h"
16
#include "kcs_bmc_client.h"
17
18
/* Record registered devices and drivers */
19
static DEFINE_MUTEX(kcs_bmc_lock);
20
static LIST_HEAD(kcs_bmc_devices);
21
static LIST_HEAD(kcs_bmc_drivers);
22
23
/* Consumer data access */
24
25
u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
26
{
27
return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
28
}
29
EXPORT_SYMBOL(kcs_bmc_read_data);
30
31
void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data)
32
{
33
kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
34
}
35
EXPORT_SYMBOL(kcs_bmc_write_data);
36
37
u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc)
38
{
39
return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
40
}
41
EXPORT_SYMBOL(kcs_bmc_read_status);
42
43
void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data)
44
{
45
kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
46
}
47
EXPORT_SYMBOL(kcs_bmc_write_status);
48
49
void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val)
50
{
51
kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
52
}
53
EXPORT_SYMBOL(kcs_bmc_update_status);
54
55
irqreturn_t kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
56
{
57
struct kcs_bmc_client *client;
58
irqreturn_t rc = IRQ_NONE;
59
unsigned long flags;
60
61
spin_lock_irqsave(&kcs_bmc->lock, flags);
62
client = kcs_bmc->client;
63
if (client)
64
rc = client->ops->event(client);
65
spin_unlock_irqrestore(&kcs_bmc->lock, flags);
66
67
return rc;
68
}
69
EXPORT_SYMBOL(kcs_bmc_handle_event);
70
71
int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
72
{
73
int rc;
74
75
spin_lock_irq(&kcs_bmc->lock);
76
if (kcs_bmc->client) {
77
rc = -EBUSY;
78
} else {
79
u8 mask = KCS_BMC_EVENT_TYPE_IBF;
80
81
kcs_bmc->client = client;
82
kcs_bmc_update_event_mask(kcs_bmc, mask, mask);
83
rc = 0;
84
}
85
spin_unlock_irq(&kcs_bmc->lock);
86
87
return rc;
88
}
89
EXPORT_SYMBOL(kcs_bmc_enable_device);
90
91
void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
92
{
93
spin_lock_irq(&kcs_bmc->lock);
94
if (client == kcs_bmc->client) {
95
u8 mask = KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE;
96
97
kcs_bmc_update_event_mask(kcs_bmc, mask, 0);
98
kcs_bmc->client = NULL;
99
}
100
spin_unlock_irq(&kcs_bmc->lock);
101
}
102
EXPORT_SYMBOL(kcs_bmc_disable_device);
103
104
int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
105
{
106
struct kcs_bmc_driver *drv;
107
int error = 0;
108
int rc;
109
110
spin_lock_init(&kcs_bmc->lock);
111
kcs_bmc->client = NULL;
112
113
mutex_lock(&kcs_bmc_lock);
114
list_add(&kcs_bmc->entry, &kcs_bmc_devices);
115
list_for_each_entry(drv, &kcs_bmc_drivers, entry) {
116
rc = drv->ops->add_device(kcs_bmc);
117
if (!rc)
118
continue;
119
120
dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
121
kcs_bmc->channel, rc);
122
error = rc;
123
}
124
mutex_unlock(&kcs_bmc_lock);
125
126
return error;
127
}
128
EXPORT_SYMBOL(kcs_bmc_add_device);
129
130
void kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
131
{
132
struct kcs_bmc_driver *drv;
133
int rc;
134
135
mutex_lock(&kcs_bmc_lock);
136
list_del(&kcs_bmc->entry);
137
list_for_each_entry(drv, &kcs_bmc_drivers, entry) {
138
rc = drv->ops->remove_device(kcs_bmc);
139
if (rc)
140
dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d",
141
kcs_bmc->channel, rc);
142
}
143
mutex_unlock(&kcs_bmc_lock);
144
}
145
EXPORT_SYMBOL(kcs_bmc_remove_device);
146
147
void kcs_bmc_register_driver(struct kcs_bmc_driver *drv)
148
{
149
struct kcs_bmc_device *kcs_bmc;
150
int rc;
151
152
mutex_lock(&kcs_bmc_lock);
153
list_add(&drv->entry, &kcs_bmc_drivers);
154
list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
155
rc = drv->ops->add_device(kcs_bmc);
156
if (rc)
157
dev_err(kcs_bmc->dev, "Failed to add driver for KCS channel %d: %d",
158
kcs_bmc->channel, rc);
159
}
160
mutex_unlock(&kcs_bmc_lock);
161
}
162
EXPORT_SYMBOL(kcs_bmc_register_driver);
163
164
void kcs_bmc_unregister_driver(struct kcs_bmc_driver *drv)
165
{
166
struct kcs_bmc_device *kcs_bmc;
167
int rc;
168
169
mutex_lock(&kcs_bmc_lock);
170
list_del(&drv->entry);
171
list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
172
rc = drv->ops->remove_device(kcs_bmc);
173
if (rc)
174
dev_err(kcs_bmc->dev, "Failed to remove driver for KCS channel %d: %d",
175
kcs_bmc->channel, rc);
176
}
177
mutex_unlock(&kcs_bmc_lock);
178
}
179
EXPORT_SYMBOL(kcs_bmc_unregister_driver);
180
181
void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
182
{
183
kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
184
}
185
EXPORT_SYMBOL(kcs_bmc_update_event_mask);
186
187
MODULE_LICENSE("GPL v2");
188
MODULE_AUTHOR("Haiyue Wang <[email protected]>");
189
MODULE_AUTHOR("Andrew Jeffery <[email protected]>");
190
MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
191
192