18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Original author: 68c2ecf20Sopenharmony_ci * Ben Collins <bcollins@ubuntu.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Additional work by: 98c2ecf20Sopenharmony_ci * John Brooks <john.brooks@bluecherry.net> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c 138c2ecf20Sopenharmony_ci * channel. The bus can only handle one i2c event at a time. The below handles 148c2ecf20Sopenharmony_ci * this all wrong. We should be using the status registers to see if the bus 158c2ecf20Sopenharmony_ci * is in use, and have a global lock to check the status register. Also, 168c2ecf20Sopenharmony_ci * the bulk of the work should be handled out-of-interrupt. The ugly loops 178c2ecf20Sopenharmony_ci * that occur during interrupt scare me. The ISR should merely signal 188c2ecf20Sopenharmony_ci * thread context, ACK the interrupt, and move on. -- BenC */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "solo6x10.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciu8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct i2c_msg msgs[2]; 288c2ecf20Sopenharmony_ci u8 data; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci msgs[0].flags = 0; 318c2ecf20Sopenharmony_ci msgs[0].addr = addr; 328c2ecf20Sopenharmony_ci msgs[0].len = 1; 338c2ecf20Sopenharmony_ci msgs[0].buf = &off; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci msgs[1].flags = I2C_M_RD; 368c2ecf20Sopenharmony_ci msgs[1].addr = addr; 378c2ecf20Sopenharmony_ci msgs[1].len = 1; 388c2ecf20Sopenharmony_ci msgs[1].buf = &data; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return data; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_civoid solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, 468c2ecf20Sopenharmony_ci u8 off, u8 data) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct i2c_msg msgs; 498c2ecf20Sopenharmony_ci u8 buf[2]; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci buf[0] = off; 528c2ecf20Sopenharmony_ci buf[1] = data; 538c2ecf20Sopenharmony_ci msgs.flags = 0; 548c2ecf20Sopenharmony_ci msgs.addr = addr; 558c2ecf20Sopenharmony_ci msgs.len = 2; 568c2ecf20Sopenharmony_ci msgs.buf = buf; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void solo_i2c_flush(struct solo_dev *solo_dev, int wr) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci u32 ctrl; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (solo_dev->i2c_state == IIC_STATE_START) 688c2ecf20Sopenharmony_ci ctrl |= SOLO_IIC_START; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (wr) { 718c2ecf20Sopenharmony_ci ctrl |= SOLO_IIC_WRITE; 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci ctrl |= SOLO_IIC_READ; 748c2ecf20Sopenharmony_ci if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) 758c2ecf20Sopenharmony_ci ctrl |= SOLO_IIC_ACK_EN; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) 798c2ecf20Sopenharmony_ci ctrl |= SOLO_IIC_STOP; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void solo_i2c_start(struct solo_dev *solo_dev) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci u32 addr = solo_dev->i2c_msg->addr << 1; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg->flags & I2C_M_RD) 898c2ecf20Sopenharmony_ci addr |= 1; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci solo_dev->i2c_state = IIC_STATE_START; 928c2ecf20Sopenharmony_ci solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); 938c2ecf20Sopenharmony_ci solo_i2c_flush(solo_dev, 1); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void solo_i2c_stop(struct solo_dev *solo_dev) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci solo_irq_off(solo_dev, SOLO_IRQ_IIC); 998c2ecf20Sopenharmony_ci solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); 1008c2ecf20Sopenharmony_ci solo_dev->i2c_state = IIC_STATE_STOP; 1018c2ecf20Sopenharmony_ci wake_up(&solo_dev->i2c_wait); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int solo_i2c_handle_read(struct solo_dev *solo_dev) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ciprepare_read: 1078c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { 1088c2ecf20Sopenharmony_ci solo_i2c_flush(solo_dev, 0); 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci solo_dev->i2c_msg_ptr = 0; 1138c2ecf20Sopenharmony_ci solo_dev->i2c_msg++; 1148c2ecf20Sopenharmony_ci solo_dev->i2c_msg_num--; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg_num == 0) { 1178c2ecf20Sopenharmony_ci solo_i2c_stop(solo_dev); 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { 1228c2ecf20Sopenharmony_ci solo_i2c_start(solo_dev); 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg->flags & I2C_M_RD) 1258c2ecf20Sopenharmony_ci goto prepare_read; 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci solo_i2c_stop(solo_dev); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int solo_i2c_handle_write(struct solo_dev *solo_dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ciretry_write: 1368c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { 1378c2ecf20Sopenharmony_ci solo_reg_write(solo_dev, SOLO_IIC_TXD, 1388c2ecf20Sopenharmony_ci solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); 1398c2ecf20Sopenharmony_ci solo_dev->i2c_msg_ptr++; 1408c2ecf20Sopenharmony_ci solo_i2c_flush(solo_dev, 1); 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci solo_dev->i2c_msg_ptr = 0; 1458c2ecf20Sopenharmony_ci solo_dev->i2c_msg++; 1468c2ecf20Sopenharmony_ci solo_dev->i2c_msg_num--; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg_num == 0) { 1498c2ecf20Sopenharmony_ci solo_i2c_stop(solo_dev); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { 1548c2ecf20Sopenharmony_ci solo_i2c_start(solo_dev); 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg->flags & I2C_M_RD) 1578c2ecf20Sopenharmony_ci solo_i2c_stop(solo_dev); 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci goto retry_write; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciint solo_i2c_isr(struct solo_dev *solo_dev) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); 1688c2ecf20Sopenharmony_ci int ret = -EINVAL; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) 1728c2ecf20Sopenharmony_ci || solo_dev->i2c_id < 0) { 1738c2ecf20Sopenharmony_ci solo_i2c_stop(solo_dev); 1748c2ecf20Sopenharmony_ci return -ENXIO; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci switch (solo_dev->i2c_state) { 1788c2ecf20Sopenharmony_ci case IIC_STATE_START: 1798c2ecf20Sopenharmony_ci if (solo_dev->i2c_msg->flags & I2C_M_RD) { 1808c2ecf20Sopenharmony_ci solo_dev->i2c_state = IIC_STATE_READ; 1818c2ecf20Sopenharmony_ci ret = solo_i2c_handle_read(solo_dev); 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci solo_dev->i2c_state = IIC_STATE_WRITE; 1868c2ecf20Sopenharmony_ci fallthrough; 1878c2ecf20Sopenharmony_ci case IIC_STATE_WRITE: 1888c2ecf20Sopenharmony_ci ret = solo_i2c_handle_write(solo_dev); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci case IIC_STATE_READ: 1928c2ecf20Sopenharmony_ci solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = 1938c2ecf20Sopenharmony_ci solo_reg_read(solo_dev, SOLO_IIC_RXD); 1948c2ecf20Sopenharmony_ci solo_dev->i2c_msg_ptr++; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = solo_i2c_handle_read(solo_dev); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci solo_i2c_stop(solo_dev); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int solo_i2c_master_xfer(struct i2c_adapter *adap, 2078c2ecf20Sopenharmony_ci struct i2c_msg msgs[], int num) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct solo_dev *solo_dev = adap->algo_data; 2108c2ecf20Sopenharmony_ci unsigned long timeout; 2118c2ecf20Sopenharmony_ci int ret; 2128c2ecf20Sopenharmony_ci int i; 2138c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { 2168c2ecf20Sopenharmony_ci if (&solo_dev->i2c_adap[i] == adap) 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (i == SOLO_I2C_ADAPTERS) 2218c2ecf20Sopenharmony_ci return num; /* XXX Right return value for failure? */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci mutex_lock(&solo_dev->i2c_mutex); 2248c2ecf20Sopenharmony_ci solo_dev->i2c_id = i; 2258c2ecf20Sopenharmony_ci solo_dev->i2c_msg = msgs; 2268c2ecf20Sopenharmony_ci solo_dev->i2c_msg_num = num; 2278c2ecf20Sopenharmony_ci solo_dev->i2c_msg_ptr = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); 2308c2ecf20Sopenharmony_ci solo_irq_on(solo_dev, SOLO_IRQ_IIC); 2318c2ecf20Sopenharmony_ci solo_i2c_start(solo_dev); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci timeout = HZ / 2; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci for (;;) { 2368c2ecf20Sopenharmony_ci prepare_to_wait(&solo_dev->i2c_wait, &wait, 2378c2ecf20Sopenharmony_ci TASK_INTERRUPTIBLE); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (solo_dev->i2c_state == IIC_STATE_STOP) 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci timeout = schedule_timeout(timeout); 2438c2ecf20Sopenharmony_ci if (!timeout) 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (signal_pending(current)) 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci finish_wait(&solo_dev->i2c_wait, &wait); 2518c2ecf20Sopenharmony_ci ret = num - solo_dev->i2c_msg_num; 2528c2ecf20Sopenharmony_ci solo_dev->i2c_state = IIC_STATE_IDLE; 2538c2ecf20Sopenharmony_ci solo_dev->i2c_id = -1; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mutex_unlock(&solo_dev->i2c_mutex); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic u32 solo_i2c_functionality(struct i2c_adapter *adap) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return I2C_FUNC_I2C; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct i2c_algorithm solo_i2c_algo = { 2668c2ecf20Sopenharmony_ci .master_xfer = solo_i2c_master_xfer, 2678c2ecf20Sopenharmony_ci .functionality = solo_i2c_functionality, 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciint solo_i2c_init(struct solo_dev *solo_dev) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci int i; 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci solo_reg_write(solo_dev, SOLO_IIC_CFG, 2768c2ecf20Sopenharmony_ci SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci solo_dev->i2c_id = -1; 2798c2ecf20Sopenharmony_ci solo_dev->i2c_state = IIC_STATE_IDLE; 2808c2ecf20Sopenharmony_ci init_waitqueue_head(&solo_dev->i2c_wait); 2818c2ecf20Sopenharmony_ci mutex_init(&solo_dev->i2c_mutex); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { 2848c2ecf20Sopenharmony_ci struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", 2878c2ecf20Sopenharmony_ci SOLO6X10_NAME, i); 2888c2ecf20Sopenharmony_ci adap->algo = &solo_i2c_algo; 2898c2ecf20Sopenharmony_ci adap->algo_data = solo_dev; 2908c2ecf20Sopenharmony_ci adap->retries = 1; 2918c2ecf20Sopenharmony_ci adap->dev.parent = &solo_dev->pdev->dev; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ret = i2c_add_adapter(adap); 2948c2ecf20Sopenharmony_ci if (ret) { 2958c2ecf20Sopenharmony_ci adap->algo_data = NULL; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (ret) { 3018c2ecf20Sopenharmony_ci for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { 3028c2ecf20Sopenharmony_ci if (!solo_dev->i2c_adap[i].algo_data) 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci i2c_del_adapter(&solo_dev->i2c_adap[i]); 3058c2ecf20Sopenharmony_ci solo_dev->i2c_adap[i].algo_data = NULL; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_civoid solo_i2c_exit(struct solo_dev *solo_dev) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci int i; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { 3188c2ecf20Sopenharmony_ci if (!solo_dev->i2c_adap[i].algo_data) 3198c2ecf20Sopenharmony_ci continue; 3208c2ecf20Sopenharmony_ci i2c_del_adapter(&solo_dev->i2c_adap[i]); 3218c2ecf20Sopenharmony_ci solo_dev->i2c_adap[i].algo_data = NULL; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci} 324