18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Michael Shych <michaels@mellanox.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 68c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 98c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 108c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 118c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 128c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 138c2ecf20Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its 148c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived from 158c2ecf20Sopenharmony_ci * this software without specific prior written permission. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 188c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free 198c2ecf20Sopenharmony_ci * Software Foundation. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 228c2ecf20Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 238c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 248c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 258c2ecf20Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 268c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 278c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 288c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 298c2ecf20Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 308c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 318c2ecf20Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/delay.h> 358c2ecf20Sopenharmony_ci#include <linux/i2c.h> 368c2ecf20Sopenharmony_ci#include <linux/init.h> 378c2ecf20Sopenharmony_ci#include <linux/io.h> 388c2ecf20Sopenharmony_ci#include <linux/kernel.h> 398c2ecf20Sopenharmony_ci#include <linux/module.h> 408c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* General defines */ 438c2ecf20Sopenharmony_ci#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000 448c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld" 458c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD) 468c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_BUS_NUM 1 478c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_DATA_REG_SZ 36 488c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_DATA_SZ_BIT BIT(5) 498c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_DATA_SZ_MASK GENMASK(6, 5) 508c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_SMBUS_BLK_BIT BIT(7) 518c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_MAX_ADDR_LEN 4 528c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_RETR_NUM 2 538c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_XFER_TO 500000 /* usec */ 548c2ecf20Sopenharmony_ci#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* LPC I2C registers */ 578c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0 588c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_CTRL_REG 0x1 598c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4 608c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5 618c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_CMD_REG 0x6 628c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7 638c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8 648c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_STATUS_REG 0x9 658c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_DATA_REG 0xa 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* LPC I2C masks and parametres */ 688c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1 698c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_TRANS_END 0x1 708c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_STATUS_NACK 0x10 718c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_NO_IND 0 728c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_ACK_IND 1 738c2ecf20Sopenharmony_ci#define MLXCPLD_LPCI2C_NACK_IND 2 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct mlxcpld_i2c_curr_xfer { 768c2ecf20Sopenharmony_ci u8 cmd; 778c2ecf20Sopenharmony_ci u8 addr_width; 788c2ecf20Sopenharmony_ci u8 data_len; 798c2ecf20Sopenharmony_ci u8 msg_num; 808c2ecf20Sopenharmony_ci struct i2c_msg *msg; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct mlxcpld_i2c_priv { 848c2ecf20Sopenharmony_ci struct i2c_adapter adap; 858c2ecf20Sopenharmony_ci u32 base_addr; 868c2ecf20Sopenharmony_ci struct mutex lock; 878c2ecf20Sopenharmony_ci struct mlxcpld_i2c_curr_xfer xfer; 888c2ecf20Sopenharmony_ci struct device *dev; 898c2ecf20Sopenharmony_ci bool smbus_block; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int i; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci for (i = 0; i < len - len % 4; i += 4) 978c2ecf20Sopenharmony_ci outl(*(u32 *)(data + i), addr + i); 988c2ecf20Sopenharmony_ci for (; i < len; ++i) 998c2ecf20Sopenharmony_ci outb(*(data + i), addr + i); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_lpc_read_buf(u8 *data, u8 len, u32 addr) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < len - len % 4; i += 4) 1078c2ecf20Sopenharmony_ci *(u32 *)(data + i) = inl(addr + i); 1088c2ecf20Sopenharmony_ci for (; i < len; ++i) 1098c2ecf20Sopenharmony_ci *(data + i) = inb(addr + i); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_read_comm(struct mlxcpld_i2c_priv *priv, u8 offs, 1138c2ecf20Sopenharmony_ci u8 *data, u8 datalen) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u32 addr = priv->base_addr + offs; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci switch (datalen) { 1188c2ecf20Sopenharmony_ci case 1: 1198c2ecf20Sopenharmony_ci *(data) = inb(addr); 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case 2: 1228c2ecf20Sopenharmony_ci *((u16 *)data) = inw(addr); 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case 3: 1258c2ecf20Sopenharmony_ci *((u16 *)data) = inw(addr); 1268c2ecf20Sopenharmony_ci *(data + 2) = inb(addr + 2); 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case 4: 1298c2ecf20Sopenharmony_ci *((u32 *)data) = inl(addr); 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci default: 1328c2ecf20Sopenharmony_ci mlxcpld_i2c_lpc_read_buf(data, datalen, addr); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_write_comm(struct mlxcpld_i2c_priv *priv, u8 offs, 1388c2ecf20Sopenharmony_ci u8 *data, u8 datalen) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u32 addr = priv->base_addr + offs; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci switch (datalen) { 1438c2ecf20Sopenharmony_ci case 1: 1448c2ecf20Sopenharmony_ci outb(*(data), addr); 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case 2: 1478c2ecf20Sopenharmony_ci outw(*((u16 *)data), addr); 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 3: 1508c2ecf20Sopenharmony_ci outw(*((u16 *)data), addr); 1518c2ecf20Sopenharmony_ci outb(*(data + 2), addr + 2); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case 4: 1548c2ecf20Sopenharmony_ci outl(*((u32 *)data), addr); 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci mlxcpld_i2c_lpc_write_buf(data, datalen, addr); 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * Check validity of received i2c messages parameters. 1648c2ecf20Sopenharmony_ci * Returns 0 if OK, other - in case of invalid parameters. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_check_msg_params(struct mlxcpld_i2c_priv *priv, 1678c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int i; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!num) { 1728c2ecf20Sopenharmony_ci dev_err(priv->dev, "Incorrect 0 num of messages\n"); 1738c2ecf20Sopenharmony_ci return -EINVAL; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (unlikely(msgs[0].addr > 0x7f)) { 1778c2ecf20Sopenharmony_ci dev_err(priv->dev, "Invalid address 0x%03x\n", 1788c2ecf20Sopenharmony_ci msgs[0].addr); 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; i < num; ++i) { 1838c2ecf20Sopenharmony_ci if (unlikely(!msgs[i].buf)) { 1848c2ecf20Sopenharmony_ci dev_err(priv->dev, "Invalid buf in msg[%d]\n", 1858c2ecf20Sopenharmony_ci i); 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci if (unlikely(msgs[0].addr != msgs[i].addr)) { 1898c2ecf20Sopenharmony_ci dev_err(priv->dev, "Invalid addr in msg[%d]\n", 1908c2ecf20Sopenharmony_ci i); 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Check if transfer is completed and status of operation. 2008c2ecf20Sopenharmony_ci * Returns 0 - transfer completed (both ACK or NACK), 2018c2ecf20Sopenharmony_ci * negative - transfer isn't finished. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci u8 val; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (val & MLXCPLD_LPCI2C_TRANS_END) { 2108c2ecf20Sopenharmony_ci if (val & MLXCPLD_LPCI2C_STATUS_NACK) 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * The slave is unable to accept the data. No such 2138c2ecf20Sopenharmony_ci * slave, command not understood, or unable to accept 2148c2ecf20Sopenharmony_ci * any more data. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci *status = MLXCPLD_LPCI2C_NACK_IND; 2178c2ecf20Sopenharmony_ci else 2188c2ecf20Sopenharmony_ci *status = MLXCPLD_LPCI2C_ACK_IND; 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci *status = MLXCPLD_LPCI2C_NO_IND; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return -EIO; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv, 2278c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num, 2288c2ecf20Sopenharmony_ci u8 comm_len) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci priv->xfer.msg = msgs; 2318c2ecf20Sopenharmony_ci priv->xfer.msg_num = num; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * All upper layers currently are never use transfer with more than 2358c2ecf20Sopenharmony_ci * 2 messages. Actually, it's also not so relevant in Mellanox systems 2368c2ecf20Sopenharmony_ci * because of HW limitation. Max size of transfer is not more than 32 2378c2ecf20Sopenharmony_ci * or 68 bytes in the current x86 LPCI2C bridge. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (priv->xfer.cmd == I2C_M_RD && comm_len != msgs[0].len) { 2428c2ecf20Sopenharmony_ci priv->xfer.addr_width = msgs[0].len; 2438c2ecf20Sopenharmony_ci priv->xfer.data_len = comm_len - priv->xfer.addr_width; 2448c2ecf20Sopenharmony_ci } else { 2458c2ecf20Sopenharmony_ci priv->xfer.addr_width = 0; 2468c2ecf20Sopenharmony_ci priv->xfer.data_len = comm_len; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* Reset CPLD LPCI2C block */ 2518c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_reset(struct mlxcpld_i2c_priv *priv) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u8 val; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1); 2588c2ecf20Sopenharmony_ci val &= ~MLXCPLD_LPCI2C_RST_SEL_MASK; 2598c2ecf20Sopenharmony_ci mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* Make sure the CPLD is ready to start transmitting. */ 2658c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_check_busy(struct mlxcpld_i2c_priv *priv) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci u8 val; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (val & MLXCPLD_LPCI2C_TRANS_END) 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return -EIO; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci int timeout = 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci do { 2828c2ecf20Sopenharmony_ci if (!mlxcpld_i2c_check_busy(priv)) 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME); 2858c2ecf20Sopenharmony_ci timeout += MLXCPLD_I2C_POLL_TIME; 2868c2ecf20Sopenharmony_ci } while (timeout <= MLXCPLD_I2C_XFER_TO); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (timeout > MLXCPLD_I2C_XFER_TO) 2898c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * Wait for master transfer to complete. 2968c2ecf20Sopenharmony_ci * It puts current process to sleep until we get interrupt or timeout expires. 2978c2ecf20Sopenharmony_ci * Returns the number of transferred or read bytes or error (<0). 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci int status, i, timeout = 0; 3028c2ecf20Sopenharmony_ci u8 datalen, val; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci do { 3058c2ecf20Sopenharmony_ci usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME); 3068c2ecf20Sopenharmony_ci if (!mlxcpld_i2c_check_status(priv, &status)) 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci timeout += MLXCPLD_I2C_POLL_TIME; 3098c2ecf20Sopenharmony_ci } while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci switch (status) { 3128c2ecf20Sopenharmony_ci case MLXCPLD_LPCI2C_NO_IND: 3138c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci case MLXCPLD_LPCI2C_ACK_IND: 3168c2ecf20Sopenharmony_ci if (priv->xfer.cmd != I2C_M_RD) 3178c2ecf20Sopenharmony_ci return (priv->xfer.addr_width + priv->xfer.data_len); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (priv->xfer.msg_num == 1) 3208c2ecf20Sopenharmony_ci i = 0; 3218c2ecf20Sopenharmony_ci else 3228c2ecf20Sopenharmony_ci i = 1; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!priv->xfer.msg[i].buf) 3258c2ecf20Sopenharmony_ci return -EINVAL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * Actual read data len will be always the same as 3298c2ecf20Sopenharmony_ci * requested len. 0xff (line pull-up) will be returned 3308c2ecf20Sopenharmony_ci * if slave has no data to return. Thus don't read 3318c2ecf20Sopenharmony_ci * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD. Only in case of 3328c2ecf20Sopenharmony_ci * SMBus block read transaction data len can be different, 3338c2ecf20Sopenharmony_ci * check this case. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, 3368c2ecf20Sopenharmony_ci 1); 3378c2ecf20Sopenharmony_ci if (priv->smbus_block && (val & MLXCPLD_I2C_SMBUS_BLK_BIT)) { 3388c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG, 3398c2ecf20Sopenharmony_ci &datalen, 1); 3408c2ecf20Sopenharmony_ci if (unlikely(datalen > I2C_SMBUS_BLOCK_MAX)) { 3418c2ecf20Sopenharmony_ci dev_err(priv->dev, "Incorrect smbus block read message len\n"); 3428c2ecf20Sopenharmony_ci return -EPROTO; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci datalen = priv->xfer.data_len; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG, 3498c2ecf20Sopenharmony_ci priv->xfer.msg[i].buf, datalen); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return datalen; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci case MLXCPLD_LPCI2C_NACK_IND: 3548c2ecf20Sopenharmony_ci return -ENXIO; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci default: 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int i, len = 0; 3648c2ecf20Sopenharmony_ci u8 cmd, val; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG, 3678c2ecf20Sopenharmony_ci &priv->xfer.data_len, 1); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci val = priv->xfer.addr_width; 3708c2ecf20Sopenharmony_ci /* Notify HW about SMBus block read transaction */ 3718c2ecf20Sopenharmony_ci if (priv->smbus_block && priv->xfer.msg_num >= 2 && 3728c2ecf20Sopenharmony_ci priv->xfer.msg[1].len == 1 && 3738c2ecf20Sopenharmony_ci (priv->xfer.msg[1].flags & I2C_M_RECV_LEN) && 3748c2ecf20Sopenharmony_ci (priv->xfer.msg[1].flags & I2C_M_RD)) 3758c2ecf20Sopenharmony_ci val |= MLXCPLD_I2C_SMBUS_BLK_BIT; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG, &val, 1); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci for (i = 0; i < priv->xfer.msg_num; i++) { 3808c2ecf20Sopenharmony_ci if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) { 3818c2ecf20Sopenharmony_ci /* Don't write to CPLD buffer in read transaction */ 3828c2ecf20Sopenharmony_ci mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_DATA_REG + 3838c2ecf20Sopenharmony_ci len, priv->xfer.msg[i].buf, 3848c2ecf20Sopenharmony_ci priv->xfer.msg[i].len); 3858c2ecf20Sopenharmony_ci len += priv->xfer.msg[i].len; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Set target slave address with command for master transfer. 3918c2ecf20Sopenharmony_ci * It should be latest executed function before CPLD transaction. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci cmd = (priv->xfer.msg[0].addr << 1) | priv->xfer.cmd; 3948c2ecf20Sopenharmony_ci mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CMD_REG, &cmd, 1); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * Generic lpc-i2c transfer. 3998c2ecf20Sopenharmony_ci * Returns the number of processed messages or error (<0). 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 4028c2ecf20Sopenharmony_ci int num) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap); 4058c2ecf20Sopenharmony_ci u8 comm_len = 0; 4068c2ecf20Sopenharmony_ci int i, err; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci err = mlxcpld_i2c_check_msg_params(priv, msgs, num); 4098c2ecf20Sopenharmony_ci if (err) { 4108c2ecf20Sopenharmony_ci dev_err(priv->dev, "Incorrect message\n"); 4118c2ecf20Sopenharmony_ci return err; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci for (i = 0; i < num; ++i) 4158c2ecf20Sopenharmony_ci comm_len += msgs[i].len; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Check bus state */ 4188c2ecf20Sopenharmony_ci if (mlxcpld_i2c_wait_for_free(priv)) { 4198c2ecf20Sopenharmony_ci dev_err(priv->dev, "LPCI2C bridge is busy\n"); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * Usually it means something serious has happened. 4238c2ecf20Sopenharmony_ci * We can not have unfinished previous transfer 4248c2ecf20Sopenharmony_ci * so it doesn't make any sense to try to stop it. 4258c2ecf20Sopenharmony_ci * Probably we were not able to recover from the 4268c2ecf20Sopenharmony_ci * previous error. 4278c2ecf20Sopenharmony_ci * The only reasonable thing - is soft reset. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_ci mlxcpld_i2c_reset(priv); 4308c2ecf20Sopenharmony_ci if (mlxcpld_i2c_check_busy(priv)) { 4318c2ecf20Sopenharmony_ci dev_err(priv->dev, "LPCI2C bridge is busy after reset\n"); 4328c2ecf20Sopenharmony_ci return -EIO; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci mlxcpld_i2c_set_transf_data(priv, msgs, num, comm_len); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Do real transfer. Can't fail */ 4418c2ecf20Sopenharmony_ci mlxcpld_i2c_xfer_msg(priv); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* Wait for transaction complete */ 4448c2ecf20Sopenharmony_ci err = mlxcpld_i2c_wait_for_tc(priv); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return err < 0 ? err : num; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic u32 mlxcpld_i2c_func(struct i2c_adapter *adap) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (priv->smbus_block) 4568c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | 4578c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA; 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | 4608c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic const struct i2c_algorithm mlxcpld_i2c_algo = { 4648c2ecf20Sopenharmony_ci .master_xfer = mlxcpld_i2c_xfer, 4658c2ecf20Sopenharmony_ci .functionality = mlxcpld_i2c_func 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks mlxcpld_i2c_quirks = { 4698c2ecf20Sopenharmony_ci .flags = I2C_AQ_COMB_WRITE_THEN_READ, 4708c2ecf20Sopenharmony_ci .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN, 4718c2ecf20Sopenharmony_ci .max_write_len = MLXCPLD_I2C_DATA_REG_SZ, 4728c2ecf20Sopenharmony_ci .max_comb_1st_msg_len = 4, 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = { 4768c2ecf20Sopenharmony_ci .flags = I2C_AQ_COMB_WRITE_THEN_READ, 4778c2ecf20Sopenharmony_ci .max_read_len = MLXCPLD_I2C_DATA_REG_SZ * 2 - MLXCPLD_I2C_MAX_ADDR_LEN, 4788c2ecf20Sopenharmony_ci .max_write_len = MLXCPLD_I2C_DATA_REG_SZ * 2, 4798c2ecf20Sopenharmony_ci .max_comb_1st_msg_len = 4, 4808c2ecf20Sopenharmony_ci}; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic struct i2c_adapter mlxcpld_i2c_adapter = { 4838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4848c2ecf20Sopenharmony_ci .name = "i2c-mlxcpld", 4858c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 4868c2ecf20Sopenharmony_ci .algo = &mlxcpld_i2c_algo, 4878c2ecf20Sopenharmony_ci .quirks = &mlxcpld_i2c_quirks, 4888c2ecf20Sopenharmony_ci .retries = MLXCPLD_I2C_RETR_NUM, 4898c2ecf20Sopenharmony_ci .nr = MLXCPLD_I2C_BUS_NUM, 4908c2ecf20Sopenharmony_ci}; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_probe(struct platform_device *pdev) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct mlxcpld_i2c_priv *priv; 4958c2ecf20Sopenharmony_ci int err; 4968c2ecf20Sopenharmony_ci u8 val; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 4998c2ecf20Sopenharmony_ci if (!priv) 5008c2ecf20Sopenharmony_ci return -ENOMEM; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci mutex_init(&priv->lock); 5038c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci priv->dev = &pdev->dev; 5068c2ecf20Sopenharmony_ci priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Register with i2c layer */ 5098c2ecf20Sopenharmony_ci mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO); 5108c2ecf20Sopenharmony_ci /* Read capability register */ 5118c2ecf20Sopenharmony_ci mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CPBLTY_REG, &val, 1); 5128c2ecf20Sopenharmony_ci /* Check support for extended transaction length */ 5138c2ecf20Sopenharmony_ci if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_SZ_BIT) 5148c2ecf20Sopenharmony_ci mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext; 5158c2ecf20Sopenharmony_ci /* Check support for smbus block transaction */ 5168c2ecf20Sopenharmony_ci if (val & MLXCPLD_I2C_SMBUS_BLK_BIT) 5178c2ecf20Sopenharmony_ci priv->smbus_block = true; 5188c2ecf20Sopenharmony_ci if (pdev->id >= -1) 5198c2ecf20Sopenharmony_ci mlxcpld_i2c_adapter.nr = pdev->id; 5208c2ecf20Sopenharmony_ci priv->adap = mlxcpld_i2c_adapter; 5218c2ecf20Sopenharmony_ci priv->adap.dev.parent = &pdev->dev; 5228c2ecf20Sopenharmony_ci i2c_set_adapdata(&priv->adap, priv); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci err = i2c_add_numbered_adapter(&priv->adap); 5258c2ecf20Sopenharmony_ci if (err) 5268c2ecf20Sopenharmony_ci mutex_destroy(&priv->lock); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return err; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int mlxcpld_i2c_remove(struct platform_device *pdev) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct mlxcpld_i2c_priv *priv = platform_get_drvdata(pdev); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci i2c_del_adapter(&priv->adap); 5368c2ecf20Sopenharmony_ci mutex_destroy(&priv->lock); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic struct platform_driver mlxcpld_i2c_driver = { 5428c2ecf20Sopenharmony_ci .probe = mlxcpld_i2c_probe, 5438c2ecf20Sopenharmony_ci .remove = mlxcpld_i2c_remove, 5448c2ecf20Sopenharmony_ci .driver = { 5458c2ecf20Sopenharmony_ci .name = MLXCPLD_I2C_DEVICE_NAME, 5468c2ecf20Sopenharmony_ci }, 5478c2ecf20Sopenharmony_ci}; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cimodule_platform_driver(mlxcpld_i2c_driver); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Shych <michaels@mellanox.com>"); 5528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mellanox I2C-CPLD controller driver"); 5538c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 5548c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:i2c-mlxcpld"); 555