xref: /linux/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c (revision d4e7ac68f771addc19352121706d8584eb0166cd)
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