Commit | Line | Data |
---|---|---|
20d60f61 | 1 | // SPDX-License-Identifier: GPL-2.0 |
3b6d082f HW |
2 | /* |
3 | * Copyright (c) 2015-2018, Intel Corporation. | |
faae6e39 | 4 | * Copyright (c) 2021, IBM Corp. |
3b6d082f | 5 | */ |
20d60f61 | 6 | |
d4e7ac68 | 7 | #include <linux/device.h> |
7cafff99 | 8 | #include <linux/list.h> |
20d60f61 | 9 | #include <linux/module.h> |
7cafff99 | 10 | #include <linux/mutex.h> |
20d60f61 HW |
11 | |
12 | #include "kcs_bmc.h" | |
13 | ||
faae6e39 AJ |
14 | /* Implement both the device and client interfaces here */ |
15 | #include "kcs_bmc_device.h" | |
16 | #include "kcs_bmc_client.h" | |
17 | ||
7cafff99 AJ |
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 | ||
faae6e39 AJ |
23 | /* Consumer data access */ |
24 | ||
d4e7ac68 | 25 | u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc) |
20d60f61 | 26 | { |
faae6e39 | 27 | return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr); |
20d60f61 | 28 | } |
55ab48b4 | 29 | EXPORT_SYMBOL(kcs_bmc_read_data); |
20d60f61 | 30 | |
d4e7ac68 | 31 | void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data) |
20d60f61 | 32 | { |
faae6e39 | 33 | kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data); |
20d60f61 | 34 | } |
55ab48b4 | 35 | EXPORT_SYMBOL(kcs_bmc_write_data); |
20d60f61 | 36 | |
d4e7ac68 | 37 | u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc) |
20d60f61 | 38 | { |
faae6e39 | 39 | return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str); |
20d60f61 | 40 | } |
55ab48b4 | 41 | EXPORT_SYMBOL(kcs_bmc_read_status); |
20d60f61 | 42 | |
d4e7ac68 | 43 | void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data) |
20d60f61 | 44 | { |
faae6e39 | 45 | kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data); |
20d60f61 | 46 | } |
55ab48b4 | 47 | EXPORT_SYMBOL(kcs_bmc_write_status); |
20d60f61 | 48 | |
d4e7ac68 | 49 | void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val) |
20d60f61 | 50 | { |
faae6e39 | 51 | kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val); |
20d60f61 | 52 | } |
55ab48b4 | 53 | EXPORT_SYMBOL(kcs_bmc_update_status); |
20d60f61 | 54 | |
d4e7ac68 | 55 | irqreturn_t kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc) |
20d60f61 | 56 | { |
d4e7ac68 | 57 | struct kcs_bmc_client *client; |
fb6379f5 | 58 | irqreturn_t rc = IRQ_NONE; |
b02bb79e | 59 | unsigned long flags; |
d4e7ac68 | 60 | |
b02bb79e | 61 | spin_lock_irqsave(&kcs_bmc->lock, flags); |
d4e7ac68 | 62 | client = kcs_bmc->client; |
fb6379f5 | 63 | if (client) |
d4e7ac68 | 64 | rc = client->ops->event(client); |
b02bb79e | 65 | spin_unlock_irqrestore(&kcs_bmc->lock, flags); |
d4e7ac68 AJ |
66 | |
67 | return rc; | |
20d60f61 HW |
68 | } |
69 | EXPORT_SYMBOL(kcs_bmc_handle_event); | |
70 | ||
d4e7ac68 AJ |
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 { | |
fb6379f5 AJ |
79 | u8 mask = KCS_BMC_EVENT_TYPE_IBF; |
80 | ||
d4e7ac68 | 81 | kcs_bmc->client = client; |
fb6379f5 | 82 | kcs_bmc_update_event_mask(kcs_bmc, mask, mask); |
d4e7ac68 AJ |
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); | |
fb6379f5 AJ |
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); | |
d4e7ac68 | 98 | kcs_bmc->client = NULL; |
fb6379f5 | 99 | } |
d4e7ac68 AJ |
100 | spin_unlock_irq(&kcs_bmc->lock); |
101 | } | |
102 | EXPORT_SYMBOL(kcs_bmc_disable_device); | |
103 | ||
d4e7ac68 | 104 | int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc) |
20d60f61 | 105 | { |
7cafff99 AJ |
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; | |
20d60f61 | 127 | } |
d7096970 AJ |
128 | EXPORT_SYMBOL(kcs_bmc_add_device); |
129 | ||
d4e7ac68 | 130 | void kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc) |
d7096970 | 131 | { |
7cafff99 AJ |
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); | |
d7096970 AJ |
144 | } |
145 | EXPORT_SYMBOL(kcs_bmc_remove_device); | |
20d60f61 | 146 | |
7cafff99 AJ |
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 | ||
28651e6c AJ |
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 | ||
20d60f61 HW |
187 | MODULE_LICENSE("GPL v2"); |
188 | MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); | |
faae6e39 | 189 | MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); |
20d60f61 | 190 | MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); |