diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/char/ipmi/kcs_bmc.c | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/char/ipmi/kcs_bmc.c')
-rw-r--r-- | drivers/char/ipmi/kcs_bmc.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c new file mode 100644 index 0000000000..8b1161d519 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015-2018, Intel Corporation. + * Copyright (c) 2021, IBM Corp. + */ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> + +#include "kcs_bmc.h" + +/* Implement both the device and client interfaces here */ +#include "kcs_bmc_device.h" +#include "kcs_bmc_client.h" + +/* Record registered devices and drivers */ +static DEFINE_MUTEX(kcs_bmc_lock); +static LIST_HEAD(kcs_bmc_devices); +static LIST_HEAD(kcs_bmc_drivers); + +/* Consumer data access */ + +u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc) +{ + return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr); +} +EXPORT_SYMBOL(kcs_bmc_read_data); + +void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data) +{ + kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data); +} +EXPORT_SYMBOL(kcs_bmc_write_data); + +u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc) +{ + return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str); +} +EXPORT_SYMBOL(kcs_bmc_read_status); + +void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data) +{ + kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data); +} +EXPORT_SYMBOL(kcs_bmc_write_status); + +void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val) +{ + kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val); +} +EXPORT_SYMBOL(kcs_bmc_update_status); + +irqreturn_t kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc) +{ + struct kcs_bmc_client *client; + irqreturn_t rc = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&kcs_bmc->lock, flags); + client = kcs_bmc->client; + if (client) + rc = client->ops->event(client); + spin_unlock_irqrestore(&kcs_bmc->lock, flags); + + return rc; +} +EXPORT_SYMBOL(kcs_bmc_handle_event); + +int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client) +{ + int rc; + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->client) { + rc = -EBUSY; + } else { + u8 mask = KCS_BMC_EVENT_TYPE_IBF; + + kcs_bmc->client = client; + kcs_bmc_update_event_mask(kcs_bmc, mask, mask); + rc = 0; + } + spin_unlock_irq(&kcs_bmc->lock); + + return rc; +} +EXPORT_SYMBOL(kcs_bmc_enable_device); + +void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client) +{ + spin_lock_irq(&kcs_bmc->lock); + if (client == kcs_bmc->client) { + u8 mask = KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE; + + kcs_bmc_update_event_mask(kcs_bmc, mask, 0); + kcs_bmc->client = NULL; + } + spin_unlock_irq(&kcs_bmc->lock); +} +EXPORT_SYMBOL(kcs_bmc_disable_device); + +int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc) +{ + struct kcs_bmc_driver *drv; + int error = 0; + int rc; + + spin_lock_init(&kcs_bmc->lock); + kcs_bmc->client = NULL; + + mutex_lock(&kcs_bmc_lock); + list_add(&kcs_bmc->entry, &kcs_bmc_devices); + list_for_each_entry(drv, &kcs_bmc_drivers, entry) { + rc = drv->ops->add_device(kcs_bmc); + if (!rc) + continue; + + dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d", + kcs_bmc->channel, rc); + error = rc; + } + mutex_unlock(&kcs_bmc_lock); + + return error; +} +EXPORT_SYMBOL(kcs_bmc_add_device); + +void kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc) +{ + struct kcs_bmc_driver *drv; + int rc; + + mutex_lock(&kcs_bmc_lock); + list_del(&kcs_bmc->entry); + list_for_each_entry(drv, &kcs_bmc_drivers, entry) { + rc = drv->ops->remove_device(kcs_bmc); + if (rc) + dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d", + kcs_bmc->channel, rc); + } + mutex_unlock(&kcs_bmc_lock); +} +EXPORT_SYMBOL(kcs_bmc_remove_device); + +void kcs_bmc_register_driver(struct kcs_bmc_driver *drv) +{ + struct kcs_bmc_device *kcs_bmc; + int rc; + + mutex_lock(&kcs_bmc_lock); + list_add(&drv->entry, &kcs_bmc_drivers); + list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) { + rc = drv->ops->add_device(kcs_bmc); + if (rc) + dev_err(kcs_bmc->dev, "Failed to add driver for KCS channel %d: %d", + kcs_bmc->channel, rc); + } + mutex_unlock(&kcs_bmc_lock); +} +EXPORT_SYMBOL(kcs_bmc_register_driver); + +void kcs_bmc_unregister_driver(struct kcs_bmc_driver *drv) +{ + struct kcs_bmc_device *kcs_bmc; + int rc; + + mutex_lock(&kcs_bmc_lock); + list_del(&drv->entry); + list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) { + rc = drv->ops->remove_device(kcs_bmc); + if (rc) + dev_err(kcs_bmc->dev, "Failed to remove driver for KCS channel %d: %d", + kcs_bmc->channel, rc); + } + mutex_unlock(&kcs_bmc_lock); +} +EXPORT_SYMBOL(kcs_bmc_unregister_driver); + +void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events) +{ + kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events); +} +EXPORT_SYMBOL(kcs_bmc_update_event_mask); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); +MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); +MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); |