18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Nano River Technologies viperboard i2c master driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2012 by Lemonage GmbH 68c2ecf20Sopenharmony_ci * Author: Lars Poeschel <poeschel@lemonage.de> 78c2ecf20Sopenharmony_ci * All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/usb.h> 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/mfd/viperboard.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct vprbrd_i2c { 248c2ecf20Sopenharmony_ci struct i2c_adapter i2c; 258c2ecf20Sopenharmony_ci u8 bus_freq_param; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* i2c bus frequency module parameter */ 298c2ecf20Sopenharmony_cistatic u8 i2c_bus_param; 308c2ecf20Sopenharmony_cistatic unsigned int i2c_bus_freq = 100; 318c2ecf20Sopenharmony_cimodule_param(i2c_bus_freq, int, 0); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(i2c_bus_freq, 338c2ecf20Sopenharmony_ci "i2c bus frequency in khz (default is 100) valid values: 10, 100, 200, 400, 1000, 3000, 6000"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int vprbrd_i2c_status(struct i2c_adapter *i2c, 368c2ecf20Sopenharmony_ci struct vprbrd_i2c_status *status, bool prev_error) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci u16 bytes_xfer; 398c2ecf20Sopenharmony_ci int ret; 408c2ecf20Sopenharmony_ci struct vprbrd *vb = (struct vprbrd *)i2c->algo_data; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* check for protocol error */ 438c2ecf20Sopenharmony_ci bytes_xfer = sizeof(struct vprbrd_i2c_status); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), 468c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_I2C, VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, 478c2ecf20Sopenharmony_ci status, bytes_xfer, VPRBRD_USB_TIMEOUT_MS); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (ret != bytes_xfer) 508c2ecf20Sopenharmony_ci prev_error = true; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (prev_error) { 538c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "failure in usb communication\n"); 548c2ecf20Sopenharmony_ci return -EREMOTEIO; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, " status = %d\n", status->status); 588c2ecf20Sopenharmony_ci if (status->status != 0x00) { 598c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "failure: i2c protocol error\n"); 608c2ecf20Sopenharmony_ci return -EPROTO; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int vprbrd_i2c_receive(struct usb_device *usb_dev, 668c2ecf20Sopenharmony_ci struct vprbrd_i2c_read_msg *rmsg, int bytes_xfer) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int ret, bytes_actual; 698c2ecf20Sopenharmony_ci int error = 0; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* send the read request */ 728c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, 738c2ecf20Sopenharmony_ci usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), rmsg, 748c2ecf20Sopenharmony_ci sizeof(struct vprbrd_i2c_read_hdr), &bytes_actual, 758c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if ((ret < 0) 788c2ecf20Sopenharmony_ci || (bytes_actual != sizeof(struct vprbrd_i2c_read_hdr))) { 798c2ecf20Sopenharmony_ci dev_err(&usb_dev->dev, "failure transmitting usb\n"); 808c2ecf20Sopenharmony_ci error = -EREMOTEIO; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* read the actual data */ 848c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, 858c2ecf20Sopenharmony_ci usb_rcvbulkpipe(usb_dev, VPRBRD_EP_IN), rmsg, 868c2ecf20Sopenharmony_ci bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if ((ret < 0) || (bytes_xfer != bytes_actual)) { 898c2ecf20Sopenharmony_ci dev_err(&usb_dev->dev, "failure receiving usb\n"); 908c2ecf20Sopenharmony_ci error = -EREMOTEIO; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci return error; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int vprbrd_i2c_addr(struct usb_device *usb_dev, 968c2ecf20Sopenharmony_ci struct vprbrd_i2c_addr_msg *amsg) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int ret, bytes_actual; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, 1018c2ecf20Sopenharmony_ci usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), amsg, 1028c2ecf20Sopenharmony_ci sizeof(struct vprbrd_i2c_addr_msg), &bytes_actual, 1038c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if ((ret < 0) || 1068c2ecf20Sopenharmony_ci (sizeof(struct vprbrd_i2c_addr_msg) != bytes_actual)) { 1078c2ecf20Sopenharmony_ci dev_err(&usb_dev->dev, "failure transmitting usb\n"); 1088c2ecf20Sopenharmony_ci return -EREMOTEIO; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int ret; 1168c2ecf20Sopenharmony_ci u16 remain_len, len1, len2, start = 0x0000; 1178c2ecf20Sopenharmony_ci struct vprbrd_i2c_read_msg *rmsg = 1188c2ecf20Sopenharmony_ci (struct vprbrd_i2c_read_msg *)vb->buf; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci remain_len = msg->len; 1218c2ecf20Sopenharmony_ci rmsg->header.cmd = VPRBRD_I2C_CMD_READ; 1228c2ecf20Sopenharmony_ci while (remain_len > 0) { 1238c2ecf20Sopenharmony_ci rmsg->header.addr = cpu_to_le16(start + 0x4000); 1248c2ecf20Sopenharmony_ci if (remain_len <= 255) { 1258c2ecf20Sopenharmony_ci len1 = remain_len; 1268c2ecf20Sopenharmony_ci len2 = 0x00; 1278c2ecf20Sopenharmony_ci rmsg->header.len0 = remain_len; 1288c2ecf20Sopenharmony_ci rmsg->header.len1 = 0x00; 1298c2ecf20Sopenharmony_ci rmsg->header.len2 = 0x00; 1308c2ecf20Sopenharmony_ci rmsg->header.len3 = 0x00; 1318c2ecf20Sopenharmony_ci rmsg->header.len4 = 0x00; 1328c2ecf20Sopenharmony_ci rmsg->header.len5 = 0x00; 1338c2ecf20Sopenharmony_ci remain_len = 0; 1348c2ecf20Sopenharmony_ci } else if (remain_len <= 510) { 1358c2ecf20Sopenharmony_ci len1 = remain_len; 1368c2ecf20Sopenharmony_ci len2 = 0x00; 1378c2ecf20Sopenharmony_ci rmsg->header.len0 = remain_len - 255; 1388c2ecf20Sopenharmony_ci rmsg->header.len1 = 0xff; 1398c2ecf20Sopenharmony_ci rmsg->header.len2 = 0x00; 1408c2ecf20Sopenharmony_ci rmsg->header.len3 = 0x00; 1418c2ecf20Sopenharmony_ci rmsg->header.len4 = 0x00; 1428c2ecf20Sopenharmony_ci rmsg->header.len5 = 0x00; 1438c2ecf20Sopenharmony_ci remain_len = 0; 1448c2ecf20Sopenharmony_ci } else if (remain_len <= 512) { 1458c2ecf20Sopenharmony_ci len1 = remain_len; 1468c2ecf20Sopenharmony_ci len2 = 0x00; 1478c2ecf20Sopenharmony_ci rmsg->header.len0 = remain_len - 510; 1488c2ecf20Sopenharmony_ci rmsg->header.len1 = 0xff; 1498c2ecf20Sopenharmony_ci rmsg->header.len2 = 0xff; 1508c2ecf20Sopenharmony_ci rmsg->header.len3 = 0x00; 1518c2ecf20Sopenharmony_ci rmsg->header.len4 = 0x00; 1528c2ecf20Sopenharmony_ci rmsg->header.len5 = 0x00; 1538c2ecf20Sopenharmony_ci remain_len = 0; 1548c2ecf20Sopenharmony_ci } else if (remain_len <= 767) { 1558c2ecf20Sopenharmony_ci len1 = 512; 1568c2ecf20Sopenharmony_ci len2 = remain_len - 512; 1578c2ecf20Sopenharmony_ci rmsg->header.len0 = 0x02; 1588c2ecf20Sopenharmony_ci rmsg->header.len1 = 0xff; 1598c2ecf20Sopenharmony_ci rmsg->header.len2 = 0xff; 1608c2ecf20Sopenharmony_ci rmsg->header.len3 = remain_len - 512; 1618c2ecf20Sopenharmony_ci rmsg->header.len4 = 0x00; 1628c2ecf20Sopenharmony_ci rmsg->header.len5 = 0x00; 1638c2ecf20Sopenharmony_ci remain_len = 0; 1648c2ecf20Sopenharmony_ci } else if (remain_len <= 1022) { 1658c2ecf20Sopenharmony_ci len1 = 512; 1668c2ecf20Sopenharmony_ci len2 = remain_len - 512; 1678c2ecf20Sopenharmony_ci rmsg->header.len0 = 0x02; 1688c2ecf20Sopenharmony_ci rmsg->header.len1 = 0xff; 1698c2ecf20Sopenharmony_ci rmsg->header.len2 = 0xff; 1708c2ecf20Sopenharmony_ci rmsg->header.len3 = remain_len - 767; 1718c2ecf20Sopenharmony_ci rmsg->header.len4 = 0xff; 1728c2ecf20Sopenharmony_ci rmsg->header.len5 = 0x00; 1738c2ecf20Sopenharmony_ci remain_len = 0; 1748c2ecf20Sopenharmony_ci } else if (remain_len <= 1024) { 1758c2ecf20Sopenharmony_ci len1 = 512; 1768c2ecf20Sopenharmony_ci len2 = remain_len - 512; 1778c2ecf20Sopenharmony_ci rmsg->header.len0 = 0x02; 1788c2ecf20Sopenharmony_ci rmsg->header.len1 = 0xff; 1798c2ecf20Sopenharmony_ci rmsg->header.len2 = 0xff; 1808c2ecf20Sopenharmony_ci rmsg->header.len3 = remain_len - 1022; 1818c2ecf20Sopenharmony_ci rmsg->header.len4 = 0xff; 1828c2ecf20Sopenharmony_ci rmsg->header.len5 = 0xff; 1838c2ecf20Sopenharmony_ci remain_len = 0; 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci len1 = 512; 1868c2ecf20Sopenharmony_ci len2 = 512; 1878c2ecf20Sopenharmony_ci rmsg->header.len0 = 0x02; 1888c2ecf20Sopenharmony_ci rmsg->header.len1 = 0xff; 1898c2ecf20Sopenharmony_ci rmsg->header.len2 = 0xff; 1908c2ecf20Sopenharmony_ci rmsg->header.len3 = 0x02; 1918c2ecf20Sopenharmony_ci rmsg->header.len4 = 0xff; 1928c2ecf20Sopenharmony_ci rmsg->header.len5 = 0xff; 1938c2ecf20Sopenharmony_ci remain_len -= 1024; 1948c2ecf20Sopenharmony_ci start += 1024; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci rmsg->header.tf1 = cpu_to_le16(len1); 1978c2ecf20Sopenharmony_ci rmsg->header.tf2 = cpu_to_le16(len2); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* first read transfer */ 2008c2ecf20Sopenharmony_ci ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len1); 2018c2ecf20Sopenharmony_ci if (ret < 0) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci /* copy the received data */ 2048c2ecf20Sopenharmony_ci memcpy(msg->buf + start, rmsg, len1); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* second read transfer if neccessary */ 2078c2ecf20Sopenharmony_ci if (len2 > 0) { 2088c2ecf20Sopenharmony_ci ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2); 2098c2ecf20Sopenharmony_ci if (ret < 0) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci /* copy the received data */ 2128c2ecf20Sopenharmony_ci memcpy(msg->buf + start + 512, rmsg, len2); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int vprbrd_i2c_write(struct vprbrd *vb, struct i2c_msg *msg) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int ret, bytes_actual; 2218c2ecf20Sopenharmony_ci u16 remain_len, bytes_xfer, 2228c2ecf20Sopenharmony_ci start = 0x0000; 2238c2ecf20Sopenharmony_ci struct vprbrd_i2c_write_msg *wmsg = 2248c2ecf20Sopenharmony_ci (struct vprbrd_i2c_write_msg *)vb->buf; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci remain_len = msg->len; 2278c2ecf20Sopenharmony_ci wmsg->header.cmd = VPRBRD_I2C_CMD_WRITE; 2288c2ecf20Sopenharmony_ci wmsg->header.last = 0x00; 2298c2ecf20Sopenharmony_ci wmsg->header.chan = 0x00; 2308c2ecf20Sopenharmony_ci wmsg->header.spi = 0x0000; 2318c2ecf20Sopenharmony_ci while (remain_len > 0) { 2328c2ecf20Sopenharmony_ci wmsg->header.addr = cpu_to_le16(start + 0x4000); 2338c2ecf20Sopenharmony_ci if (remain_len > 503) { 2348c2ecf20Sopenharmony_ci wmsg->header.len1 = 0xff; 2358c2ecf20Sopenharmony_ci wmsg->header.len2 = 0xf8; 2368c2ecf20Sopenharmony_ci remain_len -= 503; 2378c2ecf20Sopenharmony_ci bytes_xfer = 503 + sizeof(struct vprbrd_i2c_write_hdr); 2388c2ecf20Sopenharmony_ci start += 503; 2398c2ecf20Sopenharmony_ci } else if (remain_len > 255) { 2408c2ecf20Sopenharmony_ci wmsg->header.len1 = 0xff; 2418c2ecf20Sopenharmony_ci wmsg->header.len2 = (remain_len - 255); 2428c2ecf20Sopenharmony_ci bytes_xfer = remain_len + 2438c2ecf20Sopenharmony_ci sizeof(struct vprbrd_i2c_write_hdr); 2448c2ecf20Sopenharmony_ci remain_len = 0; 2458c2ecf20Sopenharmony_ci } else { 2468c2ecf20Sopenharmony_ci wmsg->header.len1 = remain_len; 2478c2ecf20Sopenharmony_ci wmsg->header.len2 = 0x00; 2488c2ecf20Sopenharmony_ci bytes_xfer = remain_len + 2498c2ecf20Sopenharmony_ci sizeof(struct vprbrd_i2c_write_hdr); 2508c2ecf20Sopenharmony_ci remain_len = 0; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci memcpy(wmsg->data, msg->buf + start, 2538c2ecf20Sopenharmony_ci bytes_xfer - sizeof(struct vprbrd_i2c_write_hdr)); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = usb_bulk_msg(vb->usb_dev, 2568c2ecf20Sopenharmony_ci usb_sndbulkpipe(vb->usb_dev, 2578c2ecf20Sopenharmony_ci VPRBRD_EP_OUT), wmsg, 2588c2ecf20Sopenharmony_ci bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS); 2598c2ecf20Sopenharmony_ci if ((ret < 0) || (bytes_xfer != bytes_actual)) 2608c2ecf20Sopenharmony_ci return -EREMOTEIO; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs, 2668c2ecf20Sopenharmony_ci int num) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 2698c2ecf20Sopenharmony_ci int i, ret, 2708c2ecf20Sopenharmony_ci error = 0; 2718c2ecf20Sopenharmony_ci struct vprbrd *vb = (struct vprbrd *)i2c->algo_data; 2728c2ecf20Sopenharmony_ci struct vprbrd_i2c_addr_msg *amsg = 2738c2ecf20Sopenharmony_ci (struct vprbrd_i2c_addr_msg *)vb->buf; 2748c2ecf20Sopenharmony_ci struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "master xfer %d messages:\n", num); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci for (i = 0 ; i < num ; i++) { 2798c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, 2828c2ecf20Sopenharmony_ci " %d: %s (flags %d) %d bytes to 0x%02x\n", 2838c2ecf20Sopenharmony_ci i, pmsg->flags & I2C_M_RD ? "read" : "write", 2848c2ecf20Sopenharmony_ci pmsg->flags, pmsg->len, pmsg->addr); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 2878c2ecf20Sopenharmony_ci /* directly send the message */ 2888c2ecf20Sopenharmony_ci if (pmsg->flags & I2C_M_RD) { 2898c2ecf20Sopenharmony_ci /* read data */ 2908c2ecf20Sopenharmony_ci amsg->cmd = VPRBRD_I2C_CMD_ADDR; 2918c2ecf20Sopenharmony_ci amsg->unknown2 = 0x00; 2928c2ecf20Sopenharmony_ci amsg->unknown3 = 0x00; 2938c2ecf20Sopenharmony_ci amsg->addr = pmsg->addr; 2948c2ecf20Sopenharmony_ci amsg->unknown1 = 0x01; 2958c2ecf20Sopenharmony_ci amsg->len = cpu_to_le16(pmsg->len); 2968c2ecf20Sopenharmony_ci /* send the addr and len, we're interested to board */ 2978c2ecf20Sopenharmony_ci ret = vprbrd_i2c_addr(vb->usb_dev, amsg); 2988c2ecf20Sopenharmony_ci if (ret < 0) 2998c2ecf20Sopenharmony_ci error = ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = vprbrd_i2c_read(vb, pmsg); 3028c2ecf20Sopenharmony_ci if (ret < 0) 3038c2ecf20Sopenharmony_ci error = ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = vprbrd_i2c_status(i2c, smsg, error); 3068c2ecf20Sopenharmony_ci if (ret < 0) 3078c2ecf20Sopenharmony_ci error = ret; 3088c2ecf20Sopenharmony_ci /* in case of protocol error, return the error */ 3098c2ecf20Sopenharmony_ci if (error < 0) 3108c2ecf20Sopenharmony_ci goto error; 3118c2ecf20Sopenharmony_ci } else { 3128c2ecf20Sopenharmony_ci /* write data */ 3138c2ecf20Sopenharmony_ci ret = vprbrd_i2c_write(vb, pmsg); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci amsg->cmd = VPRBRD_I2C_CMD_ADDR; 3168c2ecf20Sopenharmony_ci amsg->unknown2 = 0x00; 3178c2ecf20Sopenharmony_ci amsg->unknown3 = 0x00; 3188c2ecf20Sopenharmony_ci amsg->addr = pmsg->addr; 3198c2ecf20Sopenharmony_ci amsg->unknown1 = 0x00; 3208c2ecf20Sopenharmony_ci amsg->len = cpu_to_le16(pmsg->len); 3218c2ecf20Sopenharmony_ci /* send the addr, the data goes to to board */ 3228c2ecf20Sopenharmony_ci ret = vprbrd_i2c_addr(vb->usb_dev, amsg); 3238c2ecf20Sopenharmony_ci if (ret < 0) 3248c2ecf20Sopenharmony_ci error = ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ret = vprbrd_i2c_status(i2c, smsg, error); 3278c2ecf20Sopenharmony_ci if (ret < 0) 3288c2ecf20Sopenharmony_ci error = ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (error < 0) 3318c2ecf20Sopenharmony_ci goto error; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci return num; 3368c2ecf20Sopenharmony_cierror: 3378c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 3388c2ecf20Sopenharmony_ci return error; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic u32 vprbrd_i2c_func(struct i2c_adapter *i2c) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* This is the actual algorithm we define */ 3478c2ecf20Sopenharmony_cistatic const struct i2c_algorithm vprbrd_algorithm = { 3488c2ecf20Sopenharmony_ci .master_xfer = vprbrd_i2c_xfer, 3498c2ecf20Sopenharmony_ci .functionality = vprbrd_i2c_func, 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks vprbrd_quirks = { 3538c2ecf20Sopenharmony_ci .max_read_len = 2048, 3548c2ecf20Sopenharmony_ci .max_write_len = 2048, 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int vprbrd_i2c_probe(struct platform_device *pdev) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); 3608c2ecf20Sopenharmony_ci struct vprbrd_i2c *vb_i2c; 3618c2ecf20Sopenharmony_ci int ret; 3628c2ecf20Sopenharmony_ci int pipe; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci vb_i2c = devm_kzalloc(&pdev->dev, sizeof(*vb_i2c), GFP_KERNEL); 3658c2ecf20Sopenharmony_ci if (vb_i2c == NULL) 3668c2ecf20Sopenharmony_ci return -ENOMEM; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* setup i2c adapter description */ 3698c2ecf20Sopenharmony_ci vb_i2c->i2c.owner = THIS_MODULE; 3708c2ecf20Sopenharmony_ci vb_i2c->i2c.class = I2C_CLASS_HWMON; 3718c2ecf20Sopenharmony_ci vb_i2c->i2c.algo = &vprbrd_algorithm; 3728c2ecf20Sopenharmony_ci vb_i2c->i2c.quirks = &vprbrd_quirks; 3738c2ecf20Sopenharmony_ci vb_i2c->i2c.algo_data = vb; 3748c2ecf20Sopenharmony_ci /* save the param in usb capabable memory */ 3758c2ecf20Sopenharmony_ci vb_i2c->bus_freq_param = i2c_bus_param; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci snprintf(vb_i2c->i2c.name, sizeof(vb_i2c->i2c.name), 3788c2ecf20Sopenharmony_ci "viperboard at bus %03d device %03d", 3798c2ecf20Sopenharmony_ci vb->usb_dev->bus->busnum, vb->usb_dev->devnum); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* setting the bus frequency */ 3828c2ecf20Sopenharmony_ci if ((i2c_bus_param <= VPRBRD_I2C_FREQ_10KHZ) 3838c2ecf20Sopenharmony_ci && (i2c_bus_param >= VPRBRD_I2C_FREQ_6MHZ)) { 3848c2ecf20Sopenharmony_ci pipe = usb_sndctrlpipe(vb->usb_dev, 0); 3858c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, pipe, 3868c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT, 3878c2ecf20Sopenharmony_ci 0x0000, 0x0000, &vb_i2c->bus_freq_param, 1, 3888c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 3898c2ecf20Sopenharmony_ci if (ret != 1) { 3908c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failure setting i2c_bus_freq to %d\n", 3918c2ecf20Sopenharmony_ci i2c_bus_freq); 3928c2ecf20Sopenharmony_ci return -EIO; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3968c2ecf20Sopenharmony_ci "invalid i2c_bus_freq setting:%d\n", i2c_bus_freq); 3978c2ecf20Sopenharmony_ci return -EIO; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci vb_i2c->i2c.dev.parent = &pdev->dev; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* attach to i2c layer */ 4038c2ecf20Sopenharmony_ci i2c_add_adapter(&vb_i2c->i2c); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, vb_i2c); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int vprbrd_i2c_remove(struct platform_device *pdev) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci i2c_del_adapter(&vb_i2c->i2c); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic struct platform_driver vprbrd_i2c_driver = { 4208c2ecf20Sopenharmony_ci .driver.name = "viperboard-i2c", 4218c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 4228c2ecf20Sopenharmony_ci .probe = vprbrd_i2c_probe, 4238c2ecf20Sopenharmony_ci .remove = vprbrd_i2c_remove, 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int __init vprbrd_i2c_init(void) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci switch (i2c_bus_freq) { 4298c2ecf20Sopenharmony_ci case 6000: 4308c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_6MHZ; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci case 3000: 4338c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_3MHZ; 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci case 1000: 4368c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_1MHZ; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci case 400: 4398c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_400KHZ; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 200: 4428c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_200KHZ; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case 100: 4458c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case 10: 4488c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_10KHZ; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci default: 4518c2ecf20Sopenharmony_ci pr_warn("invalid i2c_bus_freq (%d)\n", i2c_bus_freq); 4528c2ecf20Sopenharmony_ci i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return platform_driver_register(&vprbrd_i2c_driver); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_cisubsys_initcall(vprbrd_i2c_init); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void __exit vprbrd_i2c_exit(void) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci platform_driver_unregister(&vprbrd_i2c_driver); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_cimodule_exit(vprbrd_i2c_exit); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>"); 4668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C master driver for Nano River Techs Viperboard"); 4678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4688c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:viperboard-i2c"); 469