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 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 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 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 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 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 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 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 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 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 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 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 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