Loading...
Note: File does not exist in v3.1.
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 */
19static DEFINE_MUTEX(kcs_bmc_lock);
20static LIST_HEAD(kcs_bmc_devices);
21static LIST_HEAD(kcs_bmc_drivers);
22
23/* Consumer data access */
24
25u8 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}
29EXPORT_SYMBOL(kcs_bmc_read_data);
30
31void 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}
35EXPORT_SYMBOL(kcs_bmc_write_data);
36
37u8 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}
41EXPORT_SYMBOL(kcs_bmc_read_status);
42
43void 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}
47EXPORT_SYMBOL(kcs_bmc_write_status);
48
49void 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}
53EXPORT_SYMBOL(kcs_bmc_update_status);
54
55irqreturn_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
60 spin_lock(&kcs_bmc->lock);
61 client = kcs_bmc->client;
62 if (client)
63 rc = client->ops->event(client);
64 spin_unlock(&kcs_bmc->lock);
65
66 return rc;
67}
68EXPORT_SYMBOL(kcs_bmc_handle_event);
69
70int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
71{
72 int rc;
73
74 spin_lock_irq(&kcs_bmc->lock);
75 if (kcs_bmc->client) {
76 rc = -EBUSY;
77 } else {
78 u8 mask = KCS_BMC_EVENT_TYPE_IBF;
79
80 kcs_bmc->client = client;
81 kcs_bmc_update_event_mask(kcs_bmc, mask, mask);
82 rc = 0;
83 }
84 spin_unlock_irq(&kcs_bmc->lock);
85
86 return rc;
87}
88EXPORT_SYMBOL(kcs_bmc_enable_device);
89
90void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
91{
92 spin_lock_irq(&kcs_bmc->lock);
93 if (client == kcs_bmc->client) {
94 u8 mask = KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE;
95
96 kcs_bmc_update_event_mask(kcs_bmc, mask, 0);
97 kcs_bmc->client = NULL;
98 }
99 spin_unlock_irq(&kcs_bmc->lock);
100}
101EXPORT_SYMBOL(kcs_bmc_disable_device);
102
103int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
104{
105 struct kcs_bmc_driver *drv;
106 int error = 0;
107 int rc;
108
109 spin_lock_init(&kcs_bmc->lock);
110 kcs_bmc->client = NULL;
111
112 mutex_lock(&kcs_bmc_lock);
113 list_add(&kcs_bmc->entry, &kcs_bmc_devices);
114 list_for_each_entry(drv, &kcs_bmc_drivers, entry) {
115 rc = drv->ops->add_device(kcs_bmc);
116 if (!rc)
117 continue;
118
119 dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
120 kcs_bmc->channel, rc);
121 error = rc;
122 }
123 mutex_unlock(&kcs_bmc_lock);
124
125 return error;
126}
127EXPORT_SYMBOL(kcs_bmc_add_device);
128
129void kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
130{
131 struct kcs_bmc_driver *drv;
132 int rc;
133
134 mutex_lock(&kcs_bmc_lock);
135 list_del(&kcs_bmc->entry);
136 list_for_each_entry(drv, &kcs_bmc_drivers, entry) {
137 rc = drv->ops->remove_device(kcs_bmc);
138 if (rc)
139 dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d",
140 kcs_bmc->channel, rc);
141 }
142 mutex_unlock(&kcs_bmc_lock);
143}
144EXPORT_SYMBOL(kcs_bmc_remove_device);
145
146void kcs_bmc_register_driver(struct kcs_bmc_driver *drv)
147{
148 struct kcs_bmc_device *kcs_bmc;
149 int rc;
150
151 mutex_lock(&kcs_bmc_lock);
152 list_add(&drv->entry, &kcs_bmc_drivers);
153 list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
154 rc = drv->ops->add_device(kcs_bmc);
155 if (rc)
156 dev_err(kcs_bmc->dev, "Failed to add driver for KCS channel %d: %d",
157 kcs_bmc->channel, rc);
158 }
159 mutex_unlock(&kcs_bmc_lock);
160}
161EXPORT_SYMBOL(kcs_bmc_register_driver);
162
163void kcs_bmc_unregister_driver(struct kcs_bmc_driver *drv)
164{
165 struct kcs_bmc_device *kcs_bmc;
166 int rc;
167
168 mutex_lock(&kcs_bmc_lock);
169 list_del(&drv->entry);
170 list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
171 rc = drv->ops->remove_device(kcs_bmc);
172 if (rc)
173 dev_err(kcs_bmc->dev, "Failed to remove driver for KCS channel %d: %d",
174 kcs_bmc->channel, rc);
175 }
176 mutex_unlock(&kcs_bmc_lock);
177}
178EXPORT_SYMBOL(kcs_bmc_unregister_driver);
179
180void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
181{
182 kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
183}
184EXPORT_SYMBOL(kcs_bmc_update_event_mask);
185
186MODULE_LICENSE("GPL v2");
187MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
188MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
189MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");