18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Coral-P(A)/Lime I2C adapter driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/fb.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "mb862xxfb.h" 158c2ecf20Sopenharmony_ci#include "mb862xx_reg.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int mb862xx_i2c_wait_event(struct i2c_adapter *adap) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct mb862xxfb_par *par = adap->algo_data; 208c2ecf20Sopenharmony_ci u32 reg; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci do { 238c2ecf20Sopenharmony_ci udelay(10); 248c2ecf20Sopenharmony_ci reg = inreg(i2c, GC_I2C_BCR); 258c2ecf20Sopenharmony_ci if (reg & (I2C_INT | I2C_BER)) 268c2ecf20Sopenharmony_ci break; 278c2ecf20Sopenharmony_ci } while (1); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return (reg & I2C_BER) ? 0 : 1; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int mb862xx_i2c_do_address(struct i2c_adapter *adap, int addr) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct mb862xxfb_par *par = adap->algo_data; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_DAR, addr); 378c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_CCR, I2C_CLOCK_AND_ENABLE); 388c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_BCR, par->i2c_rs ? I2C_REPEATED_START : I2C_START); 398c2ecf20Sopenharmony_ci if (!mb862xx_i2c_wait_event(adap)) 408c2ecf20Sopenharmony_ci return -EIO; 418c2ecf20Sopenharmony_ci par->i2c_rs = !(inreg(i2c, GC_I2C_BSR) & I2C_LRB); 428c2ecf20Sopenharmony_ci return par->i2c_rs; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int mb862xx_i2c_write_byte(struct i2c_adapter *adap, u8 byte) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct mb862xxfb_par *par = adap->algo_data; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_DAR, byte); 508c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_BCR, I2C_START); 518c2ecf20Sopenharmony_ci if (!mb862xx_i2c_wait_event(adap)) 528c2ecf20Sopenharmony_ci return -EIO; 538c2ecf20Sopenharmony_ci return !(inreg(i2c, GC_I2C_BSR) & I2C_LRB); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct mb862xxfb_par *par = adap->algo_data; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_BCR, I2C_START | (last ? 0 : I2C_ACK)); 618c2ecf20Sopenharmony_ci if (!mb862xx_i2c_wait_event(adap)) 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci *byte = inreg(i2c, GC_I2C_DAR); 648c2ecf20Sopenharmony_ci return 1; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void mb862xx_i2c_stop(struct i2c_adapter *adap) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct mb862xxfb_par *par = adap->algo_data; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_BCR, I2C_STOP); 728c2ecf20Sopenharmony_ci outreg(i2c, GC_I2C_CCR, I2C_DISABLE); 738c2ecf20Sopenharmony_ci par->i2c_rs = 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int mb862xx_i2c_read(struct i2c_adapter *adap, struct i2c_msg *m) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci int i, ret = 0; 798c2ecf20Sopenharmony_ci int last = m->len - 1; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < m->len; i++) { 828c2ecf20Sopenharmony_ci if (!mb862xx_i2c_read_byte(adap, &m->buf[i], i == last)) { 838c2ecf20Sopenharmony_ci ret = -EIO; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int mb862xx_i2c_write(struct i2c_adapter *adap, struct i2c_msg *m) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int i, ret = 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci for (i = 0; i < m->len; i++) { 958c2ecf20Sopenharmony_ci if (!mb862xx_i2c_write_byte(adap, m->buf[i])) { 968c2ecf20Sopenharmony_ci ret = -EIO; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci return ret; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int mb862xx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 1048c2ecf20Sopenharmony_ci int num) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct mb862xxfb_par *par = adap->algo_data; 1078c2ecf20Sopenharmony_ci struct i2c_msg *m; 1088c2ecf20Sopenharmony_ci int addr; 1098c2ecf20Sopenharmony_ci int i = 0, err = 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dev_dbg(par->dev, "%s: %d msgs\n", __func__, num); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1148c2ecf20Sopenharmony_ci m = &msgs[i]; 1158c2ecf20Sopenharmony_ci if (!m->len) { 1168c2ecf20Sopenharmony_ci dev_dbg(par->dev, "%s: null msgs\n", __func__); 1178c2ecf20Sopenharmony_ci continue; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci addr = m->addr; 1208c2ecf20Sopenharmony_ci if (m->flags & I2C_M_RD) 1218c2ecf20Sopenharmony_ci addr |= 1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci err = mb862xx_i2c_do_address(adap, addr); 1248c2ecf20Sopenharmony_ci if (err < 0) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci if (m->flags & I2C_M_RD) 1278c2ecf20Sopenharmony_ci err = mb862xx_i2c_read(adap, m); 1288c2ecf20Sopenharmony_ci else 1298c2ecf20Sopenharmony_ci err = mb862xx_i2c_write(adap, m); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (i) 1338c2ecf20Sopenharmony_ci mb862xx_i2c_stop(adap); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return (err < 0) ? err : i; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic u32 mb862xx_func(struct i2c_adapter *adap) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_BYTE_DATA; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct i2c_algorithm mb862xx_algo = { 1448c2ecf20Sopenharmony_ci .master_xfer = mb862xx_xfer, 1458c2ecf20Sopenharmony_ci .functionality = mb862xx_func, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct i2c_adapter mb862xx_i2c_adapter = { 1498c2ecf20Sopenharmony_ci .name = "MB862xx I2C adapter", 1508c2ecf20Sopenharmony_ci .algo = &mb862xx_algo, 1518c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciint mb862xx_i2c_init(struct mb862xxfb_par *par) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci mb862xx_i2c_adapter.algo_data = par; 1578c2ecf20Sopenharmony_ci par->adap = &mb862xx_i2c_adapter; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return i2c_add_adapter(par->adap); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_civoid mb862xx_i2c_exit(struct mb862xxfb_par *par) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci if (par->adap) { 1658c2ecf20Sopenharmony_ci i2c_del_adapter(par->adap); 1668c2ecf20Sopenharmony_ci par->adap = NULL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 169