kcs_bmc.c (4757B)
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 */ 19static DEFINE_MUTEX(kcs_bmc_lock); 20static LIST_HEAD(kcs_bmc_devices); 21static LIST_HEAD(kcs_bmc_drivers); 22 23/* Consumer data access */ 24 25u8 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} 29EXPORT_SYMBOL(kcs_bmc_read_data); 30 31void 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} 35EXPORT_SYMBOL(kcs_bmc_write_data); 36 37u8 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} 41EXPORT_SYMBOL(kcs_bmc_read_status); 42 43void 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} 47EXPORT_SYMBOL(kcs_bmc_write_status); 48 49void 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} 53EXPORT_SYMBOL(kcs_bmc_update_status); 54 55irqreturn_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} 68EXPORT_SYMBOL(kcs_bmc_handle_event); 69 70int 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} 88EXPORT_SYMBOL(kcs_bmc_enable_device); 89 90void 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} 101EXPORT_SYMBOL(kcs_bmc_disable_device); 102 103int 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} 127EXPORT_SYMBOL(kcs_bmc_add_device); 128 129void 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} 144EXPORT_SYMBOL(kcs_bmc_remove_device); 145 146void 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} 161EXPORT_SYMBOL(kcs_bmc_register_driver); 162 163void 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} 178EXPORT_SYMBOL(kcs_bmc_unregister_driver); 179 180void 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} 184EXPORT_SYMBOL(kcs_bmc_update_event_mask); 185 186MODULE_LICENSE("GPL v2"); 187MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); 188MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 189MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");