18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * IPMB driver to receive a request and send a response 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2019 Mellanox Techologies, Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This was inspired by Brendan Higgins' ipmi-bmc-bt-i2c driver. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/poll.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/wait.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MAX_MSG_LEN 240 238c2ecf20Sopenharmony_ci#define IPMB_REQUEST_LEN_MIN 7 248c2ecf20Sopenharmony_ci#define NETFN_RSP_BIT_MASK 0x4 258c2ecf20Sopenharmony_ci#define REQUEST_QUEUE_MAX_LEN 256 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define IPMB_MSG_LEN_IDX 0 288c2ecf20Sopenharmony_ci#define RQ_SA_8BIT_IDX 1 298c2ecf20Sopenharmony_ci#define NETFN_LUN_IDX 2 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define GET_7BIT_ADDR(addr_8bit) (addr_8bit >> 1) 328c2ecf20Sopenharmony_ci#define GET_8BIT_ADDR(addr_7bit) ((addr_7bit << 1) & 0xff) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define IPMB_MSG_PAYLOAD_LEN_MAX (MAX_MSG_LEN - IPMB_REQUEST_LEN_MIN - 1) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define SMBUS_MSG_HEADER_LENGTH 2 378c2ecf20Sopenharmony_ci#define SMBUS_MSG_IDX_OFFSET (SMBUS_MSG_HEADER_LENGTH + 1) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct ipmb_msg { 408c2ecf20Sopenharmony_ci u8 len; 418c2ecf20Sopenharmony_ci u8 rs_sa; 428c2ecf20Sopenharmony_ci u8 netfn_rs_lun; 438c2ecf20Sopenharmony_ci u8 checksum1; 448c2ecf20Sopenharmony_ci u8 rq_sa; 458c2ecf20Sopenharmony_ci u8 rq_seq_rq_lun; 468c2ecf20Sopenharmony_ci u8 cmd; 478c2ecf20Sopenharmony_ci u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX]; 488c2ecf20Sopenharmony_ci /* checksum2 is included in payload */ 498c2ecf20Sopenharmony_ci} __packed; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct ipmb_request_elem { 528c2ecf20Sopenharmony_ci struct list_head list; 538c2ecf20Sopenharmony_ci struct ipmb_msg request; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct ipmb_dev { 578c2ecf20Sopenharmony_ci struct i2c_client *client; 588c2ecf20Sopenharmony_ci struct miscdevice miscdev; 598c2ecf20Sopenharmony_ci struct ipmb_msg request; 608c2ecf20Sopenharmony_ci struct list_head request_queue; 618c2ecf20Sopenharmony_ci atomic_t request_queue_len; 628c2ecf20Sopenharmony_ci size_t msg_idx; 638c2ecf20Sopenharmony_ci spinlock_t lock; 648c2ecf20Sopenharmony_ci wait_queue_head_t wait_queue; 658c2ecf20Sopenharmony_ci struct mutex file_mutex; 668c2ecf20Sopenharmony_ci bool is_i2c_protocol; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline struct ipmb_dev *to_ipmb_dev(struct file *file) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci return container_of(file->private_data, struct ipmb_dev, miscdev); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic ssize_t ipmb_read(struct file *file, char __user *buf, size_t count, 758c2ecf20Sopenharmony_ci loff_t *ppos) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); 788c2ecf20Sopenharmony_ci struct ipmb_request_elem *queue_elem; 798c2ecf20Sopenharmony_ci struct ipmb_msg msg; 808c2ecf20Sopenharmony_ci ssize_t ret = 0; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci spin_lock_irq(&ipmb_dev->lock); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (list_empty(&ipmb_dev->request_queue)) { 878c2ecf20Sopenharmony_ci spin_unlock_irq(&ipmb_dev->lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 908c2ecf20Sopenharmony_ci return -EAGAIN; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = wait_event_interruptible(ipmb_dev->wait_queue, 938c2ecf20Sopenharmony_ci !list_empty(&ipmb_dev->request_queue)); 948c2ecf20Sopenharmony_ci if (ret) 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irq(&ipmb_dev->lock); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci queue_elem = list_first_entry(&ipmb_dev->request_queue, 1018c2ecf20Sopenharmony_ci struct ipmb_request_elem, list); 1028c2ecf20Sopenharmony_ci memcpy(&msg, &queue_elem->request, sizeof(msg)); 1038c2ecf20Sopenharmony_ci list_del(&queue_elem->list); 1048c2ecf20Sopenharmony_ci kfree(queue_elem); 1058c2ecf20Sopenharmony_ci atomic_dec(&ipmb_dev->request_queue_len); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci spin_unlock_irq(&ipmb_dev->lock); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci count = min_t(size_t, count, msg.len + 1); 1108c2ecf20Sopenharmony_ci if (copy_to_user(buf, &msg, count)) 1118c2ecf20Sopenharmony_ci ret = -EFAULT; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return ret < 0 ? ret : count; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int ipmb_i2c_write(struct i2c_client *client, u8 *msg, u8 addr) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct i2c_msg i2c_msg; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * subtract 1 byte (rq_sa) from the length of the msg passed to 1228c2ecf20Sopenharmony_ci * raw i2c_transfer 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci i2c_msg.len = msg[IPMB_MSG_LEN_IDX] - 1; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Assign message to buffer except first 2 bytes (length and address) */ 1278c2ecf20Sopenharmony_ci i2c_msg.buf = msg + 2; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci i2c_msg.addr = addr; 1308c2ecf20Sopenharmony_ci i2c_msg.flags = client->flags & I2C_CLIENT_PEC; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return i2c_transfer(client->adapter, &i2c_msg, 1); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic ssize_t ipmb_write(struct file *file, const char __user *buf, 1368c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); 1398c2ecf20Sopenharmony_ci u8 rq_sa, netf_rq_lun, msg_len; 1408c2ecf20Sopenharmony_ci union i2c_smbus_data data; 1418c2ecf20Sopenharmony_ci u8 msg[MAX_MSG_LEN]; 1428c2ecf20Sopenharmony_ci ssize_t ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (count > sizeof(msg)) 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (copy_from_user(&msg, buf, count)) 1488c2ecf20Sopenharmony_ci return -EFAULT; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (count < msg[0]) 1518c2ecf20Sopenharmony_ci return -EINVAL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); 1548c2ecf20Sopenharmony_ci netf_rq_lun = msg[NETFN_LUN_IDX]; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Check i2c block transfer vs smbus */ 1578c2ecf20Sopenharmony_ci if (ipmb_dev->is_i2c_protocol) { 1588c2ecf20Sopenharmony_ci ret = ipmb_i2c_write(ipmb_dev->client, msg, rq_sa); 1598c2ecf20Sopenharmony_ci return (ret == 1) ? count : ret; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * subtract rq_sa and netf_rq_lun from the length of the msg passed to 1648c2ecf20Sopenharmony_ci * i2c_smbus_xfer 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci msg_len = msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH; 1678c2ecf20Sopenharmony_ci if (msg_len > I2C_SMBUS_BLOCK_MAX) 1688c2ecf20Sopenharmony_ci msg_len = I2C_SMBUS_BLOCK_MAX; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci data.block[0] = msg_len; 1718c2ecf20Sopenharmony_ci memcpy(&data.block[1], msg + SMBUS_MSG_IDX_OFFSET, msg_len); 1728c2ecf20Sopenharmony_ci ret = i2c_smbus_xfer(ipmb_dev->client->adapter, rq_sa, 1738c2ecf20Sopenharmony_ci ipmb_dev->client->flags, 1748c2ecf20Sopenharmony_ci I2C_SMBUS_WRITE, netf_rq_lun, 1758c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_DATA, &data); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return ret ? : count; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic __poll_t ipmb_poll(struct file *file, poll_table *wait) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); 1838c2ecf20Sopenharmony_ci __poll_t mask = EPOLLOUT; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mutex_lock(&ipmb_dev->file_mutex); 1868c2ecf20Sopenharmony_ci poll_wait(file, &ipmb_dev->wait_queue, wait); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (atomic_read(&ipmb_dev->request_queue_len)) 1898c2ecf20Sopenharmony_ci mask |= EPOLLIN; 1908c2ecf20Sopenharmony_ci mutex_unlock(&ipmb_dev->file_mutex); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return mask; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct file_operations ipmb_fops = { 1968c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1978c2ecf20Sopenharmony_ci .read = ipmb_read, 1988c2ecf20Sopenharmony_ci .write = ipmb_write, 1998c2ecf20Sopenharmony_ci .poll = ipmb_poll, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* Called with ipmb_dev->lock held. */ 2038c2ecf20Sopenharmony_cistatic void ipmb_handle_request(struct ipmb_dev *ipmb_dev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct ipmb_request_elem *queue_elem; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (atomic_read(&ipmb_dev->request_queue_len) >= 2088c2ecf20Sopenharmony_ci REQUEST_QUEUE_MAX_LEN) 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC); 2128c2ecf20Sopenharmony_ci if (!queue_elem) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci memcpy(&queue_elem->request, &ipmb_dev->request, 2168c2ecf20Sopenharmony_ci sizeof(struct ipmb_msg)); 2178c2ecf20Sopenharmony_ci list_add(&queue_elem->list, &ipmb_dev->request_queue); 2188c2ecf20Sopenharmony_ci atomic_inc(&ipmb_dev->request_queue_len); 2198c2ecf20Sopenharmony_ci wake_up_all(&ipmb_dev->wait_queue); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci /* The 8 lsb of the sum is 0 when the checksum is valid */ 2258c2ecf20Sopenharmony_ci return (rs_sa + ipmb_dev->request.netfn_rs_lun + 2268c2ecf20Sopenharmony_ci ipmb_dev->request.checksum1); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* 2308c2ecf20Sopenharmony_ci * Verify if message has proper ipmb header with minimum length 2318c2ecf20Sopenharmony_ci * and correct checksum byte. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) && 2368c2ecf20Sopenharmony_ci (!ipmb_verify_checksum1(ipmb_dev, rs_sa))) 2378c2ecf20Sopenharmony_ci return true; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return false; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * The IPMB protocol only supports I2C Writes so there is no need 2448c2ecf20Sopenharmony_ci * to support I2C_SLAVE_READ* events. 2458c2ecf20Sopenharmony_ci * This i2c callback function only monitors IPMB request messages 2468c2ecf20Sopenharmony_ci * and adds them in a queue, so that they can be handled by 2478c2ecf20Sopenharmony_ci * receive_ipmb_request. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistatic int ipmb_slave_cb(struct i2c_client *client, 2508c2ecf20Sopenharmony_ci enum i2c_slave_event event, u8 *val) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); 2538c2ecf20Sopenharmony_ci u8 *buf = (u8 *)&ipmb_dev->request; 2548c2ecf20Sopenharmony_ci unsigned long flags; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipmb_dev->lock, flags); 2578c2ecf20Sopenharmony_ci switch (event) { 2588c2ecf20Sopenharmony_ci case I2C_SLAVE_WRITE_REQUESTED: 2598c2ecf20Sopenharmony_ci memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request)); 2608c2ecf20Sopenharmony_ci ipmb_dev->msg_idx = 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * At index 0, ipmb_msg stores the length of msg, 2648c2ecf20Sopenharmony_ci * skip it for now. 2658c2ecf20Sopenharmony_ci * The len will be populated once the whole 2668c2ecf20Sopenharmony_ci * buf is populated. 2678c2ecf20Sopenharmony_ci * 2688c2ecf20Sopenharmony_ci * The I2C bus driver's responsibility is to pass the 2698c2ecf20Sopenharmony_ci * data bytes to the backend driver; it does not 2708c2ecf20Sopenharmony_ci * forward the i2c slave address. 2718c2ecf20Sopenharmony_ci * Since the first byte in the IPMB message is the 2728c2ecf20Sopenharmony_ci * address of the responder, it is the responsibility 2738c2ecf20Sopenharmony_ci * of the IPMB driver to format the message properly. 2748c2ecf20Sopenharmony_ci * So this driver prepends the address of the responder 2758c2ecf20Sopenharmony_ci * to the received i2c data before the request message 2768c2ecf20Sopenharmony_ci * is handled in userland. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci case I2C_SLAVE_WRITE_RECEIVED: 2828c2ecf20Sopenharmony_ci if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg) - 1) 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci buf[++ipmb_dev->msg_idx] = *val; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci case I2C_SLAVE_STOP: 2898c2ecf20Sopenharmony_ci ipmb_dev->request.len = ipmb_dev->msg_idx; 2908c2ecf20Sopenharmony_ci if (is_ipmb_msg(ipmb_dev, GET_8BIT_ADDR(client->addr))) 2918c2ecf20Sopenharmony_ci ipmb_handle_request(ipmb_dev); 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci default: 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipmb_dev->lock, flags); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int ipmb_probe(struct i2c_client *client, 3038c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct ipmb_dev *ipmb_dev; 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ipmb_dev = devm_kzalloc(&client->dev, sizeof(*ipmb_dev), 3098c2ecf20Sopenharmony_ci GFP_KERNEL); 3108c2ecf20Sopenharmony_ci if (!ipmb_dev) 3118c2ecf20Sopenharmony_ci return -ENOMEM; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci spin_lock_init(&ipmb_dev->lock); 3148c2ecf20Sopenharmony_ci init_waitqueue_head(&ipmb_dev->wait_queue); 3158c2ecf20Sopenharmony_ci atomic_set(&ipmb_dev->request_queue_len, 0); 3168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ipmb_dev->request_queue); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci mutex_init(&ipmb_dev->file_mutex); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ipmb_dev->miscdev.minor = MISC_DYNAMIC_MINOR; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL, 3238c2ecf20Sopenharmony_ci "%s%d", "ipmb-", 3248c2ecf20Sopenharmony_ci client->adapter->nr); 3258c2ecf20Sopenharmony_ci ipmb_dev->miscdev.fops = &ipmb_fops; 3268c2ecf20Sopenharmony_ci ipmb_dev->miscdev.parent = &client->dev; 3278c2ecf20Sopenharmony_ci ret = misc_register(&ipmb_dev->miscdev); 3288c2ecf20Sopenharmony_ci if (ret) 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ipmb_dev->is_i2c_protocol 3328c2ecf20Sopenharmony_ci = device_property_read_bool(&client->dev, "i2c-protocol"); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ipmb_dev->client = client; 3358c2ecf20Sopenharmony_ci i2c_set_clientdata(client, ipmb_dev); 3368c2ecf20Sopenharmony_ci ret = i2c_slave_register(client, ipmb_slave_cb); 3378c2ecf20Sopenharmony_ci if (ret) { 3388c2ecf20Sopenharmony_ci misc_deregister(&ipmb_dev->miscdev); 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int ipmb_remove(struct i2c_client *client) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci i2c_slave_unregister(client); 3508c2ecf20Sopenharmony_ci misc_deregister(&ipmb_dev->miscdev); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic const struct i2c_device_id ipmb_id[] = { 3568c2ecf20Sopenharmony_ci { "ipmb-dev", 0 }, 3578c2ecf20Sopenharmony_ci {}, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ipmb_id); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic const struct acpi_device_id acpi_ipmb_id[] = { 3628c2ecf20Sopenharmony_ci { "IPMB0001", 0 }, 3638c2ecf20Sopenharmony_ci {}, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, acpi_ipmb_id); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic struct i2c_driver ipmb_driver = { 3688c2ecf20Sopenharmony_ci .driver = { 3698c2ecf20Sopenharmony_ci .name = "ipmb-dev", 3708c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(acpi_ipmb_id), 3718c2ecf20Sopenharmony_ci }, 3728c2ecf20Sopenharmony_ci .probe = ipmb_probe, 3738c2ecf20Sopenharmony_ci .remove = ipmb_remove, 3748c2ecf20Sopenharmony_ci .id_table = ipmb_id, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_cimodule_i2c_driver(ipmb_driver); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mellanox Technologies"); 3798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPMB driver"); 3808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 381