18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the Diolan DLN-2 USB-I2C adapter 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Derived from: 88c2ecf20Sopenharmony_ci * i2c-diolan-u2c.c 98c2ecf20Sopenharmony_ci * Copyright (c) 2010-2011 Ericsson AB 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/dln2.h> 198c2ecf20Sopenharmony_ci#include <linux/acpi.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DLN2_I2C_MODULE_ID 0x03 228c2ecf20Sopenharmony_ci#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* I2C commands */ 258c2ecf20Sopenharmony_ci#define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00) 268c2ecf20Sopenharmony_ci#define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01) 278c2ecf20Sopenharmony_ci#define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02) 288c2ecf20Sopenharmony_ci#define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03) 298c2ecf20Sopenharmony_ci#define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06) 308c2ecf20Sopenharmony_ci#define DLN2_I2C_READ DLN2_I2C_CMD(0x07) 318c2ecf20Sopenharmony_ci#define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08) 328c2ecf20Sopenharmony_ci#define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09) 338c2ecf20Sopenharmony_ci#define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A) 348c2ecf20Sopenharmony_ci#define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B) 358c2ecf20Sopenharmony_ci#define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C) 368c2ecf20Sopenharmony_ci#define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D) 378c2ecf20Sopenharmony_ci#define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define DLN2_I2C_MAX_XFER_SIZE 256 408c2ecf20Sopenharmony_ci#define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct dln2_i2c { 438c2ecf20Sopenharmony_ci struct platform_device *pdev; 448c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 458c2ecf20Sopenharmony_ci u8 port; 468c2ecf20Sopenharmony_ci /* 478c2ecf20Sopenharmony_ci * Buffer to hold the packet for read or write transfers. One is enough 488c2ecf20Sopenharmony_ci * since we can't have multiple transfers in parallel on the i2c bus. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci void *buf; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci u16 cmd; 568c2ecf20Sopenharmony_ci struct { 578c2ecf20Sopenharmony_ci u8 port; 588c2ecf20Sopenharmony_ci } tx; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci tx.port = dln2->port; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (enable) 638c2ecf20Sopenharmony_ci cmd = DLN2_I2C_ENABLE; 648c2ecf20Sopenharmony_ci else 658c2ecf20Sopenharmony_ci cmd = DLN2_I2C_DISABLE; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, 718c2ecf20Sopenharmony_ci u8 *data, u16 data_len) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int ret; 748c2ecf20Sopenharmony_ci struct { 758c2ecf20Sopenharmony_ci u8 port; 768c2ecf20Sopenharmony_ci u8 addr; 778c2ecf20Sopenharmony_ci u8 mem_addr_len; 788c2ecf20Sopenharmony_ci __le32 mem_addr; 798c2ecf20Sopenharmony_ci __le16 buf_len; 808c2ecf20Sopenharmony_ci u8 buf[DLN2_I2C_MAX_XFER_SIZE]; 818c2ecf20Sopenharmony_ci } __packed *tx = dln2->buf; 828c2ecf20Sopenharmony_ci unsigned len; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci tx->port = dln2->port; 878c2ecf20Sopenharmony_ci tx->addr = addr; 888c2ecf20Sopenharmony_ci tx->mem_addr_len = 0; 898c2ecf20Sopenharmony_ci tx->mem_addr = 0; 908c2ecf20Sopenharmony_ci tx->buf_len = cpu_to_le16(data_len); 918c2ecf20Sopenharmony_ci memcpy(tx->buf, data, data_len); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; 948c2ecf20Sopenharmony_ci ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); 958c2ecf20Sopenharmony_ci if (ret < 0) 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return data_len; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, 1028c2ecf20Sopenharmony_ci u16 data_len) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci struct { 1068c2ecf20Sopenharmony_ci u8 port; 1078c2ecf20Sopenharmony_ci u8 addr; 1088c2ecf20Sopenharmony_ci u8 mem_addr_len; 1098c2ecf20Sopenharmony_ci __le32 mem_addr; 1108c2ecf20Sopenharmony_ci __le16 buf_len; 1118c2ecf20Sopenharmony_ci } __packed tx; 1128c2ecf20Sopenharmony_ci struct { 1138c2ecf20Sopenharmony_ci __le16 buf_len; 1148c2ecf20Sopenharmony_ci u8 buf[DLN2_I2C_MAX_XFER_SIZE]; 1158c2ecf20Sopenharmony_ci } __packed *rx = dln2->buf; 1168c2ecf20Sopenharmony_ci unsigned rx_len = sizeof(*rx); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci tx.port = dln2->port; 1218c2ecf20Sopenharmony_ci tx.addr = addr; 1228c2ecf20Sopenharmony_ci tx.mem_addr_len = 0; 1238c2ecf20Sopenharmony_ci tx.mem_addr = 0; 1248c2ecf20Sopenharmony_ci tx.buf_len = cpu_to_le16(data_len); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), 1278c2ecf20Sopenharmony_ci rx, &rx_len); 1288c2ecf20Sopenharmony_ci if (ret < 0) 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci if (rx_len < sizeof(rx->buf_len) + data_len) 1318c2ecf20Sopenharmony_ci return -EPROTO; 1328c2ecf20Sopenharmony_ci if (le16_to_cpu(rx->buf_len) != data_len) 1338c2ecf20Sopenharmony_ci return -EPROTO; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci memcpy(data, rx->buf, data_len); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return data_len; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int dln2_i2c_xfer(struct i2c_adapter *adapter, 1418c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); 1448c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 1458c2ecf20Sopenharmony_ci int i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1488c2ecf20Sopenharmony_ci int ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (pmsg->flags & I2C_M_RD) { 1538c2ecf20Sopenharmony_ci ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, 1548c2ecf20Sopenharmony_ci pmsg->len); 1558c2ecf20Sopenharmony_ci if (ret < 0) 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci pmsg->len = ret; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, 1618c2ecf20Sopenharmony_ci pmsg->len); 1628c2ecf20Sopenharmony_ci if (ret != pmsg->len) 1638c2ecf20Sopenharmony_ci return -EPROTO; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return num; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic u32 dln2_i2c_func(struct i2c_adapter *a) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | 1738c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | 1748c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct i2c_algorithm dln2_i2c_usb_algorithm = { 1788c2ecf20Sopenharmony_ci .master_xfer = dln2_i2c_xfer, 1798c2ecf20Sopenharmony_ci .functionality = dln2_i2c_func, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks dln2_i2c_quirks = { 1838c2ecf20Sopenharmony_ci .max_read_len = DLN2_I2C_MAX_XFER_SIZE, 1848c2ecf20Sopenharmony_ci .max_write_len = DLN2_I2C_MAX_XFER_SIZE, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int dln2_i2c_probe(struct platform_device *pdev) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci int ret; 1908c2ecf20Sopenharmony_ci struct dln2_i2c *dln2; 1918c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1928c2ecf20Sopenharmony_ci struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!dln2) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); 1998c2ecf20Sopenharmony_ci if (!dln2->buf) 2008c2ecf20Sopenharmony_ci return -ENOMEM; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci dln2->pdev = pdev; 2038c2ecf20Sopenharmony_ci dln2->port = pdata->port; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* setup i2c adapter description */ 2068c2ecf20Sopenharmony_ci dln2->adapter.owner = THIS_MODULE; 2078c2ecf20Sopenharmony_ci dln2->adapter.class = I2C_CLASS_HWMON; 2088c2ecf20Sopenharmony_ci dln2->adapter.algo = &dln2_i2c_usb_algorithm; 2098c2ecf20Sopenharmony_ci dln2->adapter.quirks = &dln2_i2c_quirks; 2108c2ecf20Sopenharmony_ci dln2->adapter.dev.parent = dev; 2118c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(&dln2->adapter.dev, ACPI_COMPANION(&pdev->dev)); 2128c2ecf20Sopenharmony_ci dln2->adapter.dev.of_node = dev->of_node; 2138c2ecf20Sopenharmony_ci i2c_set_adapdata(&dln2->adapter, dln2); 2148c2ecf20Sopenharmony_ci snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", 2158c2ecf20Sopenharmony_ci "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dln2); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* initialize the i2c interface */ 2208c2ecf20Sopenharmony_ci ret = dln2_i2c_enable(dln2, true); 2218c2ecf20Sopenharmony_ci if (ret < 0) { 2228c2ecf20Sopenharmony_ci dev_err(dev, "failed to initialize adapter: %d\n", ret); 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* and finally attach to i2c layer */ 2278c2ecf20Sopenharmony_ci ret = i2c_add_adapter(&dln2->adapter); 2288c2ecf20Sopenharmony_ci if (ret < 0) 2298c2ecf20Sopenharmony_ci goto out_disable; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciout_disable: 2348c2ecf20Sopenharmony_ci dln2_i2c_enable(dln2, false); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int dln2_i2c_remove(struct platform_device *pdev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct dln2_i2c *dln2 = platform_get_drvdata(pdev); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci i2c_del_adapter(&dln2->adapter); 2448c2ecf20Sopenharmony_ci dln2_i2c_enable(dln2, false); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct platform_driver dln2_i2c_driver = { 2508c2ecf20Sopenharmony_ci .driver.name = "dln2-i2c", 2518c2ecf20Sopenharmony_ci .probe = dln2_i2c_probe, 2528c2ecf20Sopenharmony_ci .remove = dln2_i2c_remove, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cimodule_platform_driver(dln2_i2c_driver); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); 2588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface"); 2598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2608c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:dln2-i2c"); 261