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> 11*d4e7ac68SAndrew Jeffery #include <linux/list.h> 12*d4e7ac68SAndrew Jeffery #include <linux/miscdevice.h> 1355ab48b4SAndrew Jeffery #include <linux/module.h> 14*d4e7ac68SAndrew 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 20*d4e7ac68SAndrew Jeffery #include "kcs_bmc_client.h" 21*d4e7ac68SAndrew Jeffery 22*d4e7ac68SAndrew Jeffery /* Different phases of the KCS BMC module. 23*d4e7ac68SAndrew Jeffery * KCS_PHASE_IDLE: 24*d4e7ac68SAndrew Jeffery * BMC should not be expecting nor sending any data. 25*d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_START: 26*d4e7ac68SAndrew Jeffery * BMC is receiving a WRITE_START command from system software. 27*d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_DATA: 28*d4e7ac68SAndrew Jeffery * BMC is receiving a data byte from system software. 29*d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_END_CMD: 30*d4e7ac68SAndrew Jeffery * BMC is waiting a last data byte from system software. 31*d4e7ac68SAndrew Jeffery * KCS_PHASE_WRITE_DONE: 32*d4e7ac68SAndrew Jeffery * BMC has received the whole request from system software. 33*d4e7ac68SAndrew Jeffery * KCS_PHASE_WAIT_READ: 34*d4e7ac68SAndrew Jeffery * BMC is waiting the response from the upper IPMI service. 35*d4e7ac68SAndrew Jeffery * KCS_PHASE_READ: 36*d4e7ac68SAndrew Jeffery * BMC is transferring the response to system software. 37*d4e7ac68SAndrew Jeffery * KCS_PHASE_ABORT_ERROR1: 38*d4e7ac68SAndrew Jeffery * BMC is waiting error status request from system software. 39*d4e7ac68SAndrew Jeffery * KCS_PHASE_ABORT_ERROR2: 40*d4e7ac68SAndrew Jeffery * BMC is waiting for idle status afer error from system software. 41*d4e7ac68SAndrew Jeffery * KCS_PHASE_ERROR: 42*d4e7ac68SAndrew Jeffery * BMC has detected a protocol violation at the interface level. 43*d4e7ac68SAndrew Jeffery */ 44*d4e7ac68SAndrew Jeffery enum kcs_ipmi_phases { 45*d4e7ac68SAndrew Jeffery KCS_PHASE_IDLE, 46*d4e7ac68SAndrew Jeffery 47*d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_START, 48*d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_DATA, 49*d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_END_CMD, 50*d4e7ac68SAndrew Jeffery KCS_PHASE_WRITE_DONE, 51*d4e7ac68SAndrew Jeffery 52*d4e7ac68SAndrew Jeffery KCS_PHASE_WAIT_READ, 53*d4e7ac68SAndrew Jeffery KCS_PHASE_READ, 54*d4e7ac68SAndrew Jeffery 55*d4e7ac68SAndrew Jeffery KCS_PHASE_ABORT_ERROR1, 56*d4e7ac68SAndrew Jeffery KCS_PHASE_ABORT_ERROR2, 57*d4e7ac68SAndrew Jeffery KCS_PHASE_ERROR 58*d4e7ac68SAndrew Jeffery }; 59*d4e7ac68SAndrew Jeffery 60*d4e7ac68SAndrew Jeffery /* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */ 61*d4e7ac68SAndrew Jeffery enum kcs_ipmi_errors { 62*d4e7ac68SAndrew Jeffery KCS_NO_ERROR = 0x00, 63*d4e7ac68SAndrew Jeffery KCS_ABORTED_BY_COMMAND = 0x01, 64*d4e7ac68SAndrew Jeffery KCS_ILLEGAL_CONTROL_CODE = 0x02, 65*d4e7ac68SAndrew Jeffery KCS_LENGTH_ERROR = 0x06, 66*d4e7ac68SAndrew Jeffery KCS_UNSPECIFIED_ERROR = 0xFF 67*d4e7ac68SAndrew Jeffery }; 68*d4e7ac68SAndrew Jeffery 69*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi { 70*d4e7ac68SAndrew Jeffery struct list_head entry; 71*d4e7ac68SAndrew Jeffery 72*d4e7ac68SAndrew Jeffery struct kcs_bmc_client client; 73*d4e7ac68SAndrew Jeffery 74*d4e7ac68SAndrew Jeffery spinlock_t lock; 75*d4e7ac68SAndrew Jeffery 76*d4e7ac68SAndrew Jeffery enum kcs_ipmi_phases phase; 77*d4e7ac68SAndrew Jeffery enum kcs_ipmi_errors error; 78*d4e7ac68SAndrew Jeffery 79*d4e7ac68SAndrew Jeffery wait_queue_head_t queue; 80*d4e7ac68SAndrew Jeffery bool data_in_avail; 81*d4e7ac68SAndrew Jeffery int data_in_idx; 82*d4e7ac68SAndrew Jeffery u8 *data_in; 83*d4e7ac68SAndrew Jeffery 84*d4e7ac68SAndrew Jeffery int data_out_idx; 85*d4e7ac68SAndrew Jeffery int data_out_len; 86*d4e7ac68SAndrew Jeffery u8 *data_out; 87*d4e7ac68SAndrew Jeffery 88*d4e7ac68SAndrew Jeffery struct mutex mutex; 89*d4e7ac68SAndrew Jeffery u8 *kbuffer; 90*d4e7ac68SAndrew Jeffery 91*d4e7ac68SAndrew Jeffery struct miscdevice miscdev; 92*d4e7ac68SAndrew 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 122*d4e7ac68SAndrew Jeffery static inline void set_state(struct kcs_bmc_ipmi *priv, u8 state) 12355ab48b4SAndrew Jeffery { 124*d4e7ac68SAndrew Jeffery kcs_bmc_update_status(priv->client.dev, KCS_STATUS_STATE_MASK, KCS_STATUS_STATE(state)); 12555ab48b4SAndrew Jeffery } 12655ab48b4SAndrew Jeffery 127*d4e7ac68SAndrew Jeffery static void kcs_bmc_ipmi_force_abort(struct kcs_bmc_ipmi *priv) 12855ab48b4SAndrew Jeffery { 129*d4e7ac68SAndrew Jeffery set_state(priv, ERROR_STATE); 130*d4e7ac68SAndrew Jeffery kcs_bmc_read_data(priv->client.dev); 131*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA); 13255ab48b4SAndrew Jeffery 133*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_ERROR; 134*d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 135*d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 13655ab48b4SAndrew Jeffery } 13755ab48b4SAndrew Jeffery 138*d4e7ac68SAndrew Jeffery static void kcs_bmc_ipmi_handle_data(struct kcs_bmc_ipmi *priv) 13955ab48b4SAndrew Jeffery { 140*d4e7ac68SAndrew Jeffery struct kcs_bmc_device *dev; 14155ab48b4SAndrew Jeffery u8 data; 14255ab48b4SAndrew Jeffery 143*d4e7ac68SAndrew Jeffery dev = priv->client.dev; 144*d4e7ac68SAndrew Jeffery 145*d4e7ac68SAndrew Jeffery switch (priv->phase) { 14655ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_START: 147*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_DATA; 14855ab48b4SAndrew Jeffery fallthrough; 14955ab48b4SAndrew Jeffery 15055ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_DATA: 151*d4e7ac68SAndrew Jeffery if (priv->data_in_idx < KCS_MSG_BUFSIZ) { 152*d4e7ac68SAndrew Jeffery set_state(priv, WRITE_STATE); 153*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 154*d4e7ac68SAndrew Jeffery priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev); 15555ab48b4SAndrew Jeffery } else { 156*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 157*d4e7ac68SAndrew Jeffery priv->error = KCS_LENGTH_ERROR; 15855ab48b4SAndrew Jeffery } 15955ab48b4SAndrew Jeffery break; 16055ab48b4SAndrew Jeffery 16155ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_END_CMD: 162*d4e7ac68SAndrew Jeffery if (priv->data_in_idx < KCS_MSG_BUFSIZ) { 163*d4e7ac68SAndrew Jeffery set_state(priv, READ_STATE); 164*d4e7ac68SAndrew Jeffery priv->data_in[priv->data_in_idx++] = kcs_bmc_read_data(dev); 165*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_DONE; 166*d4e7ac68SAndrew Jeffery priv->data_in_avail = true; 167*d4e7ac68SAndrew Jeffery wake_up_interruptible(&priv->queue); 16855ab48b4SAndrew Jeffery } else { 169*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 170*d4e7ac68SAndrew Jeffery priv->error = KCS_LENGTH_ERROR; 17155ab48b4SAndrew Jeffery } 17255ab48b4SAndrew Jeffery break; 17355ab48b4SAndrew Jeffery 17455ab48b4SAndrew Jeffery case KCS_PHASE_READ: 175*d4e7ac68SAndrew Jeffery if (priv->data_out_idx == priv->data_out_len) 176*d4e7ac68SAndrew Jeffery set_state(priv, IDLE_STATE); 17755ab48b4SAndrew Jeffery 178*d4e7ac68SAndrew Jeffery data = kcs_bmc_read_data(dev); 17955ab48b4SAndrew Jeffery if (data != KCS_CMD_READ_BYTE) { 180*d4e7ac68SAndrew Jeffery set_state(priv, ERROR_STATE); 181*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 18255ab48b4SAndrew Jeffery break; 18355ab48b4SAndrew Jeffery } 18455ab48b4SAndrew Jeffery 185*d4e7ac68SAndrew Jeffery if (priv->data_out_idx == priv->data_out_len) { 186*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 187*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_IDLE; 18855ab48b4SAndrew Jeffery break; 18955ab48b4SAndrew Jeffery } 19055ab48b4SAndrew Jeffery 191*d4e7ac68SAndrew 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: 195*d4e7ac68SAndrew Jeffery set_state(priv, READ_STATE); 196*d4e7ac68SAndrew Jeffery kcs_bmc_read_data(dev); 197*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, priv->error); 198*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_ABORT_ERROR2; 19955ab48b4SAndrew Jeffery break; 20055ab48b4SAndrew Jeffery 20155ab48b4SAndrew Jeffery case KCS_PHASE_ABORT_ERROR2: 202*d4e7ac68SAndrew Jeffery set_state(priv, IDLE_STATE); 203*d4e7ac68SAndrew Jeffery kcs_bmc_read_data(dev); 204*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(dev, KCS_ZERO_DATA); 205*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_IDLE; 20655ab48b4SAndrew Jeffery break; 20755ab48b4SAndrew Jeffery 20855ab48b4SAndrew Jeffery default: 209*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 21055ab48b4SAndrew Jeffery break; 21155ab48b4SAndrew Jeffery } 21255ab48b4SAndrew Jeffery } 21355ab48b4SAndrew Jeffery 214*d4e7ac68SAndrew Jeffery static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc_ipmi *priv) 21555ab48b4SAndrew Jeffery { 21655ab48b4SAndrew Jeffery u8 cmd; 21755ab48b4SAndrew Jeffery 218*d4e7ac68SAndrew Jeffery set_state(priv, WRITE_STATE); 219*d4e7ac68SAndrew Jeffery kcs_bmc_write_data(priv->client.dev, KCS_ZERO_DATA); 22055ab48b4SAndrew Jeffery 221*d4e7ac68SAndrew Jeffery cmd = kcs_bmc_read_data(priv->client.dev); 22255ab48b4SAndrew Jeffery switch (cmd) { 22355ab48b4SAndrew Jeffery case KCS_CMD_WRITE_START: 224*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_START; 225*d4e7ac68SAndrew Jeffery priv->error = KCS_NO_ERROR; 226*d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 227*d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 22855ab48b4SAndrew Jeffery break; 22955ab48b4SAndrew Jeffery 23055ab48b4SAndrew Jeffery case KCS_CMD_WRITE_END: 231*d4e7ac68SAndrew Jeffery if (priv->phase != KCS_PHASE_WRITE_DATA) { 232*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 23355ab48b4SAndrew Jeffery break; 23455ab48b4SAndrew Jeffery } 23555ab48b4SAndrew Jeffery 236*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WRITE_END_CMD; 23755ab48b4SAndrew Jeffery break; 23855ab48b4SAndrew Jeffery 23955ab48b4SAndrew Jeffery case KCS_CMD_GET_STATUS_ABORT: 240*d4e7ac68SAndrew Jeffery if (priv->error == KCS_NO_ERROR) 241*d4e7ac68SAndrew Jeffery priv->error = KCS_ABORTED_BY_COMMAND; 24255ab48b4SAndrew Jeffery 243*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_ABORT_ERROR1; 244*d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 245*d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 24655ab48b4SAndrew Jeffery break; 24755ab48b4SAndrew Jeffery 24855ab48b4SAndrew Jeffery default: 249*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 250*d4e7ac68SAndrew Jeffery priv->error = KCS_ILLEGAL_CONTROL_CODE; 25155ab48b4SAndrew Jeffery break; 25255ab48b4SAndrew Jeffery } 25355ab48b4SAndrew Jeffery } 25455ab48b4SAndrew Jeffery 255*d4e7ac68SAndrew Jeffery static inline struct kcs_bmc_ipmi *client_to_kcs_bmc_ipmi(struct kcs_bmc_client *client) 25655ab48b4SAndrew Jeffery { 257*d4e7ac68SAndrew 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 { 262*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv; 26355ab48b4SAndrew Jeffery u8 status; 264faae6e39SAndrew Jeffery int ret; 265faae6e39SAndrew Jeffery 266*d4e7ac68SAndrew Jeffery priv = client_to_kcs_bmc_ipmi(client); 267*d4e7ac68SAndrew Jeffery if (!priv) 268*d4e7ac68SAndrew Jeffery return IRQ_NONE; 26955ab48b4SAndrew Jeffery 270*d4e7ac68SAndrew Jeffery spin_lock(&priv->lock); 27155ab48b4SAndrew Jeffery 272*d4e7ac68SAndrew Jeffery status = kcs_bmc_read_status(client->dev); 27355ab48b4SAndrew Jeffery if (status & KCS_STATUS_IBF) { 274*d4e7ac68SAndrew Jeffery if (status & KCS_STATUS_CMD_DAT) 275*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_handle_cmd(priv); 27655ab48b4SAndrew Jeffery else 277*d4e7ac68SAndrew 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 284*d4e7ac68SAndrew 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 293*d4e7ac68SAndrew Jeffery static inline struct kcs_bmc_ipmi *to_kcs_bmc(struct file *filp) 29455ab48b4SAndrew Jeffery { 295*d4e7ac68SAndrew 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 { 300*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 30155ab48b4SAndrew Jeffery 302*d4e7ac68SAndrew 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 { 307*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 30855ab48b4SAndrew Jeffery __poll_t mask = 0; 30955ab48b4SAndrew Jeffery 310*d4e7ac68SAndrew Jeffery poll_wait(filp, &priv->queue, wait); 31155ab48b4SAndrew Jeffery 312*d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 313*d4e7ac68SAndrew Jeffery if (priv->data_in_avail) 31455ab48b4SAndrew Jeffery mask |= EPOLLIN; 315*d4e7ac68SAndrew 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 { 323*d4e7ac68SAndrew 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)) 329*d4e7ac68SAndrew Jeffery wait_event_interruptible(priv->queue, 330*d4e7ac68SAndrew Jeffery priv->data_in_avail); 33155ab48b4SAndrew Jeffery 332*d4e7ac68SAndrew Jeffery mutex_lock(&priv->mutex); 33355ab48b4SAndrew Jeffery 334*d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 335*d4e7ac68SAndrew Jeffery data_avail = priv->data_in_avail; 33655ab48b4SAndrew Jeffery if (data_avail) { 337*d4e7ac68SAndrew Jeffery data_len = priv->data_in_idx; 338*d4e7ac68SAndrew Jeffery memcpy(priv->kbuffer, priv->data_in, data_len); 33955ab48b4SAndrew Jeffery } 340*d4e7ac68SAndrew 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", 349*d4e7ac68SAndrew Jeffery priv->client.dev->channel, data_len); 35055ab48b4SAndrew Jeffery 351*d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 352*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 353*d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 35455ab48b4SAndrew Jeffery 35555ab48b4SAndrew Jeffery ret = -EOVERFLOW; 35655ab48b4SAndrew Jeffery goto out_unlock; 35755ab48b4SAndrew Jeffery } 35855ab48b4SAndrew Jeffery 359*d4e7ac68SAndrew 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 366*d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 367*d4e7ac68SAndrew Jeffery if (priv->phase == KCS_PHASE_WRITE_DONE) { 368*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_WAIT_READ; 369*d4e7ac68SAndrew Jeffery priv->data_in_avail = false; 370*d4e7ac68SAndrew Jeffery priv->data_in_idx = 0; 37155ab48b4SAndrew Jeffery } else { 37255ab48b4SAndrew Jeffery ret = -EAGAIN; 37355ab48b4SAndrew Jeffery } 374*d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 37555ab48b4SAndrew Jeffery 37655ab48b4SAndrew Jeffery out_unlock: 377*d4e7ac68SAndrew 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 { 385*d4e7ac68SAndrew 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 392*d4e7ac68SAndrew Jeffery mutex_lock(&priv->mutex); 39355ab48b4SAndrew Jeffery 394*d4e7ac68SAndrew Jeffery if (copy_from_user(priv->kbuffer, buf, count)) { 39555ab48b4SAndrew Jeffery ret = -EFAULT; 39655ab48b4SAndrew Jeffery goto out_unlock; 39755ab48b4SAndrew Jeffery } 39855ab48b4SAndrew Jeffery 399*d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 400*d4e7ac68SAndrew Jeffery if (priv->phase == KCS_PHASE_WAIT_READ) { 401*d4e7ac68SAndrew Jeffery priv->phase = KCS_PHASE_READ; 402*d4e7ac68SAndrew Jeffery priv->data_out_idx = 1; 403*d4e7ac68SAndrew Jeffery priv->data_out_len = count; 404*d4e7ac68SAndrew Jeffery memcpy(priv->data_out, priv->kbuffer, count); 405*d4e7ac68SAndrew 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 } 410*d4e7ac68SAndrew Jeffery spin_unlock_irq(&priv->lock); 41155ab48b4SAndrew Jeffery 41255ab48b4SAndrew Jeffery out_unlock: 413*d4e7ac68SAndrew 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 { 421*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 42255ab48b4SAndrew Jeffery long ret = 0; 42355ab48b4SAndrew Jeffery 424*d4e7ac68SAndrew Jeffery spin_lock_irq(&priv->lock); 42555ab48b4SAndrew Jeffery 42655ab48b4SAndrew Jeffery switch (cmd) { 42755ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_SET_SMS_ATN: 428*d4e7ac68SAndrew 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: 432*d4e7ac68SAndrew 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: 436*d4e7ac68SAndrew 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 444*d4e7ac68SAndrew 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 { 451*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = to_kcs_bmc(filp); 45255ab48b4SAndrew Jeffery 453*d4e7ac68SAndrew Jeffery kcs_bmc_ipmi_force_abort(priv); 454*d4e7ac68SAndrew 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 469*d4e7ac68SAndrew Jeffery static DEFINE_SPINLOCK(kcs_bmc_ipmi_instances_lock); 470*d4e7ac68SAndrew Jeffery static LIST_HEAD(kcs_bmc_ipmi_instances); 471*d4e7ac68SAndrew Jeffery 472*d4e7ac68SAndrew Jeffery int kcs_bmc_ipmi_add_device(struct kcs_bmc_device *kcs_bmc); 473*d4e7ac68SAndrew Jeffery int kcs_bmc_ipmi_add_device(struct kcs_bmc_device *kcs_bmc) 47455ab48b4SAndrew Jeffery { 475*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv; 476d7096970SAndrew Jeffery int rc; 47755ab48b4SAndrew Jeffery 478*d4e7ac68SAndrew Jeffery priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL); 479*d4e7ac68SAndrew Jeffery if (!priv) 480d7096970SAndrew Jeffery return -ENOMEM; 48155ab48b4SAndrew Jeffery 482*d4e7ac68SAndrew Jeffery spin_lock_init(&priv->lock); 483*d4e7ac68SAndrew Jeffery mutex_init(&priv->mutex); 484d7096970SAndrew Jeffery 485*d4e7ac68SAndrew Jeffery init_waitqueue_head(&priv->queue); 486*d4e7ac68SAndrew Jeffery 487*d4e7ac68SAndrew Jeffery priv->client.dev = kcs_bmc; 488*d4e7ac68SAndrew Jeffery priv->client.ops = &kcs_bmc_ipmi_client_ops; 489*d4e7ac68SAndrew Jeffery priv->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 490*d4e7ac68SAndrew Jeffery priv->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 491*d4e7ac68SAndrew Jeffery priv->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 492*d4e7ac68SAndrew Jeffery 493*d4e7ac68SAndrew Jeffery priv->miscdev.minor = MISC_DYNAMIC_MINOR; 494*d4e7ac68SAndrew Jeffery priv->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", DEVICE_NAME, 495*d4e7ac68SAndrew Jeffery kcs_bmc->channel); 496*d4e7ac68SAndrew Jeffery if (!priv->data_in || !priv->data_out || !priv->kbuffer || !priv->miscdev.name) 497*d4e7ac68SAndrew Jeffery return -EINVAL; 498*d4e7ac68SAndrew Jeffery 499*d4e7ac68SAndrew Jeffery priv->miscdev.fops = &kcs_bmc_ipmi_fops; 500*d4e7ac68SAndrew Jeffery 501*d4e7ac68SAndrew Jeffery rc = misc_register(&priv->miscdev); 502d7096970SAndrew Jeffery if (rc) { 503d7096970SAndrew Jeffery dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc); 504d7096970SAndrew Jeffery return rc; 50555ab48b4SAndrew Jeffery } 506d7096970SAndrew Jeffery 507*d4e7ac68SAndrew Jeffery spin_lock_irq(&kcs_bmc_ipmi_instances_lock); 508*d4e7ac68SAndrew Jeffery list_add(&priv->entry, &kcs_bmc_ipmi_instances); 509*d4e7ac68SAndrew Jeffery spin_unlock_irq(&kcs_bmc_ipmi_instances_lock); 510*d4e7ac68SAndrew Jeffery 511d7096970SAndrew Jeffery dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel); 512d7096970SAndrew Jeffery 513d7096970SAndrew Jeffery return 0; 514d7096970SAndrew Jeffery } 515d7096970SAndrew Jeffery EXPORT_SYMBOL(kcs_bmc_ipmi_add_device); 516d7096970SAndrew Jeffery 517*d4e7ac68SAndrew Jeffery int kcs_bmc_ipmi_remove_device(struct kcs_bmc_device *kcs_bmc); 518*d4e7ac68SAndrew Jeffery int kcs_bmc_ipmi_remove_device(struct kcs_bmc_device *kcs_bmc) 519d7096970SAndrew Jeffery { 520*d4e7ac68SAndrew Jeffery struct kcs_bmc_ipmi *priv = NULL, *pos; 521d7096970SAndrew Jeffery 522*d4e7ac68SAndrew Jeffery spin_lock_irq(&kcs_bmc_ipmi_instances_lock); 523*d4e7ac68SAndrew Jeffery list_for_each_entry(pos, &kcs_bmc_ipmi_instances, entry) { 524*d4e7ac68SAndrew Jeffery if (pos->client.dev == kcs_bmc) { 525*d4e7ac68SAndrew Jeffery priv = pos; 526*d4e7ac68SAndrew Jeffery list_del(&pos->entry); 527*d4e7ac68SAndrew Jeffery break; 528*d4e7ac68SAndrew Jeffery } 529*d4e7ac68SAndrew Jeffery } 530*d4e7ac68SAndrew Jeffery spin_unlock_irq(&kcs_bmc_ipmi_instances_lock); 531d7096970SAndrew Jeffery 532*d4e7ac68SAndrew Jeffery if (!priv) 533*d4e7ac68SAndrew Jeffery return -ENODEV; 534*d4e7ac68SAndrew Jeffery 535*d4e7ac68SAndrew Jeffery misc_deregister(&priv->miscdev); 536*d4e7ac68SAndrew Jeffery kcs_bmc_disable_device(priv->client.dev, &priv->client); 537*d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv->kbuffer); 538*d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv->data_out); 539*d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv->data_in); 540*d4e7ac68SAndrew Jeffery devm_kfree(kcs_bmc->dev, priv); 541d7096970SAndrew Jeffery 542d7096970SAndrew Jeffery return 0; 543d7096970SAndrew Jeffery } 544d7096970SAndrew Jeffery EXPORT_SYMBOL(kcs_bmc_ipmi_remove_device); 54555ab48b4SAndrew Jeffery 54655ab48b4SAndrew Jeffery MODULE_LICENSE("GPL v2"); 54755ab48b4SAndrew Jeffery MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); 548*d4e7ac68SAndrew Jeffery MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 54955ab48b4SAndrew Jeffery MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); 550