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 
kcs_bmc_read_data(struct kcs_bmc_device * kcs_bmc)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 
kcs_bmc_write_data(struct kcs_bmc_device * kcs_bmc,u8 data)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 
kcs_bmc_read_status(struct kcs_bmc_device * kcs_bmc)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 
kcs_bmc_write_status(struct kcs_bmc_device * kcs_bmc,u8 data)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 
kcs_bmc_update_status(struct kcs_bmc_device * kcs_bmc,u8 mask,u8 val)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 
kcs_bmc_handle_event(struct kcs_bmc_device * kcs_bmc)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 
kcs_bmc_enable_device(struct kcs_bmc_device * kcs_bmc,struct kcs_bmc_client * client)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 
kcs_bmc_disable_device(struct kcs_bmc_device * kcs_bmc,struct kcs_bmc_client * client)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 
kcs_bmc_add_device(struct kcs_bmc_device * kcs_bmc)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 
kcs_bmc_remove_device(struct kcs_bmc_device * kcs_bmc)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 
kcs_bmc_register_driver(struct kcs_bmc_driver * drv)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 
kcs_bmc_unregister_driver(struct kcs_bmc_driver * drv)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 
kcs_bmc_update_event_mask(struct kcs_bmc_device * kcs_bmc,u8 mask,u8 events)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 <haiyue.wang@linux.intel.com>");
189 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
190 MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
191