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> 1155ab48b4SAndrew Jeffery #include <linux/module.h> 1255ab48b4SAndrew Jeffery #include <linux/platform_device.h> 1355ab48b4SAndrew Jeffery #include <linux/poll.h> 1455ab48b4SAndrew Jeffery #include <linux/sched.h> 1555ab48b4SAndrew Jeffery #include <linux/slab.h> 1655ab48b4SAndrew Jeffery 1755ab48b4SAndrew Jeffery #include "kcs_bmc.h" 1855ab48b4SAndrew Jeffery 1955ab48b4SAndrew Jeffery #define DEVICE_NAME "ipmi-kcs" 2055ab48b4SAndrew Jeffery 2155ab48b4SAndrew Jeffery #define KCS_MSG_BUFSIZ 1000 2255ab48b4SAndrew Jeffery 2355ab48b4SAndrew Jeffery #define KCS_ZERO_DATA 0 2455ab48b4SAndrew Jeffery 2555ab48b4SAndrew Jeffery 2655ab48b4SAndrew Jeffery /* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */ 2755ab48b4SAndrew Jeffery #define KCS_STATUS_STATE(state) (state << 6) 2855ab48b4SAndrew Jeffery #define KCS_STATUS_STATE_MASK GENMASK(7, 6) 2955ab48b4SAndrew Jeffery #define KCS_STATUS_CMD_DAT BIT(3) 3055ab48b4SAndrew Jeffery #define KCS_STATUS_SMS_ATN BIT(2) 3155ab48b4SAndrew Jeffery #define KCS_STATUS_IBF BIT(1) 3255ab48b4SAndrew Jeffery #define KCS_STATUS_OBF BIT(0) 3355ab48b4SAndrew Jeffery 3455ab48b4SAndrew Jeffery /* IPMI 2.0 - Table 9-2, KCS Interface State Bits */ 3555ab48b4SAndrew Jeffery enum kcs_states { 3655ab48b4SAndrew Jeffery IDLE_STATE = 0, 3755ab48b4SAndrew Jeffery READ_STATE = 1, 3855ab48b4SAndrew Jeffery WRITE_STATE = 2, 3955ab48b4SAndrew Jeffery ERROR_STATE = 3, 4055ab48b4SAndrew Jeffery }; 4155ab48b4SAndrew Jeffery 4255ab48b4SAndrew Jeffery /* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */ 4355ab48b4SAndrew Jeffery #define KCS_CMD_GET_STATUS_ABORT 0x60 4455ab48b4SAndrew Jeffery #define KCS_CMD_WRITE_START 0x61 4555ab48b4SAndrew Jeffery #define KCS_CMD_WRITE_END 0x62 4655ab48b4SAndrew Jeffery #define KCS_CMD_READ_BYTE 0x68 4755ab48b4SAndrew Jeffery 4855ab48b4SAndrew Jeffery static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state) 4955ab48b4SAndrew Jeffery { 5055ab48b4SAndrew Jeffery kcs_bmc_update_status(kcs_bmc, KCS_STATUS_STATE_MASK, 5155ab48b4SAndrew Jeffery KCS_STATUS_STATE(state)); 5255ab48b4SAndrew Jeffery } 5355ab48b4SAndrew Jeffery 5455ab48b4SAndrew Jeffery static void kcs_bmc_ipmi_force_abort(struct kcs_bmc *kcs_bmc) 5555ab48b4SAndrew Jeffery { 5655ab48b4SAndrew Jeffery set_state(kcs_bmc, ERROR_STATE); 5755ab48b4SAndrew Jeffery kcs_bmc_read_data(kcs_bmc); 5855ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA); 5955ab48b4SAndrew Jeffery 6055ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_ERROR; 6155ab48b4SAndrew Jeffery kcs_bmc->data_in_avail = false; 6255ab48b4SAndrew Jeffery kcs_bmc->data_in_idx = 0; 6355ab48b4SAndrew Jeffery } 6455ab48b4SAndrew Jeffery 6555ab48b4SAndrew Jeffery static void kcs_bmc_ipmi_handle_data(struct kcs_bmc *kcs_bmc) 6655ab48b4SAndrew Jeffery { 6755ab48b4SAndrew Jeffery u8 data; 6855ab48b4SAndrew Jeffery 6955ab48b4SAndrew Jeffery switch (kcs_bmc->phase) { 7055ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_START: 7155ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_WRITE_DATA; 7255ab48b4SAndrew Jeffery fallthrough; 7355ab48b4SAndrew Jeffery 7455ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_DATA: 7555ab48b4SAndrew Jeffery if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { 7655ab48b4SAndrew Jeffery set_state(kcs_bmc, WRITE_STATE); 7755ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA); 7855ab48b4SAndrew Jeffery kcs_bmc->data_in[kcs_bmc->data_in_idx++] = 7955ab48b4SAndrew Jeffery kcs_bmc_read_data(kcs_bmc); 8055ab48b4SAndrew Jeffery } else { 8155ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 8255ab48b4SAndrew Jeffery kcs_bmc->error = KCS_LENGTH_ERROR; 8355ab48b4SAndrew Jeffery } 8455ab48b4SAndrew Jeffery break; 8555ab48b4SAndrew Jeffery 8655ab48b4SAndrew Jeffery case KCS_PHASE_WRITE_END_CMD: 8755ab48b4SAndrew Jeffery if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { 8855ab48b4SAndrew Jeffery set_state(kcs_bmc, READ_STATE); 8955ab48b4SAndrew Jeffery kcs_bmc->data_in[kcs_bmc->data_in_idx++] = 9055ab48b4SAndrew Jeffery kcs_bmc_read_data(kcs_bmc); 9155ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_WRITE_DONE; 9255ab48b4SAndrew Jeffery kcs_bmc->data_in_avail = true; 9355ab48b4SAndrew Jeffery wake_up_interruptible(&kcs_bmc->queue); 9455ab48b4SAndrew Jeffery } else { 9555ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 9655ab48b4SAndrew Jeffery kcs_bmc->error = KCS_LENGTH_ERROR; 9755ab48b4SAndrew Jeffery } 9855ab48b4SAndrew Jeffery break; 9955ab48b4SAndrew Jeffery 10055ab48b4SAndrew Jeffery case KCS_PHASE_READ: 10155ab48b4SAndrew Jeffery if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) 10255ab48b4SAndrew Jeffery set_state(kcs_bmc, IDLE_STATE); 10355ab48b4SAndrew Jeffery 10455ab48b4SAndrew Jeffery data = kcs_bmc_read_data(kcs_bmc); 10555ab48b4SAndrew Jeffery if (data != KCS_CMD_READ_BYTE) { 10655ab48b4SAndrew Jeffery set_state(kcs_bmc, ERROR_STATE); 10755ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA); 10855ab48b4SAndrew Jeffery break; 10955ab48b4SAndrew Jeffery } 11055ab48b4SAndrew Jeffery 11155ab48b4SAndrew Jeffery if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) { 11255ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA); 11355ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_IDLE; 11455ab48b4SAndrew Jeffery break; 11555ab48b4SAndrew Jeffery } 11655ab48b4SAndrew Jeffery 11755ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, 11855ab48b4SAndrew Jeffery kcs_bmc->data_out[kcs_bmc->data_out_idx++]); 11955ab48b4SAndrew Jeffery break; 12055ab48b4SAndrew Jeffery 12155ab48b4SAndrew Jeffery case KCS_PHASE_ABORT_ERROR1: 12255ab48b4SAndrew Jeffery set_state(kcs_bmc, READ_STATE); 12355ab48b4SAndrew Jeffery kcs_bmc_read_data(kcs_bmc); 12455ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, kcs_bmc->error); 12555ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2; 12655ab48b4SAndrew Jeffery break; 12755ab48b4SAndrew Jeffery 12855ab48b4SAndrew Jeffery case KCS_PHASE_ABORT_ERROR2: 12955ab48b4SAndrew Jeffery set_state(kcs_bmc, IDLE_STATE); 13055ab48b4SAndrew Jeffery kcs_bmc_read_data(kcs_bmc); 13155ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA); 13255ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_IDLE; 13355ab48b4SAndrew Jeffery break; 13455ab48b4SAndrew Jeffery 13555ab48b4SAndrew Jeffery default: 13655ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 13755ab48b4SAndrew Jeffery break; 13855ab48b4SAndrew Jeffery } 13955ab48b4SAndrew Jeffery } 14055ab48b4SAndrew Jeffery 14155ab48b4SAndrew Jeffery static void kcs_bmc_ipmi_handle_cmd(struct kcs_bmc *kcs_bmc) 14255ab48b4SAndrew Jeffery { 14355ab48b4SAndrew Jeffery u8 cmd; 14455ab48b4SAndrew Jeffery 14555ab48b4SAndrew Jeffery set_state(kcs_bmc, WRITE_STATE); 14655ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, KCS_ZERO_DATA); 14755ab48b4SAndrew Jeffery 14855ab48b4SAndrew Jeffery cmd = kcs_bmc_read_data(kcs_bmc); 14955ab48b4SAndrew Jeffery switch (cmd) { 15055ab48b4SAndrew Jeffery case KCS_CMD_WRITE_START: 15155ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_WRITE_START; 15255ab48b4SAndrew Jeffery kcs_bmc->error = KCS_NO_ERROR; 15355ab48b4SAndrew Jeffery kcs_bmc->data_in_avail = false; 15455ab48b4SAndrew Jeffery kcs_bmc->data_in_idx = 0; 15555ab48b4SAndrew Jeffery break; 15655ab48b4SAndrew Jeffery 15755ab48b4SAndrew Jeffery case KCS_CMD_WRITE_END: 15855ab48b4SAndrew Jeffery if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) { 15955ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 16055ab48b4SAndrew Jeffery break; 16155ab48b4SAndrew Jeffery } 16255ab48b4SAndrew Jeffery 16355ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD; 16455ab48b4SAndrew Jeffery break; 16555ab48b4SAndrew Jeffery 16655ab48b4SAndrew Jeffery case KCS_CMD_GET_STATUS_ABORT: 16755ab48b4SAndrew Jeffery if (kcs_bmc->error == KCS_NO_ERROR) 16855ab48b4SAndrew Jeffery kcs_bmc->error = KCS_ABORTED_BY_COMMAND; 16955ab48b4SAndrew Jeffery 17055ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1; 17155ab48b4SAndrew Jeffery kcs_bmc->data_in_avail = false; 17255ab48b4SAndrew Jeffery kcs_bmc->data_in_idx = 0; 17355ab48b4SAndrew Jeffery break; 17455ab48b4SAndrew Jeffery 17555ab48b4SAndrew Jeffery default: 17655ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 17755ab48b4SAndrew Jeffery kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE; 17855ab48b4SAndrew Jeffery break; 17955ab48b4SAndrew Jeffery } 18055ab48b4SAndrew Jeffery } 18155ab48b4SAndrew Jeffery 18255ab48b4SAndrew Jeffery int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc); 18355ab48b4SAndrew Jeffery int kcs_bmc_ipmi_event(struct kcs_bmc *kcs_bmc) 18455ab48b4SAndrew Jeffery { 18555ab48b4SAndrew Jeffery unsigned long flags; 18655ab48b4SAndrew Jeffery int ret = -ENODATA; 18755ab48b4SAndrew Jeffery u8 status; 18855ab48b4SAndrew Jeffery 18955ab48b4SAndrew Jeffery spin_lock_irqsave(&kcs_bmc->lock, flags); 19055ab48b4SAndrew Jeffery 19155ab48b4SAndrew Jeffery status = kcs_bmc_read_status(kcs_bmc); 19255ab48b4SAndrew Jeffery if (status & KCS_STATUS_IBF) { 19355ab48b4SAndrew Jeffery if (!kcs_bmc->running) 19455ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 19555ab48b4SAndrew Jeffery else if (status & KCS_STATUS_CMD_DAT) 19655ab48b4SAndrew Jeffery kcs_bmc_ipmi_handle_cmd(kcs_bmc); 19755ab48b4SAndrew Jeffery else 19855ab48b4SAndrew Jeffery kcs_bmc_ipmi_handle_data(kcs_bmc); 19955ab48b4SAndrew Jeffery 20055ab48b4SAndrew Jeffery ret = 0; 20155ab48b4SAndrew Jeffery } 20255ab48b4SAndrew Jeffery 20355ab48b4SAndrew Jeffery spin_unlock_irqrestore(&kcs_bmc->lock, flags); 20455ab48b4SAndrew Jeffery 20555ab48b4SAndrew Jeffery return ret; 20655ab48b4SAndrew Jeffery } 20755ab48b4SAndrew Jeffery EXPORT_SYMBOL(kcs_bmc_ipmi_event); 20855ab48b4SAndrew Jeffery 20955ab48b4SAndrew Jeffery static inline struct kcs_bmc *to_kcs_bmc(struct file *filp) 21055ab48b4SAndrew Jeffery { 21155ab48b4SAndrew Jeffery return container_of(filp->private_data, struct kcs_bmc, miscdev); 21255ab48b4SAndrew Jeffery } 21355ab48b4SAndrew Jeffery 21455ab48b4SAndrew Jeffery static int kcs_bmc_ipmi_open(struct inode *inode, struct file *filp) 21555ab48b4SAndrew Jeffery { 21655ab48b4SAndrew Jeffery struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); 21755ab48b4SAndrew Jeffery int ret = 0; 21855ab48b4SAndrew Jeffery 21955ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 22055ab48b4SAndrew Jeffery if (!kcs_bmc->running) 22155ab48b4SAndrew Jeffery kcs_bmc->running = 1; 22255ab48b4SAndrew Jeffery else 22355ab48b4SAndrew Jeffery ret = -EBUSY; 22455ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 22555ab48b4SAndrew Jeffery 22655ab48b4SAndrew Jeffery return ret; 22755ab48b4SAndrew Jeffery } 22855ab48b4SAndrew Jeffery 22955ab48b4SAndrew Jeffery static __poll_t kcs_bmc_ipmi_poll(struct file *filp, poll_table *wait) 23055ab48b4SAndrew Jeffery { 23155ab48b4SAndrew Jeffery struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); 23255ab48b4SAndrew Jeffery __poll_t mask = 0; 23355ab48b4SAndrew Jeffery 23455ab48b4SAndrew Jeffery poll_wait(filp, &kcs_bmc->queue, wait); 23555ab48b4SAndrew Jeffery 23655ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 23755ab48b4SAndrew Jeffery if (kcs_bmc->data_in_avail) 23855ab48b4SAndrew Jeffery mask |= EPOLLIN; 23955ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 24055ab48b4SAndrew Jeffery 24155ab48b4SAndrew Jeffery return mask; 24255ab48b4SAndrew Jeffery } 24355ab48b4SAndrew Jeffery 24455ab48b4SAndrew Jeffery static ssize_t kcs_bmc_ipmi_read(struct file *filp, char __user *buf, 24555ab48b4SAndrew Jeffery size_t count, loff_t *ppos) 24655ab48b4SAndrew Jeffery { 24755ab48b4SAndrew Jeffery struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); 24855ab48b4SAndrew Jeffery bool data_avail; 24955ab48b4SAndrew Jeffery size_t data_len; 25055ab48b4SAndrew Jeffery ssize_t ret; 25155ab48b4SAndrew Jeffery 25255ab48b4SAndrew Jeffery if (!(filp->f_flags & O_NONBLOCK)) 25355ab48b4SAndrew Jeffery wait_event_interruptible(kcs_bmc->queue, 25455ab48b4SAndrew Jeffery kcs_bmc->data_in_avail); 25555ab48b4SAndrew Jeffery 25655ab48b4SAndrew Jeffery mutex_lock(&kcs_bmc->mutex); 25755ab48b4SAndrew Jeffery 25855ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 25955ab48b4SAndrew Jeffery data_avail = kcs_bmc->data_in_avail; 26055ab48b4SAndrew Jeffery if (data_avail) { 26155ab48b4SAndrew Jeffery data_len = kcs_bmc->data_in_idx; 26255ab48b4SAndrew Jeffery memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len); 26355ab48b4SAndrew Jeffery } 26455ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 26555ab48b4SAndrew Jeffery 26655ab48b4SAndrew Jeffery if (!data_avail) { 26755ab48b4SAndrew Jeffery ret = -EAGAIN; 26855ab48b4SAndrew Jeffery goto out_unlock; 26955ab48b4SAndrew Jeffery } 27055ab48b4SAndrew Jeffery 27155ab48b4SAndrew Jeffery if (count < data_len) { 27255ab48b4SAndrew Jeffery pr_err("channel=%u with too large data : %zu\n", 27355ab48b4SAndrew Jeffery kcs_bmc->channel, data_len); 27455ab48b4SAndrew Jeffery 27555ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 27655ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 27755ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 27855ab48b4SAndrew Jeffery 27955ab48b4SAndrew Jeffery ret = -EOVERFLOW; 28055ab48b4SAndrew Jeffery goto out_unlock; 28155ab48b4SAndrew Jeffery } 28255ab48b4SAndrew Jeffery 28355ab48b4SAndrew Jeffery if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) { 28455ab48b4SAndrew Jeffery ret = -EFAULT; 28555ab48b4SAndrew Jeffery goto out_unlock; 28655ab48b4SAndrew Jeffery } 28755ab48b4SAndrew Jeffery 28855ab48b4SAndrew Jeffery ret = data_len; 28955ab48b4SAndrew Jeffery 29055ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 29155ab48b4SAndrew Jeffery if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) { 29255ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_WAIT_READ; 29355ab48b4SAndrew Jeffery kcs_bmc->data_in_avail = false; 29455ab48b4SAndrew Jeffery kcs_bmc->data_in_idx = 0; 29555ab48b4SAndrew Jeffery } else { 29655ab48b4SAndrew Jeffery ret = -EAGAIN; 29755ab48b4SAndrew Jeffery } 29855ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 29955ab48b4SAndrew Jeffery 30055ab48b4SAndrew Jeffery out_unlock: 30155ab48b4SAndrew Jeffery mutex_unlock(&kcs_bmc->mutex); 30255ab48b4SAndrew Jeffery 30355ab48b4SAndrew Jeffery return ret; 30455ab48b4SAndrew Jeffery } 30555ab48b4SAndrew Jeffery 30655ab48b4SAndrew Jeffery static ssize_t kcs_bmc_ipmi_write(struct file *filp, const char __user *buf, 30755ab48b4SAndrew Jeffery size_t count, loff_t *ppos) 30855ab48b4SAndrew Jeffery { 30955ab48b4SAndrew Jeffery struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); 31055ab48b4SAndrew Jeffery ssize_t ret; 31155ab48b4SAndrew Jeffery 31255ab48b4SAndrew Jeffery /* a minimum response size '3' : netfn + cmd + ccode */ 31355ab48b4SAndrew Jeffery if (count < 3 || count > KCS_MSG_BUFSIZ) 31455ab48b4SAndrew Jeffery return -EINVAL; 31555ab48b4SAndrew Jeffery 31655ab48b4SAndrew Jeffery mutex_lock(&kcs_bmc->mutex); 31755ab48b4SAndrew Jeffery 31855ab48b4SAndrew Jeffery if (copy_from_user(kcs_bmc->kbuffer, buf, count)) { 31955ab48b4SAndrew Jeffery ret = -EFAULT; 32055ab48b4SAndrew Jeffery goto out_unlock; 32155ab48b4SAndrew Jeffery } 32255ab48b4SAndrew Jeffery 32355ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 32455ab48b4SAndrew Jeffery if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) { 32555ab48b4SAndrew Jeffery kcs_bmc->phase = KCS_PHASE_READ; 32655ab48b4SAndrew Jeffery kcs_bmc->data_out_idx = 1; 32755ab48b4SAndrew Jeffery kcs_bmc->data_out_len = count; 32855ab48b4SAndrew Jeffery memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count); 32955ab48b4SAndrew Jeffery kcs_bmc_write_data(kcs_bmc, kcs_bmc->data_out[0]); 33055ab48b4SAndrew Jeffery ret = count; 33155ab48b4SAndrew Jeffery } else { 33255ab48b4SAndrew Jeffery ret = -EINVAL; 33355ab48b4SAndrew Jeffery } 33455ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 33555ab48b4SAndrew Jeffery 33655ab48b4SAndrew Jeffery out_unlock: 33755ab48b4SAndrew Jeffery mutex_unlock(&kcs_bmc->mutex); 33855ab48b4SAndrew Jeffery 33955ab48b4SAndrew Jeffery return ret; 34055ab48b4SAndrew Jeffery } 34155ab48b4SAndrew Jeffery 34255ab48b4SAndrew Jeffery static long kcs_bmc_ipmi_ioctl(struct file *filp, unsigned int cmd, 34355ab48b4SAndrew Jeffery unsigned long arg) 34455ab48b4SAndrew Jeffery { 34555ab48b4SAndrew Jeffery struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); 34655ab48b4SAndrew Jeffery long ret = 0; 34755ab48b4SAndrew Jeffery 34855ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 34955ab48b4SAndrew Jeffery 35055ab48b4SAndrew Jeffery switch (cmd) { 35155ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_SET_SMS_ATN: 35255ab48b4SAndrew Jeffery kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, KCS_STATUS_SMS_ATN); 35355ab48b4SAndrew Jeffery break; 35455ab48b4SAndrew Jeffery 35555ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_CLEAR_SMS_ATN: 35655ab48b4SAndrew Jeffery kcs_bmc_update_status(kcs_bmc, KCS_STATUS_SMS_ATN, 0); 35755ab48b4SAndrew Jeffery break; 35855ab48b4SAndrew Jeffery 35955ab48b4SAndrew Jeffery case IPMI_BMC_IOCTL_FORCE_ABORT: 36055ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 36155ab48b4SAndrew Jeffery break; 36255ab48b4SAndrew Jeffery 36355ab48b4SAndrew Jeffery default: 36455ab48b4SAndrew Jeffery ret = -EINVAL; 36555ab48b4SAndrew Jeffery break; 36655ab48b4SAndrew Jeffery } 36755ab48b4SAndrew Jeffery 36855ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 36955ab48b4SAndrew Jeffery 37055ab48b4SAndrew Jeffery return ret; 37155ab48b4SAndrew Jeffery } 37255ab48b4SAndrew Jeffery 37355ab48b4SAndrew Jeffery static int kcs_bmc_ipmi_release(struct inode *inode, struct file *filp) 37455ab48b4SAndrew Jeffery { 37555ab48b4SAndrew Jeffery struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); 37655ab48b4SAndrew Jeffery 37755ab48b4SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 37855ab48b4SAndrew Jeffery kcs_bmc->running = 0; 37955ab48b4SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 38055ab48b4SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 38155ab48b4SAndrew Jeffery 38255ab48b4SAndrew Jeffery return 0; 38355ab48b4SAndrew Jeffery } 38455ab48b4SAndrew Jeffery 385*d7096970SAndrew Jeffery static const struct file_operations kcs_bmc_ipmi_fops = { 38655ab48b4SAndrew Jeffery .owner = THIS_MODULE, 38755ab48b4SAndrew Jeffery .open = kcs_bmc_ipmi_open, 38855ab48b4SAndrew Jeffery .read = kcs_bmc_ipmi_read, 38955ab48b4SAndrew Jeffery .write = kcs_bmc_ipmi_write, 39055ab48b4SAndrew Jeffery .release = kcs_bmc_ipmi_release, 39155ab48b4SAndrew Jeffery .poll = kcs_bmc_ipmi_poll, 39255ab48b4SAndrew Jeffery .unlocked_ioctl = kcs_bmc_ipmi_ioctl, 39355ab48b4SAndrew Jeffery }; 39455ab48b4SAndrew Jeffery 395*d7096970SAndrew Jeffery int kcs_bmc_ipmi_add_device(struct kcs_bmc *kcs_bmc); 396*d7096970SAndrew Jeffery int kcs_bmc_ipmi_add_device(struct kcs_bmc *kcs_bmc) 39755ab48b4SAndrew Jeffery { 398*d7096970SAndrew Jeffery int rc; 39955ab48b4SAndrew Jeffery 40055ab48b4SAndrew Jeffery spin_lock_init(&kcs_bmc->lock); 40155ab48b4SAndrew Jeffery mutex_init(&kcs_bmc->mutex); 40255ab48b4SAndrew Jeffery init_waitqueue_head(&kcs_bmc->queue); 40355ab48b4SAndrew Jeffery 404*d7096970SAndrew Jeffery kcs_bmc->data_in = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 405*d7096970SAndrew Jeffery kcs_bmc->data_out = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 406*d7096970SAndrew Jeffery kcs_bmc->kbuffer = devm_kmalloc(kcs_bmc->dev, KCS_MSG_BUFSIZ, GFP_KERNEL); 40755ab48b4SAndrew Jeffery 40855ab48b4SAndrew Jeffery kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; 409*d7096970SAndrew Jeffery kcs_bmc->miscdev.name = devm_kasprintf(kcs_bmc->dev, GFP_KERNEL, "%s%u", 410*d7096970SAndrew Jeffery DEVICE_NAME, kcs_bmc->channel); 41155ab48b4SAndrew Jeffery if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer || 41255ab48b4SAndrew Jeffery !kcs_bmc->miscdev.name) 413*d7096970SAndrew Jeffery return -ENOMEM; 41455ab48b4SAndrew Jeffery 415*d7096970SAndrew Jeffery kcs_bmc->miscdev.fops = &kcs_bmc_ipmi_fops; 416*d7096970SAndrew Jeffery 417*d7096970SAndrew Jeffery rc = misc_register(&kcs_bmc->miscdev); 418*d7096970SAndrew Jeffery if (rc) { 419*d7096970SAndrew Jeffery dev_err(kcs_bmc->dev, "Unable to register device: %d\n", rc); 420*d7096970SAndrew Jeffery return rc; 42155ab48b4SAndrew Jeffery } 422*d7096970SAndrew Jeffery 423*d7096970SAndrew Jeffery dev_info(kcs_bmc->dev, "Initialised IPMI client for channel %d", kcs_bmc->channel); 424*d7096970SAndrew Jeffery 425*d7096970SAndrew Jeffery return 0; 426*d7096970SAndrew Jeffery } 427*d7096970SAndrew Jeffery EXPORT_SYMBOL(kcs_bmc_ipmi_add_device); 428*d7096970SAndrew Jeffery 429*d7096970SAndrew Jeffery int kcs_bmc_ipmi_remove_device(struct kcs_bmc *kcs_bmc); 430*d7096970SAndrew Jeffery int kcs_bmc_ipmi_remove_device(struct kcs_bmc *kcs_bmc) 431*d7096970SAndrew Jeffery { 432*d7096970SAndrew Jeffery misc_deregister(&kcs_bmc->miscdev); 433*d7096970SAndrew Jeffery 434*d7096970SAndrew Jeffery spin_lock_irq(&kcs_bmc->lock); 435*d7096970SAndrew Jeffery kcs_bmc->running = 0; 436*d7096970SAndrew Jeffery kcs_bmc_ipmi_force_abort(kcs_bmc); 437*d7096970SAndrew Jeffery spin_unlock_irq(&kcs_bmc->lock); 438*d7096970SAndrew Jeffery 439*d7096970SAndrew Jeffery devm_kfree(kcs_bmc->dev, kcs_bmc->kbuffer); 440*d7096970SAndrew Jeffery devm_kfree(kcs_bmc->dev, kcs_bmc->data_out); 441*d7096970SAndrew Jeffery devm_kfree(kcs_bmc->dev, kcs_bmc->data_in); 442*d7096970SAndrew Jeffery devm_kfree(kcs_bmc->dev, kcs_bmc); 443*d7096970SAndrew Jeffery 444*d7096970SAndrew Jeffery return 0; 445*d7096970SAndrew Jeffery } 446*d7096970SAndrew Jeffery EXPORT_SYMBOL(kcs_bmc_ipmi_remove_device); 44755ab48b4SAndrew Jeffery 44855ab48b4SAndrew Jeffery MODULE_LICENSE("GPL v2"); 44955ab48b4SAndrew Jeffery MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); 45055ab48b4SAndrew Jeffery MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); 451