Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2015-2018, Intel Corporation.
0004  * Copyright (c) 2021, IBM Corp.
0005  */
0006 
0007 #include <linux/device.h>
0008 #include <linux/list.h>
0009 #include <linux/module.h>
0010 #include <linux/mutex.h>
0011 
0012 #include "kcs_bmc.h"
0013 
0014 /* Implement both the device and client interfaces here */
0015 #include "kcs_bmc_device.h"
0016 #include "kcs_bmc_client.h"
0017 
0018 /* Record registered devices and drivers */
0019 static DEFINE_MUTEX(kcs_bmc_lock);
0020 static LIST_HEAD(kcs_bmc_devices);
0021 static LIST_HEAD(kcs_bmc_drivers);
0022 
0023 /* Consumer data access */
0024 
0025 u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc)
0026 {
0027     return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
0028 }
0029 EXPORT_SYMBOL(kcs_bmc_read_data);
0030 
0031 void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data)
0032 {
0033     kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
0034 }
0035 EXPORT_SYMBOL(kcs_bmc_write_data);
0036 
0037 u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc)
0038 {
0039     return kcs_bmc->ops->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
0040 }
0041 EXPORT_SYMBOL(kcs_bmc_read_status);
0042 
0043 void kcs_bmc_write_status(struct kcs_bmc_device *kcs_bmc, u8 data)
0044 {
0045     kcs_bmc->ops->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
0046 }
0047 EXPORT_SYMBOL(kcs_bmc_write_status);
0048 
0049 void kcs_bmc_update_status(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 val)
0050 {
0051     kcs_bmc->ops->io_updateb(kcs_bmc, kcs_bmc->ioreg.str, mask, val);
0052 }
0053 EXPORT_SYMBOL(kcs_bmc_update_status);
0054 
0055 irqreturn_t kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
0056 {
0057     struct kcs_bmc_client *client;
0058     irqreturn_t rc = IRQ_NONE;
0059 
0060     spin_lock(&kcs_bmc->lock);
0061     client = kcs_bmc->client;
0062     if (client)
0063         rc = client->ops->event(client);
0064     spin_unlock(&kcs_bmc->lock);
0065 
0066     return rc;
0067 }
0068 EXPORT_SYMBOL(kcs_bmc_handle_event);
0069 
0070 int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
0071 {
0072     int rc;
0073 
0074     spin_lock_irq(&kcs_bmc->lock);
0075     if (kcs_bmc->client) {
0076         rc = -EBUSY;
0077     } else {
0078         u8 mask = KCS_BMC_EVENT_TYPE_IBF;
0079 
0080         kcs_bmc->client = client;
0081         kcs_bmc_update_event_mask(kcs_bmc, mask, mask);
0082         rc = 0;
0083     }
0084     spin_unlock_irq(&kcs_bmc->lock);
0085 
0086     return rc;
0087 }
0088 EXPORT_SYMBOL(kcs_bmc_enable_device);
0089 
0090 void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client)
0091 {
0092     spin_lock_irq(&kcs_bmc->lock);
0093     if (client == kcs_bmc->client) {
0094         u8 mask = KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE;
0095 
0096         kcs_bmc_update_event_mask(kcs_bmc, mask, 0);
0097         kcs_bmc->client = NULL;
0098     }
0099     spin_unlock_irq(&kcs_bmc->lock);
0100 }
0101 EXPORT_SYMBOL(kcs_bmc_disable_device);
0102 
0103 int kcs_bmc_add_device(struct kcs_bmc_device *kcs_bmc)
0104 {
0105     struct kcs_bmc_driver *drv;
0106     int error = 0;
0107     int rc;
0108 
0109     spin_lock_init(&kcs_bmc->lock);
0110     kcs_bmc->client = NULL;
0111 
0112     mutex_lock(&kcs_bmc_lock);
0113     list_add(&kcs_bmc->entry, &kcs_bmc_devices);
0114     list_for_each_entry(drv, &kcs_bmc_drivers, entry) {
0115         rc = drv->ops->add_device(kcs_bmc);
0116         if (!rc)
0117             continue;
0118 
0119         dev_err(kcs_bmc->dev, "Failed to add chardev for KCS channel %d: %d",
0120             kcs_bmc->channel, rc);
0121         error = rc;
0122     }
0123     mutex_unlock(&kcs_bmc_lock);
0124 
0125     return error;
0126 }
0127 EXPORT_SYMBOL(kcs_bmc_add_device);
0128 
0129 void kcs_bmc_remove_device(struct kcs_bmc_device *kcs_bmc)
0130 {
0131     struct kcs_bmc_driver *drv;
0132     int rc;
0133 
0134     mutex_lock(&kcs_bmc_lock);
0135     list_del(&kcs_bmc->entry);
0136     list_for_each_entry(drv, &kcs_bmc_drivers, entry) {
0137         rc = drv->ops->remove_device(kcs_bmc);
0138         if (rc)
0139             dev_err(kcs_bmc->dev, "Failed to remove chardev for KCS channel %d: %d",
0140                 kcs_bmc->channel, rc);
0141     }
0142     mutex_unlock(&kcs_bmc_lock);
0143 }
0144 EXPORT_SYMBOL(kcs_bmc_remove_device);
0145 
0146 void kcs_bmc_register_driver(struct kcs_bmc_driver *drv)
0147 {
0148     struct kcs_bmc_device *kcs_bmc;
0149     int rc;
0150 
0151     mutex_lock(&kcs_bmc_lock);
0152     list_add(&drv->entry, &kcs_bmc_drivers);
0153     list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
0154         rc = drv->ops->add_device(kcs_bmc);
0155         if (rc)
0156             dev_err(kcs_bmc->dev, "Failed to add driver for KCS channel %d: %d",
0157                 kcs_bmc->channel, rc);
0158     }
0159     mutex_unlock(&kcs_bmc_lock);
0160 }
0161 EXPORT_SYMBOL(kcs_bmc_register_driver);
0162 
0163 void kcs_bmc_unregister_driver(struct kcs_bmc_driver *drv)
0164 {
0165     struct kcs_bmc_device *kcs_bmc;
0166     int rc;
0167 
0168     mutex_lock(&kcs_bmc_lock);
0169     list_del(&drv->entry);
0170     list_for_each_entry(kcs_bmc, &kcs_bmc_devices, entry) {
0171         rc = drv->ops->remove_device(kcs_bmc);
0172         if (rc)
0173             dev_err(kcs_bmc->dev, "Failed to remove driver for KCS channel %d: %d",
0174                 kcs_bmc->channel, rc);
0175     }
0176     mutex_unlock(&kcs_bmc_lock);
0177 }
0178 EXPORT_SYMBOL(kcs_bmc_unregister_driver);
0179 
0180 void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
0181 {
0182     kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
0183 }
0184 EXPORT_SYMBOL(kcs_bmc_update_event_mask);
0185 
0186 MODULE_LICENSE("GPL v2");
0187 MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
0188 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
0189 MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");