Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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");