xref: /openbmc/linux/drivers/char/ipmi/ipmb_dev_int.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
151bd6f29SAsmaa Mnebhi // SPDX-License-Identifier: GPL-2.0
251bd6f29SAsmaa Mnebhi 
351bd6f29SAsmaa Mnebhi /*
451bd6f29SAsmaa Mnebhi  * IPMB driver to receive a request and send a response
551bd6f29SAsmaa Mnebhi  *
651bd6f29SAsmaa Mnebhi  * Copyright (C) 2019 Mellanox Techologies, Ltd.
751bd6f29SAsmaa Mnebhi  *
851bd6f29SAsmaa Mnebhi  * This was inspired by Brendan Higgins' ipmi-bmc-bt-i2c driver.
951bd6f29SAsmaa Mnebhi  */
1051bd6f29SAsmaa Mnebhi 
1151bd6f29SAsmaa Mnebhi #include <linux/acpi.h>
1251bd6f29SAsmaa Mnebhi #include <linux/errno.h>
1351bd6f29SAsmaa Mnebhi #include <linux/i2c.h>
1451bd6f29SAsmaa Mnebhi #include <linux/miscdevice.h>
1551bd6f29SAsmaa Mnebhi #include <linux/module.h>
1651bd6f29SAsmaa Mnebhi #include <linux/mutex.h>
1751bd6f29SAsmaa Mnebhi #include <linux/poll.h>
1851bd6f29SAsmaa Mnebhi #include <linux/slab.h>
1951bd6f29SAsmaa Mnebhi #include <linux/spinlock.h>
2051bd6f29SAsmaa Mnebhi #include <linux/wait.h>
2151bd6f29SAsmaa Mnebhi 
22380665beSVijay Khemka #define MAX_MSG_LEN		240
2351bd6f29SAsmaa Mnebhi #define IPMB_REQUEST_LEN_MIN	7
2451bd6f29SAsmaa Mnebhi #define NETFN_RSP_BIT_MASK	0x4
2551bd6f29SAsmaa Mnebhi #define REQUEST_QUEUE_MAX_LEN	256
2651bd6f29SAsmaa Mnebhi 
2751bd6f29SAsmaa Mnebhi #define IPMB_MSG_LEN_IDX	0
2851bd6f29SAsmaa Mnebhi #define RQ_SA_8BIT_IDX		1
2951bd6f29SAsmaa Mnebhi #define NETFN_LUN_IDX		2
3051bd6f29SAsmaa Mnebhi 
3151bd6f29SAsmaa Mnebhi #define GET_7BIT_ADDR(addr_8bit)	(addr_8bit >> 1)
3251bd6f29SAsmaa Mnebhi #define GET_8BIT_ADDR(addr_7bit)	((addr_7bit << 1) & 0xff)
3351bd6f29SAsmaa Mnebhi 
3451bd6f29SAsmaa Mnebhi #define IPMB_MSG_PAYLOAD_LEN_MAX (MAX_MSG_LEN - IPMB_REQUEST_LEN_MIN - 1)
3551bd6f29SAsmaa Mnebhi 
3651bd6f29SAsmaa Mnebhi #define SMBUS_MSG_HEADER_LENGTH	2
3751bd6f29SAsmaa Mnebhi #define SMBUS_MSG_IDX_OFFSET	(SMBUS_MSG_HEADER_LENGTH + 1)
3851bd6f29SAsmaa Mnebhi 
3951bd6f29SAsmaa Mnebhi struct ipmb_msg {
4051bd6f29SAsmaa Mnebhi 	u8 len;
4151bd6f29SAsmaa Mnebhi 	u8 rs_sa;
4251bd6f29SAsmaa Mnebhi 	u8 netfn_rs_lun;
4351bd6f29SAsmaa Mnebhi 	u8 checksum1;
4451bd6f29SAsmaa Mnebhi 	u8 rq_sa;
4551bd6f29SAsmaa Mnebhi 	u8 rq_seq_rq_lun;
4651bd6f29SAsmaa Mnebhi 	u8 cmd;
4751bd6f29SAsmaa Mnebhi 	u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX];
4851bd6f29SAsmaa Mnebhi 	/* checksum2 is included in payload */
4951bd6f29SAsmaa Mnebhi } __packed;
5051bd6f29SAsmaa Mnebhi 
5151bd6f29SAsmaa Mnebhi struct ipmb_request_elem {
5251bd6f29SAsmaa Mnebhi 	struct list_head list;
5351bd6f29SAsmaa Mnebhi 	struct ipmb_msg request;
5451bd6f29SAsmaa Mnebhi };
5551bd6f29SAsmaa Mnebhi 
5651bd6f29SAsmaa Mnebhi struct ipmb_dev {
5751bd6f29SAsmaa Mnebhi 	struct i2c_client *client;
5851bd6f29SAsmaa Mnebhi 	struct miscdevice miscdev;
5951bd6f29SAsmaa Mnebhi 	struct ipmb_msg request;
6051bd6f29SAsmaa Mnebhi 	struct list_head request_queue;
6151bd6f29SAsmaa Mnebhi 	atomic_t request_queue_len;
6251bd6f29SAsmaa Mnebhi 	size_t msg_idx;
6351bd6f29SAsmaa Mnebhi 	spinlock_t lock;
6451bd6f29SAsmaa Mnebhi 	wait_queue_head_t wait_queue;
6551bd6f29SAsmaa Mnebhi 	struct mutex file_mutex;
66042f057fSVijay Khemka 	bool is_i2c_protocol;
6751bd6f29SAsmaa Mnebhi };
6851bd6f29SAsmaa Mnebhi 
to_ipmb_dev(struct file * file)6951bd6f29SAsmaa Mnebhi static inline struct ipmb_dev *to_ipmb_dev(struct file *file)
7051bd6f29SAsmaa Mnebhi {
7151bd6f29SAsmaa Mnebhi 	return container_of(file->private_data, struct ipmb_dev, miscdev);
7251bd6f29SAsmaa Mnebhi }
7351bd6f29SAsmaa Mnebhi 
ipmb_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)7451bd6f29SAsmaa Mnebhi static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count,
7551bd6f29SAsmaa Mnebhi 			loff_t *ppos)
7651bd6f29SAsmaa Mnebhi {
7751bd6f29SAsmaa Mnebhi 	struct ipmb_dev *ipmb_dev = to_ipmb_dev(file);
7851bd6f29SAsmaa Mnebhi 	struct ipmb_request_elem *queue_elem;
7951bd6f29SAsmaa Mnebhi 	struct ipmb_msg msg;
8071be7b0eSAsmaa Mnebhi 	ssize_t ret = 0;
8151bd6f29SAsmaa Mnebhi 
8251bd6f29SAsmaa Mnebhi 	memset(&msg, 0, sizeof(msg));
8351bd6f29SAsmaa Mnebhi 
8451bd6f29SAsmaa Mnebhi 	spin_lock_irq(&ipmb_dev->lock);
8551bd6f29SAsmaa Mnebhi 
8651bd6f29SAsmaa Mnebhi 	while (list_empty(&ipmb_dev->request_queue)) {
8751bd6f29SAsmaa Mnebhi 		spin_unlock_irq(&ipmb_dev->lock);
8851bd6f29SAsmaa Mnebhi 
8951bd6f29SAsmaa Mnebhi 		if (file->f_flags & O_NONBLOCK)
9051bd6f29SAsmaa Mnebhi 			return -EAGAIN;
9151bd6f29SAsmaa Mnebhi 
9251bd6f29SAsmaa Mnebhi 		ret = wait_event_interruptible(ipmb_dev->wait_queue,
9351bd6f29SAsmaa Mnebhi 				!list_empty(&ipmb_dev->request_queue));
9451bd6f29SAsmaa Mnebhi 		if (ret)
9551bd6f29SAsmaa Mnebhi 			return ret;
9651bd6f29SAsmaa Mnebhi 
9751bd6f29SAsmaa Mnebhi 		spin_lock_irq(&ipmb_dev->lock);
9851bd6f29SAsmaa Mnebhi 	}
9951bd6f29SAsmaa Mnebhi 
10051bd6f29SAsmaa Mnebhi 	queue_elem = list_first_entry(&ipmb_dev->request_queue,
10151bd6f29SAsmaa Mnebhi 					struct ipmb_request_elem, list);
10251bd6f29SAsmaa Mnebhi 	memcpy(&msg, &queue_elem->request, sizeof(msg));
10351bd6f29SAsmaa Mnebhi 	list_del(&queue_elem->list);
10451bd6f29SAsmaa Mnebhi 	kfree(queue_elem);
10551bd6f29SAsmaa Mnebhi 	atomic_dec(&ipmb_dev->request_queue_len);
10651bd6f29SAsmaa Mnebhi 
10751bd6f29SAsmaa Mnebhi 	spin_unlock_irq(&ipmb_dev->lock);
10851bd6f29SAsmaa Mnebhi 
10951bd6f29SAsmaa Mnebhi 	count = min_t(size_t, count, msg.len + 1);
11051bd6f29SAsmaa Mnebhi 	if (copy_to_user(buf, &msg, count))
11151bd6f29SAsmaa Mnebhi 		ret = -EFAULT;
11251bd6f29SAsmaa Mnebhi 
11351bd6f29SAsmaa Mnebhi 	return ret < 0 ? ret : count;
11451bd6f29SAsmaa Mnebhi }
11551bd6f29SAsmaa Mnebhi 
ipmb_i2c_write(struct i2c_client * client,u8 * msg,u8 addr)116042f057fSVijay Khemka static int ipmb_i2c_write(struct i2c_client *client, u8 *msg, u8 addr)
117042f057fSVijay Khemka {
118042f057fSVijay Khemka 	struct i2c_msg i2c_msg;
119042f057fSVijay Khemka 
120042f057fSVijay Khemka 	/*
121042f057fSVijay Khemka 	 * subtract 1 byte (rq_sa) from the length of the msg passed to
122042f057fSVijay Khemka 	 * raw i2c_transfer
123042f057fSVijay Khemka 	 */
124042f057fSVijay Khemka 	i2c_msg.len = msg[IPMB_MSG_LEN_IDX] - 1;
125042f057fSVijay Khemka 
126042f057fSVijay Khemka 	/* Assign message to buffer except first 2 bytes (length and address) */
127042f057fSVijay Khemka 	i2c_msg.buf = msg + 2;
128042f057fSVijay Khemka 
129042f057fSVijay Khemka 	i2c_msg.addr = addr;
130042f057fSVijay Khemka 	i2c_msg.flags = client->flags & I2C_CLIENT_PEC;
131042f057fSVijay Khemka 
132042f057fSVijay Khemka 	return i2c_transfer(client->adapter, &i2c_msg, 1);
133042f057fSVijay Khemka }
134042f057fSVijay Khemka 
ipmb_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)13551bd6f29SAsmaa Mnebhi static ssize_t ipmb_write(struct file *file, const char __user *buf,
13651bd6f29SAsmaa Mnebhi 			size_t count, loff_t *ppos)
13751bd6f29SAsmaa Mnebhi {
13851bd6f29SAsmaa Mnebhi 	struct ipmb_dev *ipmb_dev = to_ipmb_dev(file);
13951bd6f29SAsmaa Mnebhi 	u8 rq_sa, netf_rq_lun, msg_len;
140fc26067cSWolfram Sang 	struct i2c_client *temp_client;
14151bd6f29SAsmaa Mnebhi 	u8 msg[MAX_MSG_LEN];
14251bd6f29SAsmaa Mnebhi 	ssize_t ret;
14351bd6f29SAsmaa Mnebhi 
14451bd6f29SAsmaa Mnebhi 	if (count > sizeof(msg))
14551bd6f29SAsmaa Mnebhi 		return -EINVAL;
14651bd6f29SAsmaa Mnebhi 
14751bd6f29SAsmaa Mnebhi 	if (copy_from_user(&msg, buf, count))
14851bd6f29SAsmaa Mnebhi 		return -EFAULT;
14951bd6f29SAsmaa Mnebhi 
15051bd6f29SAsmaa Mnebhi 	if (count < msg[0])
15151bd6f29SAsmaa Mnebhi 		return -EINVAL;
15251bd6f29SAsmaa Mnebhi 
15351bd6f29SAsmaa Mnebhi 	rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]);
15451bd6f29SAsmaa Mnebhi 	netf_rq_lun = msg[NETFN_LUN_IDX];
15551bd6f29SAsmaa Mnebhi 
156042f057fSVijay Khemka 	/* Check i2c block transfer vs smbus */
157042f057fSVijay Khemka 	if (ipmb_dev->is_i2c_protocol) {
158042f057fSVijay Khemka 		ret = ipmb_i2c_write(ipmb_dev->client, msg, rq_sa);
159042f057fSVijay Khemka 		return (ret == 1) ? count : ret;
160042f057fSVijay Khemka 	}
161042f057fSVijay Khemka 
16251bd6f29SAsmaa Mnebhi 	/*
163fc26067cSWolfram Sang 	 * subtract rq_sa and netf_rq_lun from the length of the msg. Fill the
164fc26067cSWolfram Sang 	 * temporary client. Note that its use is an exception for IPMI.
16551bd6f29SAsmaa Mnebhi 	 */
16651bd6f29SAsmaa Mnebhi 	msg_len = msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH;
167fc26067cSWolfram Sang 	temp_client = kmemdup(ipmb_dev->client, sizeof(*temp_client), GFP_KERNEL);
168fc26067cSWolfram Sang 	if (!temp_client)
169fc26067cSWolfram Sang 		return -ENOMEM;
17051bd6f29SAsmaa Mnebhi 
171fc26067cSWolfram Sang 	temp_client->addr = rq_sa;
17251bd6f29SAsmaa Mnebhi 
173fc26067cSWolfram Sang 	ret = i2c_smbus_write_block_data(temp_client, netf_rq_lun, msg_len,
174fc26067cSWolfram Sang 					 msg + SMBUS_MSG_IDX_OFFSET);
175fc26067cSWolfram Sang 	kfree(temp_client);
176fc26067cSWolfram Sang 
177fc26067cSWolfram Sang 	return ret < 0 ? ret : count;
17851bd6f29SAsmaa Mnebhi }
17951bd6f29SAsmaa Mnebhi 
ipmb_poll(struct file * file,poll_table * wait)1808e6a5c83SLuc Van Oostenryck static __poll_t ipmb_poll(struct file *file, poll_table *wait)
18151bd6f29SAsmaa Mnebhi {
18251bd6f29SAsmaa Mnebhi 	struct ipmb_dev *ipmb_dev = to_ipmb_dev(file);
1838e6a5c83SLuc Van Oostenryck 	__poll_t mask = EPOLLOUT;
18451bd6f29SAsmaa Mnebhi 
18551bd6f29SAsmaa Mnebhi 	mutex_lock(&ipmb_dev->file_mutex);
18651bd6f29SAsmaa Mnebhi 	poll_wait(file, &ipmb_dev->wait_queue, wait);
18751bd6f29SAsmaa Mnebhi 
18851bd6f29SAsmaa Mnebhi 	if (atomic_read(&ipmb_dev->request_queue_len))
1898e6a5c83SLuc Van Oostenryck 		mask |= EPOLLIN;
19051bd6f29SAsmaa Mnebhi 	mutex_unlock(&ipmb_dev->file_mutex);
19151bd6f29SAsmaa Mnebhi 
19251bd6f29SAsmaa Mnebhi 	return mask;
19351bd6f29SAsmaa Mnebhi }
19451bd6f29SAsmaa Mnebhi 
19551bd6f29SAsmaa Mnebhi static const struct file_operations ipmb_fops = {
19651bd6f29SAsmaa Mnebhi 	.owner	= THIS_MODULE,
19751bd6f29SAsmaa Mnebhi 	.read	= ipmb_read,
19851bd6f29SAsmaa Mnebhi 	.write	= ipmb_write,
19951bd6f29SAsmaa Mnebhi 	.poll	= ipmb_poll,
20051bd6f29SAsmaa Mnebhi };
20151bd6f29SAsmaa Mnebhi 
20251bd6f29SAsmaa Mnebhi /* Called with ipmb_dev->lock held. */
ipmb_handle_request(struct ipmb_dev * ipmb_dev)20351bd6f29SAsmaa Mnebhi static void ipmb_handle_request(struct ipmb_dev *ipmb_dev)
20451bd6f29SAsmaa Mnebhi {
20551bd6f29SAsmaa Mnebhi 	struct ipmb_request_elem *queue_elem;
20651bd6f29SAsmaa Mnebhi 
20751bd6f29SAsmaa Mnebhi 	if (atomic_read(&ipmb_dev->request_queue_len) >=
20851bd6f29SAsmaa Mnebhi 			REQUEST_QUEUE_MAX_LEN)
20951bd6f29SAsmaa Mnebhi 		return;
21051bd6f29SAsmaa Mnebhi 
21151bd6f29SAsmaa Mnebhi 	queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC);
21251bd6f29SAsmaa Mnebhi 	if (!queue_elem)
21351bd6f29SAsmaa Mnebhi 		return;
21451bd6f29SAsmaa Mnebhi 
21551bd6f29SAsmaa Mnebhi 	memcpy(&queue_elem->request, &ipmb_dev->request,
21651bd6f29SAsmaa Mnebhi 		sizeof(struct ipmb_msg));
21751bd6f29SAsmaa Mnebhi 	list_add(&queue_elem->list, &ipmb_dev->request_queue);
21851bd6f29SAsmaa Mnebhi 	atomic_inc(&ipmb_dev->request_queue_len);
21951bd6f29SAsmaa Mnebhi 	wake_up_all(&ipmb_dev->wait_queue);
22051bd6f29SAsmaa Mnebhi }
22151bd6f29SAsmaa Mnebhi 
ipmb_verify_checksum1(struct ipmb_dev * ipmb_dev,u8 rs_sa)22251bd6f29SAsmaa Mnebhi static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa)
22351bd6f29SAsmaa Mnebhi {
22451bd6f29SAsmaa Mnebhi 	/* The 8 lsb of the sum is 0 when the checksum is valid */
22551bd6f29SAsmaa Mnebhi 	return (rs_sa + ipmb_dev->request.netfn_rs_lun +
22651bd6f29SAsmaa Mnebhi 		ipmb_dev->request.checksum1);
22751bd6f29SAsmaa Mnebhi }
22851bd6f29SAsmaa Mnebhi 
22951bd6f29SAsmaa Mnebhi /*
2300d8633bfSVijay Khemka  * Verify if message has proper ipmb header with minimum length
2310d8633bfSVijay Khemka  * and correct checksum byte.
23251bd6f29SAsmaa Mnebhi  */
is_ipmb_msg(struct ipmb_dev * ipmb_dev,u8 rs_sa)2330d8633bfSVijay Khemka static bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa)
2340d8633bfSVijay Khemka {
2350d8633bfSVijay Khemka 	if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) &&
2360d8633bfSVijay Khemka 	   (!ipmb_verify_checksum1(ipmb_dev, rs_sa)))
23751bd6f29SAsmaa Mnebhi 		return true;
2380d8633bfSVijay Khemka 
23951bd6f29SAsmaa Mnebhi 	return false;
24051bd6f29SAsmaa Mnebhi }
24151bd6f29SAsmaa Mnebhi 
24251bd6f29SAsmaa Mnebhi /*
24351bd6f29SAsmaa Mnebhi  * The IPMB protocol only supports I2C Writes so there is no need
24451bd6f29SAsmaa Mnebhi  * to support I2C_SLAVE_READ* events.
24551bd6f29SAsmaa Mnebhi  * This i2c callback function only monitors IPMB request messages
24651bd6f29SAsmaa Mnebhi  * and adds them in a queue, so that they can be handled by
24751bd6f29SAsmaa Mnebhi  * receive_ipmb_request.
24851bd6f29SAsmaa Mnebhi  */
ipmb_slave_cb(struct i2c_client * client,enum i2c_slave_event event,u8 * val)24951bd6f29SAsmaa Mnebhi static int ipmb_slave_cb(struct i2c_client *client,
25051bd6f29SAsmaa Mnebhi 			enum i2c_slave_event event, u8 *val)
25151bd6f29SAsmaa Mnebhi {
25251bd6f29SAsmaa Mnebhi 	struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client);
25351bd6f29SAsmaa Mnebhi 	u8 *buf = (u8 *)&ipmb_dev->request;
25451bd6f29SAsmaa Mnebhi 	unsigned long flags;
25551bd6f29SAsmaa Mnebhi 
25651bd6f29SAsmaa Mnebhi 	spin_lock_irqsave(&ipmb_dev->lock, flags);
25751bd6f29SAsmaa Mnebhi 	switch (event) {
25851bd6f29SAsmaa Mnebhi 	case I2C_SLAVE_WRITE_REQUESTED:
25951bd6f29SAsmaa Mnebhi 		memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request));
26051bd6f29SAsmaa Mnebhi 		ipmb_dev->msg_idx = 0;
26151bd6f29SAsmaa Mnebhi 
26251bd6f29SAsmaa Mnebhi 		/*
26351bd6f29SAsmaa Mnebhi 		 * At index 0, ipmb_msg stores the length of msg,
26451bd6f29SAsmaa Mnebhi 		 * skip it for now.
26551bd6f29SAsmaa Mnebhi 		 * The len will be populated once the whole
26651bd6f29SAsmaa Mnebhi 		 * buf is populated.
26751bd6f29SAsmaa Mnebhi 		 *
26851bd6f29SAsmaa Mnebhi 		 * The I2C bus driver's responsibility is to pass the
26951bd6f29SAsmaa Mnebhi 		 * data bytes to the backend driver; it does not
27051bd6f29SAsmaa Mnebhi 		 * forward the i2c slave address.
27151bd6f29SAsmaa Mnebhi 		 * Since the first byte in the IPMB message is the
27251bd6f29SAsmaa Mnebhi 		 * address of the responder, it is the responsibility
27351bd6f29SAsmaa Mnebhi 		 * of the IPMB driver to format the message properly.
27451bd6f29SAsmaa Mnebhi 		 * So this driver prepends the address of the responder
27551bd6f29SAsmaa Mnebhi 		 * to the received i2c data before the request message
27651bd6f29SAsmaa Mnebhi 		 * is handled in userland.
27751bd6f29SAsmaa Mnebhi 		 */
27851bd6f29SAsmaa Mnebhi 		buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr);
27951bd6f29SAsmaa Mnebhi 		break;
28051bd6f29SAsmaa Mnebhi 
28151bd6f29SAsmaa Mnebhi 	case I2C_SLAVE_WRITE_RECEIVED:
282e0354d14SColin Ian King 		if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg) - 1)
28351bd6f29SAsmaa Mnebhi 			break;
28451bd6f29SAsmaa Mnebhi 
28551bd6f29SAsmaa Mnebhi 		buf[++ipmb_dev->msg_idx] = *val;
28651bd6f29SAsmaa Mnebhi 		break;
28751bd6f29SAsmaa Mnebhi 
28851bd6f29SAsmaa Mnebhi 	case I2C_SLAVE_STOP:
28951bd6f29SAsmaa Mnebhi 		ipmb_dev->request.len = ipmb_dev->msg_idx;
2900d8633bfSVijay Khemka 		if (is_ipmb_msg(ipmb_dev, GET_8BIT_ADDR(client->addr)))
29151bd6f29SAsmaa Mnebhi 			ipmb_handle_request(ipmb_dev);
29251bd6f29SAsmaa Mnebhi 		break;
29351bd6f29SAsmaa Mnebhi 
29451bd6f29SAsmaa Mnebhi 	default:
29551bd6f29SAsmaa Mnebhi 		break;
29651bd6f29SAsmaa Mnebhi 	}
29751bd6f29SAsmaa Mnebhi 	spin_unlock_irqrestore(&ipmb_dev->lock, flags);
29851bd6f29SAsmaa Mnebhi 
29951bd6f29SAsmaa Mnebhi 	return 0;
30051bd6f29SAsmaa Mnebhi }
30151bd6f29SAsmaa Mnebhi 
ipmb_probe(struct i2c_client * client)3020924c5a0SStephen Kitt static int ipmb_probe(struct i2c_client *client)
30351bd6f29SAsmaa Mnebhi {
30451bd6f29SAsmaa Mnebhi 	struct ipmb_dev *ipmb_dev;
30551bd6f29SAsmaa Mnebhi 	int ret;
30651bd6f29SAsmaa Mnebhi 
30751bd6f29SAsmaa Mnebhi 	ipmb_dev = devm_kzalloc(&client->dev, sizeof(*ipmb_dev),
30851bd6f29SAsmaa Mnebhi 					GFP_KERNEL);
30951bd6f29SAsmaa Mnebhi 	if (!ipmb_dev)
31051bd6f29SAsmaa Mnebhi 		return -ENOMEM;
31151bd6f29SAsmaa Mnebhi 
31251bd6f29SAsmaa Mnebhi 	spin_lock_init(&ipmb_dev->lock);
31351bd6f29SAsmaa Mnebhi 	init_waitqueue_head(&ipmb_dev->wait_queue);
31451bd6f29SAsmaa Mnebhi 	atomic_set(&ipmb_dev->request_queue_len, 0);
31551bd6f29SAsmaa Mnebhi 	INIT_LIST_HEAD(&ipmb_dev->request_queue);
31651bd6f29SAsmaa Mnebhi 
31751bd6f29SAsmaa Mnebhi 	mutex_init(&ipmb_dev->file_mutex);
31851bd6f29SAsmaa Mnebhi 
31951bd6f29SAsmaa Mnebhi 	ipmb_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
32051bd6f29SAsmaa Mnebhi 
32151bd6f29SAsmaa Mnebhi 	ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL,
32251bd6f29SAsmaa Mnebhi 						"%s%d", "ipmb-",
32351bd6f29SAsmaa Mnebhi 						client->adapter->nr);
324*4c9caf86SCharles Han 	if (!ipmb_dev->miscdev.name)
325*4c9caf86SCharles Han 		return -ENOMEM;
326*4c9caf86SCharles Han 
32751bd6f29SAsmaa Mnebhi 	ipmb_dev->miscdev.fops = &ipmb_fops;
32851bd6f29SAsmaa Mnebhi 	ipmb_dev->miscdev.parent = &client->dev;
32951bd6f29SAsmaa Mnebhi 	ret = misc_register(&ipmb_dev->miscdev);
33051bd6f29SAsmaa Mnebhi 	if (ret)
33151bd6f29SAsmaa Mnebhi 		return ret;
33251bd6f29SAsmaa Mnebhi 
333042f057fSVijay Khemka 	ipmb_dev->is_i2c_protocol
334042f057fSVijay Khemka 		= device_property_read_bool(&client->dev, "i2c-protocol");
335042f057fSVijay Khemka 
33651bd6f29SAsmaa Mnebhi 	ipmb_dev->client = client;
33751bd6f29SAsmaa Mnebhi 	i2c_set_clientdata(client, ipmb_dev);
33851bd6f29SAsmaa Mnebhi 	ret = i2c_slave_register(client, ipmb_slave_cb);
33951bd6f29SAsmaa Mnebhi 	if (ret) {
34051bd6f29SAsmaa Mnebhi 		misc_deregister(&ipmb_dev->miscdev);
34151bd6f29SAsmaa Mnebhi 		return ret;
34251bd6f29SAsmaa Mnebhi 	}
34351bd6f29SAsmaa Mnebhi 
34451bd6f29SAsmaa Mnebhi 	return 0;
34551bd6f29SAsmaa Mnebhi }
34651bd6f29SAsmaa Mnebhi 
ipmb_remove(struct i2c_client * client)347ed5c2f5fSUwe Kleine-König static void ipmb_remove(struct i2c_client *client)
34851bd6f29SAsmaa Mnebhi {
34951bd6f29SAsmaa Mnebhi 	struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client);
35051bd6f29SAsmaa Mnebhi 
35151bd6f29SAsmaa Mnebhi 	i2c_slave_unregister(client);
35251bd6f29SAsmaa Mnebhi 	misc_deregister(&ipmb_dev->miscdev);
35351bd6f29SAsmaa Mnebhi }
35451bd6f29SAsmaa Mnebhi 
35551bd6f29SAsmaa Mnebhi static const struct i2c_device_id ipmb_id[] = {
35651bd6f29SAsmaa Mnebhi 	{ "ipmb-dev", 0 },
35751bd6f29SAsmaa Mnebhi 	{},
35851bd6f29SAsmaa Mnebhi };
35951bd6f29SAsmaa Mnebhi MODULE_DEVICE_TABLE(i2c, ipmb_id);
36051bd6f29SAsmaa Mnebhi 
36151bd6f29SAsmaa Mnebhi static const struct acpi_device_id acpi_ipmb_id[] = {
36251bd6f29SAsmaa Mnebhi 	{ "IPMB0001", 0 },
36351bd6f29SAsmaa Mnebhi 	{},
36451bd6f29SAsmaa Mnebhi };
36551bd6f29SAsmaa Mnebhi MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id);
36651bd6f29SAsmaa Mnebhi 
36751bd6f29SAsmaa Mnebhi static struct i2c_driver ipmb_driver = {
36851bd6f29SAsmaa Mnebhi 	.driver = {
36951bd6f29SAsmaa Mnebhi 		.name = "ipmb-dev",
37051bd6f29SAsmaa Mnebhi 		.acpi_match_table = ACPI_PTR(acpi_ipmb_id),
37151bd6f29SAsmaa Mnebhi 	},
372e64c82b8SUwe Kleine-König 	.probe = ipmb_probe,
37351bd6f29SAsmaa Mnebhi 	.remove = ipmb_remove,
37451bd6f29SAsmaa Mnebhi 	.id_table = ipmb_id,
37551bd6f29SAsmaa Mnebhi };
37651bd6f29SAsmaa Mnebhi module_i2c_driver(ipmb_driver);
37751bd6f29SAsmaa Mnebhi 
37851bd6f29SAsmaa Mnebhi MODULE_AUTHOR("Mellanox Technologies");
37951bd6f29SAsmaa Mnebhi MODULE_DESCRIPTION("IPMB driver");
38051bd6f29SAsmaa Mnebhi MODULE_LICENSE("GPL v2");
381