GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / char / ipmi / kcs_bmc.c
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
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 }
68 EXPORT_SYMBOL(kcs_bmc_handle_event);
69
70 int 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 }
88 EXPORT_SYMBOL(kcs_bmc_enable_device);
89
90 void 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 }
101 EXPORT_SYMBOL(kcs_bmc_disable_device);
102
103 int 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 }
127 EXPORT_SYMBOL(kcs_bmc_add_device);
128
129 void 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 }
144 EXPORT_SYMBOL(kcs_bmc_remove_device);
145
146 void 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 }
161 EXPORT_SYMBOL(kcs_bmc_register_driver);
162
163 void 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 }
178 EXPORT_SYMBOL(kcs_bmc_unregister_driver);
179
180 void 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 }
184 EXPORT_SYMBOL(kcs_bmc_update_event_mask);
185
186 MODULE_LICENSE("GPL v2");
187 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
188 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
189 MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");