155ab48b4SAndrew Jeffery // SPDX-License-Identifier: GPL-2.0 255ab48b4SAndrew Jeffery /* 355ab48b4SAndrew Jeffery * Copyright (c) 2015-2018, Intel Corporation. 455ab48b4SAndrew Jeffery */ 555ab48b4SAndrew Jeffery 655ab48b4SAndrew Jeffery #define pr_fmt(fmt) "kcs-bmc: " fmt 755ab48b4SAndrew Jeffery 855ab48b4SAndrew Jeffery #include <linux/errno.h> 955ab48b4SAndrew Jeffery #include <linux/io.h> 1055ab48b4SAndrew Jeffery #include <linux/ipmi_bmc.h> 11d4e7ac68SAndrew Jeffery #include <linux/list.h> 12d4e7ac68SAndrew Jeffery #include <linux/miscdevice.h> 1355ab48b4SAndrew Jeffery #include <linux/module.h> 14d4e7ac68SAndrew Jeffery #include <linux/mutex.h> 1555ab48b4SAndrew Jeffery #include <linux/platform_device.h> 1655ab48b4SAndrew Jeffery #include <linux/poll.h> 1755ab48b4SAndrew Jeffery #include <linux/sched.h> 1855ab48b4SAndrew Jeffery #include <linux/slab.h> 1955ab48b4SAndrew Jeffery 20d4e7ac68SAndrew Jeffery #include "kcs_bmc_client.h" 21d4e7ac68SAndrew Jeffery 22d4e7ac68SAndrew Jeffery /* Different phases of the KCS BMC module. 23d4e7ac68SAndrew Jeffery * KCS_PHASE_IDLE: 24d4e7ac68SAndrew Jeffery * BMC should not be expecting nor sending any data. 25d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_START: 26d4e7ac68SAndrew Jeffery * BMC is receiving a WRITE_START command from system software. 27d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_DATA: 28d4e7ac68SAndrew Jeffery * BMC is receiving a data byte from system software. 29d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_END_CMD: 30d4e7ac68SAndrew Jeffery * BMC is waiting a last data byte from system software. 31d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_DONE: 32d4e7ac68SAndrew Jeffery * BMC has received the whole request from system software. 33d4e7ac68SAndrew Jeffery * KCS_PHASE_WAIT_READ: 34d4e7ac68SAndrew Jeffery * BMC is waiting the response from the upper IPMI service. 35d4e7ac68SAndrew Jeffery * KCS_PHASE_READ: 36d4e7ac68SAndrew Jeffery * BMC is transferring the response to system software. 37d4e7ac68SAndrew Jeffery * KCS_PHASE_ABORT_ERROR1: 38d4e7ac68SAndrew Jeffery * BMC is waiting error status request from system software. 39d4e7ac68SAndrew Jeffery * KCS_PHASE_ABORT_ERROR2: 40d4e7ac68SAndrew Jeffery * BMC is waiting for idle status afer error from system software. 41d4e7ac68SAndrew Jeffery * KCS_PHASE_ERROR: 42d4e7ac68SAndrew Jeffery * BMC has detected a protocol violation at the interface level. 43d4e7ac68SAndrew Jeffery */ 44d4e7ac68SAndrew Jeffery enum kcs_ipmi_phases { 45d4e7ac68SAndrew Jeffery KCS_PHASE_IDLE, 46d4e7ac68SAndrew Jeffery 47d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_START, 48d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_DATA, 49d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_END_CMD, 50d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_DONE, 51d4e7ac68SAndrew Jeffery 52d4e7ac68SAndrew Jeffery KCS_PHASE_WAIT_READ, 53d4e7ac68SAndrew Jeffery KCS_PHASE_READ, 54d4e7ac68SAndrew Jeffery 55d4e7ac68SAndrew Jeffery KCS_PHASE_ABORT_ERROR1, 56d4e7ac68SAndrew Jeffery KCS_PHASE_ABORT_ERROR2, 57d4e7ac68SAndrew Jeffery KCS_PHASE_ERROR 58d4e7ac68SAndrew Jeffery }; 59d4e7ac68SAndrew Jeffery 60d4e7ac68SAndrew Jeffery /* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */ 61d4e7ac68SAndrew Jeffery enum kcs_ipmi_errors { 62d4e7ac68SAndrew Jeffery KCS_NO_ERROR = 0x00, 63d4e7ac68SAndrew Jeffery KCS_ABORTED_BY_COMMAND = 0x01, 64d4e7ac68SAndrew Jeffery KCS_ILLEGAL_CONTROL_CODE = 0x02, 65d4e7ac68SAndrew Jeffery KCS_LENGTH_ERROR = 0x06, 66d4e7ac68SAndrew Jeffery KCS_UNSPECIFIED_ERROR = 0xFF 67d4e7ac68SAndrew Jeffery }; 68d4e7ac68SAndrew Jeffery 69d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi { 70d4e7ac68SAndrew Jeffery struct list_head entry; 71d4e7ac68SAndrew Jeffery 72d4e7ac68SAndrew Jeffery struct kcs_bmc_client client; 73d4e7ac68SAndrew Jeffery 74d4e7ac68SAndrew Jeffery spinlock_t lock; 75d4e7ac68SAndrew Jeffery 76d4e7ac68SAndrew Jeffery enum kcs_ipmi_phases phase; 77d4e7ac68SAndrew Jeffery enum kcs_ipmi_errors error; 78d4e7ac68SAndrew Jeffery 79d4e7ac68SAndrew Jeffery wait_queue_head_t queue; 80d4e7ac68SAndrew Jeffery bool data_in_avail; 81d4e7ac68SAndrew Jeffery int data_in_idx; 82d4e7ac68SAndrew Jeffery u8 *data_in; 83d4e7ac68SAndrew Jeffery 84d4e7ac68SAndrew Jeffery int data_out_idx; 85d4e7ac68SAndrew Jeffery int data_out_len; 86d4e7ac68SAndrew Jeffery u8 *data_out; 87d4e7ac68SAndrew Jeffery 88d4e7ac68SAndrew Jeffery struct mutex mutex; 89d4e7ac68SAndrew Jeffery u8 *kbuffer; 90d4e7ac68SAndrew Jeffery 91d4e7ac68SAndrew Jeffery struct miscdevice miscdev; 92d4e7ac68SAndrew Jeffery }; 9355ab48b4SAndrew Jeffery 9455ab48b4SAndrew Jeffery #define DEVICE_NAME "ipmi-kcs" 9555ab48b4SAndrew Jeffery 9655ab48b4SAndrew Jeffery #define KCS_MSG_BUFSIZ 1000 9755ab48b4SAndrew Jeffery 9855ab48b4SAndrew Jeffery #define KCS_ZERO_DATA 0 9955ab48b4SAndrew Jeffery 10055ab48b4SAndrew Jeffery /* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */ 10155ab48b4SAndrew Jeffery #define KCS_STATUS_STATE(state) (state << 6) 10255ab48b4SAndrew Jeffery #define KCS_STATUS_STATE_MASK GENMASK(7, 6) 10355ab48b4SAndrew Jeffery #define KCS_STATUS_CMD_DAT BIT(3) 10455ab48b4SAndrew Jeffery #define KCS_STATUS_SMS_ATN BIT(2) 10555ab48b4SAndrew Jeffery #define KCS_STATUS_IBF BIT(1) 10655ab48b4SAndrew Jeffery #define KCS_STATUS_OBF BIT(0) 10755ab48b4SAndrew Jeffery 10855ab48b4SAndrew Jeffery /* IPMI 2.0 - Table 9-2, KCS Interface State Bits */ 10955ab48b4SAndrew Jeffery enum kcs_states { 11055ab48b4SAndrew Jeffery IDLE_STATE = 0, 11155ab48b4SAndrew Jeffery READ_STATE = 1, 11255ab48b4SAndrew Jeffery WRITE_STATE = 2, 11355ab48b4SAndrew Jeffery ERROR_STATE = 3, 11455ab48b4SAndrew Jeffery }; 11555ab48b4SAndrew Jeffery 11655ab48b4SAndrew Jeffery /* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */ 11755ab48b4SAndrew Jeffery #define KCS_CMD_GET_STATUS_ABORT 0x60 11855ab48b4SAndrew Jeffery #define KCS_CMD_WRITE_START 0x61 11955ab48b4SAndrew Jeffery #define KCS_CMD_WRITE_END 0x62 12055ab48b4SAndrew Jeffery #define KCS_CMD_READ_BYTE 0x68 12155ab48b4SAndrew Jeffery 122d4e7ac68SAndrew Jeffery static inline void set_state(struct kcs_bmc_ipmi *priv, u8 state) 12355ab48b4SAndrew Jeffery { 124d4e7ac68SAndrew Jeffery kcs_bmc_update_status(priv->client.dev, KCS_STATUS_STATE_MASK, KCS_STATUS_STATE(state)); 12555ab48b4SAndrew Jeffery } 12655ab48b4SAndrew Jeffery 127d4e7ac68SAndrew Jeffery static void kcs_bmc_ipmi_force_abort(struct kcs_bmc_ipmi *priv) 12855ab48b4SAndrew Jeffery { 129d4e7ac68SAndrew Jeffery set_state(priv, ERROR_STATE); 130d4e7ac68SAndrew Jeffery kcs_bmc_read_data(priv->client.dev); 131d4e7ac68SAndrew Jeffery kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA); 13255ab48b4SAndrew Jeffery 133d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_ERROR; 134d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 135d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 13655ab48b4SAndrew Jeffery } 13755ab48b4SAndrew Jeffery 138d4e7ac68SAndrew Jeffery static void kcs_bmc_ipmi_handle_data(struct kcs_bmc_ipmi *priv) 13955ab48b4SAndrew Jeffery { 140d4e7ac68SAndrew Jeffery struct kcs_bmc_device *dev; 14155ab48b4SAndrew Jeffery u8 data; 14255ab48b4SAndrew Jeffery 143d4e7ac68SAndrew Jeffery dev = priv->client.dev; 144d4e7ac68SAndrew Jeffery 145d4e7ac68SAndrew Jeffery switch (priv->phase) { 14655ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_START: 147d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_DATA; 14855ab48b4SAndrew Jeffery fallthrough; 14955ab48b4SAndrew Jeffery 15055ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_DATA: 151d4e7ac68SAndrew Jeffery if (priv->data_in_idx < KCS_MSG_BUFSIZ) { 152d4e7ac68SAndrew Jeffery set_state(priv, WRITE_STATE); 153d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 154d4e7ac68SAndrew Jeffery priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev); 15555ab48b4SAndrew Jeffery } else { 156d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 157d4e7ac68SAndrew Jeffery priv->error = KCS_LENGTH_ERROR; 15855ab48b4SAndrew Jeffery } 15955ab48b4SAndrew Jeffery break; 16055ab48b4SAndrew Jeffery 16155ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_END_CMD: 162d4e7ac68SAndrew Jeffery if (priv->data_in_idx < KCS_MSG_BUFSIZ) { 163d4e7ac68SAndrew Jeffery set_state(priv, READ_STATE); 164d4e7ac68SAndrew Jeffery priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev); 165d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_DONE; 166d4e7ac68SAndrew Jeffery priv->data_in_avail = true; 167d4e7ac68SAndrew Jeffery wake_up_interruptible(&priv->queue); 16855ab48b4SAndrew Jeffery } else { 169d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 170d4e7ac68SAndrew Jeffery priv->error = KCS_LENGTH_ERROR; 17155ab48b4SAndrew Jeffery } 17255ab48b4SAndrew Jeffery break; 17355ab48b4SAndrew Jeffery 17455ab48b4SAndrew Jeffery case KCS_PHASE_READ: 175d4e7ac68SAndrew Jeffery if (priv->data_out_idx == priv->data_out_len) 176d4e7ac68SAndrew Jeffery set_state(priv, IDLE_STATE); 17755ab48b4SAndrew Jeffery 178d4e7ac68SAndrew Jeffery data = kcs_bmc_read_data(dev); 17955ab48b4SAndrew Jeffery if (data != KCS_CMD_READ_BYTE) { 180d4e7ac68SAndrew Jeffery set_state(priv, ERROR_STATE); 181d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 18255ab48b4SAndrew Jeffery break; 18355ab48b4SAndrew Jeffery } 18455ab48b4SAndrew Jeffery 185d4e7ac68SAndrew Jeffery if (priv->data_out_idx == priv->data_out_len) { 186d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 187d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_IDLE; 18855ab48b4SAndrew Jeffery break; 18955ab48b4SAndrew Jeffery } 19055ab48b4SAndrew Jeffery 191d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, priv->data_out[priv->data_out_idx++]); 19255ab48b4SAndrew Jeffery break; 19355ab48b4SAndrew Jeffery 19455ab48b4SAndrew Jeffery case KCS_PHASE_ABORT_ERROR1: 195d4e7ac68SAndrew Jeffery set_state(priv, READ_STATE); 196d4e7ac68SAndrew Jeffery kcs_bmc_read_data(dev); 197d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, priv->error); 198d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_ABORT_ERROR2; 19955ab48b4SAndrew Jeffery break; 20055ab48b4SAndrew Jeffery 20155ab48b4SAndrew Jeffery case KCS_PHASE_ABORT_ERROR2: 202d4e7ac68SAndrew Jeffery set_state(priv, IDLE_STATE); 203d4e7ac68SAndrew Jeffery kcs_bmc_read_data(dev); 204d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 205d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_IDLE; 20655ab48b4SAndrew Jeffery break; 20755ab48b4SAndrew Jeffery 20855ab48b4SAndrew Jeffery default: 209d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 21055ab48b4SAndrew Jeffery break; 21155ab48b4SAndrew Jeffery } 21255ab48b4SAndrew Jeffery } 21355ab48b4SAndrew Jeffery 214d4e7ac68SAndrew Jeffery static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc_ipmi *priv) 21555ab48b4SAndrew Jeffery { 21655ab48b4SAndrew Jeffery u8 cmd; 21755ab48b4SAndrew Jeffery 218d4e7ac68SAndrew Jeffery set_state(priv, WRITE_STATE); 219d4e7ac68SAndrew Jeffery kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA); 22055ab48b4SAndrew Jeffery 221d4e7ac68SAndrew Jeffery cmd = kcs_bmc_read_data(priv->client.dev); 22255ab48b4SAndrew Jeffery switch (cmd) { 22355ab48b4SAndrew Jeffery case KCS_CMD_WRITE_START: 224d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_START; 225d4e7ac68SAndrew Jeffery priv->error = KCS_NO_ERROR; 226d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 227d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 22855ab48b4SAndrew Jeffery break; 22955ab48b4SAndrew Jeffery 23055ab48b4SAndrew Jeffery case KCS_CMD_WRITE_END: 231d4e7ac68SAndrew Jeffery if (priv->phase != KCS_PHASE_WRITE_DATA) { 232d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 23355ab48b4SAndrew Jeffery break; 23455ab48b4SAndrew Jeffery } 23555ab48b4SAndrew Jeffery 236d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_END_CMD; 23755ab48b4SAndrew Jeffery break; 23855ab48b4SAndrew Jeffery 23955ab48b4SAndrew Jeffery case KCS_CMD_GET_STATUS_ABORT: 240d4e7ac68SAndrew Jeffery if (priv->error == KCS_NO_ERROR) 241d4e7ac68SAndrew Jeffery priv->error = KCS_ABORTED_BY_COMMAND; 24255ab48b4SAndrew Jeffery 243d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_ABORT_ERROR1; 244d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 245d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 24655ab48b4SAndrew Jeffery break; 24755ab48b4SAndrew Jeffery 24855ab48b4SAndrew Jeffery default: 249d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 250d4e7ac68SAndrew Jeffery priv->error = KCS_ILLEGAL_CONTROL_CODE; 25155ab48b4SAndrew Jeffery break; 25255ab48b4SAndrew Jeffery } 25355ab48b4SAndrew Jeffery } 25455ab48b4SAndrew Jeffery 255d4e7ac68SAndrew Jeffery static inline struct kcs_bmc_ipmi *client_to_kcs_bmc_ipmi(struct kcs_bmc_client *client) 25655ab48b4SAndrew Jeffery { 257d4e7ac68SAndrew Jeffery return container_of(client, struct kcs_bmc_ipmi, client); 258faae6e39SAndrew Jeffery } 259faae6e39SAndrew Jeffery 260faae6e39SAndrew Jeffery static irqreturn_t kcs_bmc_ipmi_event(struct kcs_bmc_client *client) 261faae6e39SAndrew Jeffery { 262d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv; 26355ab48b4SAndrew Jeffery u8 status; 264faae6e39SAndrew Jeffery int ret; 265faae6e39SAndrew Jeffery 266d4e7ac68SAndrew Jeffery priv = client_to_kcs_bmc_ipmi(client); 267d4e7ac68SAndrew Jeffery if (!priv) 268d4e7ac68SAndrew Jeffery return IRQ_NONE; 26955ab48b4SAndrew Jeffery 270d4e7ac68SAndrew Jeffery spin_lock(&priv->lock); 27155ab48b4SAndrew Jeffery 272d4e7ac68SAndrew Jeffery status = kcs_bmc_read_status(client->dev); 27355ab48b4SAndrew Jeffery if (status & KCS_STATUS_IBF) { 274d4e7ac68SAndrew Jeffery if (status & KCS_STATUS_CMD_DAT) 275d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_handle_cmd(priv); 27655ab48b4SAndrew Jeffery else 277d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_handle_data(priv); 27855ab48b4SAndrew Jeffery 279faae6e39SAndrew Jeffery ret = IRQ_HANDLED; 280faae6e39SAndrew Jeffery } else { 281faae6e39SAndrew Jeffery ret = IRQ_NONE; 28255ab48b4SAndrew Jeffery } 28355ab48b4SAndrew Jeffery 284d4e7ac68SAndrew Jeffery spin_unlock(&priv->lock); 28555ab48b4SAndrew Jeffery 28655ab48b4SAndrew Jeffery return ret; 28755ab48b4SAndrew Jeffery } 28855ab48b4SAndrew Jeffery 289faae6e39SAndrew Jeffery static const struct kcs_bmc_client_ops kcs_bmc_ipmi_client_ops = { 290faae6e39SAndrew Jeffery .event = kcs_bmc_ipmi_event, 291faae6e39SAndrew Jeffery }; 292faae6e39SAndrew Jeffery 293d4e7ac68SAndrew Jeffery static inline struct kcs_bmc_ipmi *to_kcs_bmc(struct file *filp) 29455ab48b4SAndrew Jeffery { 295d4e7ac68SAndrew Jeffery return container_of(filp->private_data, struct kcs_bmc_ipmi, miscdev); 29655ab48b4SAndrew Jeffery } 29755ab48b4SAndrew Jeffery 29855ab48b4SAndrew Jeffery static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp) 29955ab48b4SAndrew Jeffery { 300d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 30155ab48b4SAndrew Jeffery 302d4e7ac68SAndrew Jeffery return kcs_bmc_enable_device(priv->client.dev, &priv->client); 30355ab48b4SAndrew Jeffery } 30455ab48b4SAndrew Jeffery 30555ab48b4SAndrew Jeffery static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait) 30655ab48b4SAndrew Jeffery { 307d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 30855ab48b4SAndrew Jeffery __poll_t mask = 0; 30955ab48b4SAndrew Jeffery 310d4e7ac68SAndrew Jeffery poll_wait(filp, &priv->queue, wait); 31155ab48b4SAndrew Jeffery 312d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 313d4e7ac68SAndrew Jeffery if (priv->data_in_avail) 31455ab48b4SAndrew Jeffery mask |= EPOLLIN; 315d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 31655ab48b4SAndrew Jeffery 31755ab48b4SAndrew Jeffery return mask; 31855ab48b4SAndrew Jeffery } 31955ab48b4SAndrew Jeffery 32055ab48b4SAndrew Jeffery static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf, 32155ab48b4SAndrew Jeffery size_t count, loff_t *ppos) 32255ab48b4SAndrew Jeffery { 323d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 32455ab48b4SAndrew Jeffery bool data_avail; 32555ab48b4SAndrew Jeffery size_t data_len; 32655ab48b4SAndrew Jeffery ssize_t ret; 32755ab48b4SAndrew Jeffery 32855ab48b4SAndrew Jeffery if (!(filp->f_flags & O_NONBLOCK)) 329d4e7ac68SAndrew Jeffery wait_event_interruptible(priv->queue, 330d4e7ac68SAndrew Jeffery priv->data_in_avail); 33155ab48b4SAndrew Jeffery 332d4e7ac68SAndrew Jeffery mutex_lock(&priv->mutex); 33355ab48b4SAndrew Jeffery 334d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 335d4e7ac68SAndrew Jeffery data_avail = priv->data_in_avail; 33655ab48b4SAndrew Jeffery if (data_avail) { 337d4e7ac68SAndrew Jeffery data_len = priv->data_in_idx; 338d4e7ac68SAndrew Jeffery memcpy(priv->kbuffer, priv->data_in, data_len); 33955ab48b4SAndrew Jeffery } 340d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 34155ab48b4SAndrew Jeffery 34255ab48b4SAndrew Jeffery if (!data_avail) { 34355ab48b4SAndrew Jeffery ret = -EAGAIN; 34455ab48b4SAndrew Jeffery goto out_unlock; 34555ab48b4SAndrew Jeffery } 34655ab48b4SAndrew Jeffery 34755ab48b4SAndrew Jeffery if (count < data_len) { 34855ab48b4SAndrew Jeffery pr_err("channel=%u with too large data : %zu\n", 349d4e7ac68SAndrew Jeffery priv->client.dev->channel, data_len); 35055ab48b4SAndrew Jeffery 351d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 352d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 353d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 35455ab48b4SAndrew Jeffery 35555ab48b4SAndrew Jeffery ret = -EOVERFLOW; 35655ab48b4SAndrew Jeffery goto out_unlock; 35755ab48b4SAndrew Jeffery } 35855ab48b4SAndrew Jeffery 359d4e7ac68SAndrew Jeffery if (copy_to_user(buf, priv->kbuffer, data_len)) { 36055ab48b4SAndrew Jeffery ret = -EFAULT; 36155ab48b4SAndrew Jeffery goto out_unlock; 36255ab48b4SAndrew Jeffery } 36355ab48b4SAndrew Jeffery 36455ab48b4SAndrew Jeffery ret = data_len; 36555ab48b4SAndrew Jeffery 366d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 367d4e7ac68SAndrew Jeffery if (priv->phase == KCS_PHASE_WRITE_DONE) { 368d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WAIT_READ; 369d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 370d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 37155ab48b4SAndrew Jeffery } else { 37255ab48b4SAndrew Jeffery ret = -EAGAIN; 37355ab48b4SAndrew Jeffery } 374d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 37555ab48b4SAndrew Jeffery 37655ab48b4SAndrew Jeffery out_unlock: 377d4e7ac68SAndrew Jeffery mutex_unlock(&priv->mutex); 37855ab48b4SAndrew Jeffery 37955ab48b4SAndrew Jeffery return ret; 38055ab48b4SAndrew Jeffery } 38155ab48b4SAndrew Jeffery 38255ab48b4SAndrew Jeffery static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf, 38355ab48b4SAndrew Jeffery size_t count, loff_t *ppos) 38455ab48b4SAndrew Jeffery { 385d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 38655ab48b4SAndrew Jeffery ssize_t ret; 38755ab48b4SAndrew Jeffery 38855ab48b4SAndrew Jeffery /* a minimum response size '3' : netfn + cmd + ccode */ 38955ab48b4SAndrew Jeffery if (count < 3 || count > KCS_MSG_BUFSIZ) 39055ab48b4SAndrew Jeffery return -EINVAL; 39155ab48b4SAndrew Jeffery 392d4e7ac68SAndrew Jeffery mutex_lock(&priv->mutex); 39355ab48b4SAndrew Jeffery 394d4e7ac68SAndrew Jeffery if (copy_from_user(priv->kbuffer, buf, count)) { 39555ab48b4SAndrew Jeffery ret = -EFAULT; 39655ab48b4SAndrew Jeffery goto out_unlock; 39755ab48b4SAndrew Jeffery } 39855ab48b4SAndrew Jeffery 399d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 400d4e7ac68SAndrew Jeffery if (priv->phase == KCS_PHASE_WAIT_READ) { 401d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_READ; 402d4e7ac68SAndrew Jeffery priv->data_out_idx = 1; 403d4e7ac68SAndrew Jeffery priv->data_out_len = count; 404d4e7ac68SAndrew Jeffery memcpy(priv->data_out, priv->kbuffer, count); 405d4e7ac68SAndrew Jeffery kcs_bmc_write_data(priv->client.dev, priv->data_out[0]); 40655ab48b4SAndrew Jeffery ret = count; 40755ab48b4SAndrew Jeffery } else { 40855ab48b4SAndrew Jeffery ret = -EINVAL; 40955ab48b4SAndrew Jeffery } 410d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 41155ab48b4SAndrew Jeffery 41255ab48b4SAndrew Jeffery out_unlock: 413d4e7ac68SAndrew Jeffery mutex_unlock(&priv->mutex); 41455ab48b4SAndrew Jeffery 41555ab48b4SAndrew Jeffery return ret; 41655ab48b4SAndrew Jeffery } 41755ab48b4SAndrew Jeffery 41855ab48b4SAndrew Jeffery static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd, 41955ab48b4SAndrew Jeffery unsigned long arg) 42055ab48b4SAndrew Jeffery { 421d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 42255ab48b4SAndrew Jeffery long ret = 0; 42355ab48b4SAndrew Jeffery 424d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 42555ab48b4SAndrew Jeffery 42655ab48b4SAndrew Jeffery switch (cmd) { 42755ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_SET_SMS_ATN: 428d4e7ac68SAndrew Jeffery kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN); 42955ab48b4SAndrew Jeffery break; 43055ab48b4SAndrew Jeffery 43155ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_CLEAR_SMS_ATN: 432d4e7ac68SAndrew Jeffery kcs_bmc_update_status(priv->client.dev, KCS_STATUS_SMS_ATN, 0); 43355ab48b4SAndrew Jeffery break; 43455ab48b4SAndrew Jeffery 43555ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_FORCE_ABORT: 436d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 43755ab48b4SAndrew Jeffery break; 43855ab48b4SAndrew Jeffery 43955ab48b4SAndrew Jeffery default: 44055ab48b4SAndrew Jeffery ret = -EINVAL; 44155ab48b4SAndrew Jeffery break; 44255ab48b4SAndrew Jeffery } 44355ab48b4SAndrew Jeffery 444d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 44555ab48b4SAndrew Jeffery 44655ab48b4SAndrew Jeffery return ret; 44755ab48b4SAndrew Jeffery } 44855ab48b4SAndrew Jeffery 44955ab48b4SAndrew Jeffery static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp) 45055ab48b4SAndrew Jeffery { 451d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 45255ab48b4SAndrew Jeffery 453d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 454d4e7ac68SAndrew Jeffery kcs_bmc_disable_device(priv->client.dev, &priv->client); 45555ab48b4SAndrew Jeffery 45655ab48b4SAndrew Jeffery return 0; 45755ab48b4SAndrew Jeffery } 45855ab48b4SAndrew Jeffery 459d7096970SAndrew Jeffery static const struct file_operations kcs_bmc_ipmi_fops = { 46055ab48b4SAndrew Jeffery .owner = THIS_MODULE, 46155ab48b4SAndrew Jeffery .open = kcs_bmc_ipmi_open, 46255ab48b4SAndrew Jeffery .read = kcs_bmc_ipmi_read, 46355ab48b4SAndrew Jeffery .write = kcs_bmc_ipmi_write, 46455ab48b4SAndrew Jeffery .release = kcs_bmc_ipmi_release, 46555ab48b4SAndrew Jeffery .poll = kcs_bmc_ipmi_poll, 46655ab48b4SAndrew Jeffery .unlocked_ioctl = kcs_bmc_ipmi_ioctl, 46755ab48b4SAndrew Jeffery }; 46855ab48b4SAndrew Jeffery 469d4e7ac68SAndrew Jeffery static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock); 470d4e7ac68SAndrew Jeffery static LIST_HEAD(kcs_bmc_ipmi_instances); 471d4e7ac68SAndrew Jeffery 4727cafff99SAndrew Jeffery static int kcs_bmc_ipmi_add_device(struct kcs_bmc_device *kcs_bmc) 47355ab48b4SAndrew Jeffery { 474d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv; 475d7096970SAndrew Jeffery int rc; 47655ab48b4SAndrew Jeffery 477d4e7ac68SAndrew Jeffery priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL); 478d4e7ac68SAndrew Jeffery if (!priv) 479d7096970SAndrew Jeffery return -ENOMEM; 48055ab48b4SAndrew Jeffery 481d4e7ac68SAndrew Jeffery spin_lock_init(&priv->lock); 482d4e7ac68SAndrew Jeffery mutex_init(&priv->mutex); 483d7096970SAndrew Jeffery 484d4e7ac68SAndrew Jeffery init_waitqueue_head(&priv->queue); 485d4e7ac68SAndrew Jeffery 486d4e7ac68SAndrew Jeffery priv->client.dev = kcs_bmc; 487d4e7ac68SAndrew Jeffery priv->client.ops = &kcs_bmc_ipmi_client_ops; 488d4e7ac68SAndrew Jeffery priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 489d4e7ac68SAndrew Jeffery priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 490d4e7ac68SAndrew Jeffery priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 491d4e7ac68SAndrew Jeffery 492d4e7ac68SAndrew Jeffery priv->miscdev.minor = MISC_DYNAMIC_MINOR; 493d4e7ac68SAndrew Jeffery priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME, 494d4e7ac68SAndrew Jeffery kcs_bmc->channel); 495d4e7ac68SAndrew Jeffery if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name) 496d4e7ac68SAndrew Jeffery return -EINVAL; 497d4e7ac68SAndrew Jeffery 498d4e7ac68SAndrew Jeffery priv->miscdev.fops = &kcs_bmc_ipmi_fops; 499d4e7ac68SAndrew Jeffery 500d4e7ac68SAndrew Jeffery rc = misc_register(&priv->miscdev); 501d7096970SAndrew Jeffery if (rc) { 502d7096970SAndrew Jeffery dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc); 503d7096970SAndrew Jeffery return rc; 50455ab48b4SAndrew Jeffery } 505d7096970SAndrew Jeffery 506d4e7ac68SAndrew Jeffery spin_lock_irq(&kcs_bmc_ipmi_instances_lock); 507d4e7ac68SAndrew Jeffery list_add(&priv->entry, &kcs_bmc_ipmi_instances); 508d4e7ac68SAndrew Jeffery spin_unlock_irq(&kcs_bmc_ipmi_instances_lock); 509d4e7ac68SAndrew Jeffery 510d7096970SAndrew Jeffery dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel); 511d7096970SAndrew Jeffery 512d7096970SAndrew Jeffery return 0; 513d7096970SAndrew Jeffery } 514d7096970SAndrew Jeffery 5157cafff99SAndrew Jeffery static int kcs_bmc_ipmi_remove_device(struct kcs_bmc_device *kcs_bmc) 516d7096970SAndrew Jeffery { 517d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = NULL, *pos; 518d7096970SAndrew Jeffery 519d4e7ac68SAndrew Jeffery spin_lock_irq(&kcs_bmc_ipmi_instances_lock); 520d4e7ac68SAndrew Jeffery list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) { 521d4e7ac68SAndrew Jeffery if (pos->client.dev == kcs_bmc) { 522d4e7ac68SAndrew Jeffery priv = pos; 523d4e7ac68SAndrew Jeffery list_del(&pos->entry); 524d4e7ac68SAndrew Jeffery break; 525d4e7ac68SAndrew Jeffery } 526d4e7ac68SAndrew Jeffery } 527d4e7ac68SAndrew Jeffery spin_unlock_irq(&kcs_bmc_ipmi_instances_lock); 528d7096970SAndrew Jeffery 529d4e7ac68SAndrew Jeffery if (!priv) 530d4e7ac68SAndrew Jeffery return -ENODEV; 531d4e7ac68SAndrew Jeffery 532d4e7ac68SAndrew Jeffery misc_deregister(&priv->miscdev); 533d4e7ac68SAndrew Jeffery kcs_bmc_disable_device(priv->client.dev, &priv->client); 534d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv->kbuffer); 535d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv->data_out); 536d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv->data_in); 537d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv); 538d7096970SAndrew Jeffery 539d7096970SAndrew Jeffery return 0; 540d7096970SAndrew Jeffery } 5417cafff99SAndrew Jeffery 5427cafff99SAndrew Jeffery static const struct kcs_bmc_driver_ops kcs_bmc_ipmi_driver_ops = { 5437cafff99SAndrew Jeffery .add_device = kcs_bmc_ipmi_add_device, 5447cafff99SAndrew Jeffery .remove_device = kcs_bmc_ipmi_remove_device, 5457cafff99SAndrew Jeffery }; 5467cafff99SAndrew Jeffery 5477cafff99SAndrew Jeffery static struct kcs_bmc_driver kcs_bmc_ipmi_driver = { 5487cafff99SAndrew Jeffery .ops = &kcs_bmc_ipmi_driver_ops, 5497cafff99SAndrew Jeffery }; 5507cafff99SAndrew Jeffery 551*ec7174f6SXiu Jianfeng static int __init kcs_bmc_ipmi_init(void) 5527cafff99SAndrew Jeffery { 5537cafff99SAndrew Jeffery kcs_bmc_register_driver(&kcs_bmc_ipmi_driver); 5547cafff99SAndrew Jeffery 5557cafff99SAndrew Jeffery return 0; 5567cafff99SAndrew Jeffery } 5577cafff99SAndrew Jeffery module_init(kcs_bmc_ipmi_init); 5587cafff99SAndrew Jeffery 559*ec7174f6SXiu Jianfeng static void __exit kcs_bmc_ipmi_exit(void) 5607cafff99SAndrew Jeffery { 5617cafff99SAndrew Jeffery kcs_bmc_unregister_driver(&kcs_bmc_ipmi_driver); 5627cafff99SAndrew Jeffery } 5637cafff99SAndrew Jeffery module_exit(kcs_bmc_ipmi_exit); 56455ab48b4SAndrew Jeffery 56555ab48b4SAndrew Jeffery MODULE_LICENSE("GPL v2"); 56655ab48b4SAndrew Jeffery MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); 567d4e7ac68SAndrew Jeffery MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 56855ab48b4SAndrew Jeffery MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); 569