18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ddbridge-i2c.c: Digital Devices bridge i2c driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2017 Digital Devices GmbH 68c2ecf20Sopenharmony_ci * Ralph Metzler <rjkm@metzlerbros.de> 78c2ecf20Sopenharmony_ci * Marcus Metzler <mocm@metzlerbros.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License 118c2ecf20Sopenharmony_ci * version 2 only, as published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 168c2ecf20Sopenharmony_ci * GNU General Public License for more details. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/poll.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci#include <linux/pci.h> 278c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 288c2ecf20Sopenharmony_ci#include <linux/timer.h> 298c2ecf20Sopenharmony_ci#include <linux/i2c.h> 308c2ecf20Sopenharmony_ci#include <linux/swab.h> 318c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "ddbridge.h" 348c2ecf20Sopenharmony_ci#include "ddbridge-i2c.h" 358c2ecf20Sopenharmony_ci#include "ddbridge-regs.h" 368c2ecf20Sopenharmony_ci#include "ddbridge-io.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/******************************************************************************/ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct ddb *dev = i2c->dev; 438c2ecf20Sopenharmony_ci unsigned long stat; 448c2ecf20Sopenharmony_ci u32 val; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci ddbwritel(dev, (adr << 9) | cmd, i2c->regs + I2C_COMMAND); 478c2ecf20Sopenharmony_ci stat = wait_for_completion_timeout(&i2c->completion, HZ); 488c2ecf20Sopenharmony_ci val = ddbreadl(dev, i2c->regs + I2C_COMMAND); 498c2ecf20Sopenharmony_ci if (stat == 0) { 508c2ecf20Sopenharmony_ci dev_err(dev->dev, "I2C timeout, card %d, port %d, link %u\n", 518c2ecf20Sopenharmony_ci dev->nr, i2c->nr, i2c->link); 528c2ecf20Sopenharmony_ci { 538c2ecf20Sopenharmony_ci u32 istat = ddbreadl(dev, INTERRUPT_STATUS); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci dev_err(dev->dev, "DDBridge IRS %08x\n", istat); 568c2ecf20Sopenharmony_ci if (i2c->link) { 578c2ecf20Sopenharmony_ci u32 listat = ddbreadl(dev, 588c2ecf20Sopenharmony_ci DDB_LINK_TAG(i2c->link) | 598c2ecf20Sopenharmony_ci INTERRUPT_STATUS); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci dev_err(dev->dev, "DDBridge link %u IRS %08x\n", 628c2ecf20Sopenharmony_ci i2c->link, listat); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci if (istat & 1) { 658c2ecf20Sopenharmony_ci ddbwritel(dev, istat & 1, INTERRUPT_ACK); 668c2ecf20Sopenharmony_ci } else { 678c2ecf20Sopenharmony_ci u32 mon = ddbreadl(dev, 688c2ecf20Sopenharmony_ci i2c->regs + I2C_MONITOR); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci dev_err(dev->dev, "I2C cmd=%08x mon=%08x\n", 718c2ecf20Sopenharmony_ci val, mon); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci return -EIO; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci val &= 0x70000; 778c2ecf20Sopenharmony_ci if (val == 0x20000) 788c2ecf20Sopenharmony_ci dev_err(dev->dev, "I2C bus error\n"); 798c2ecf20Sopenharmony_ci if (val) 808c2ecf20Sopenharmony_ci return -EIO; 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int ddb_i2c_master_xfer(struct i2c_adapter *adapter, 858c2ecf20Sopenharmony_ci struct i2c_msg msg[], int num) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter); 888c2ecf20Sopenharmony_ci struct ddb *dev = i2c->dev; 898c2ecf20Sopenharmony_ci u8 addr = 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci addr = msg[0].addr; 928c2ecf20Sopenharmony_ci if (msg[0].len > i2c->bsize) 938c2ecf20Sopenharmony_ci return -EIO; 948c2ecf20Sopenharmony_ci switch (num) { 958c2ecf20Sopenharmony_ci case 1: 968c2ecf20Sopenharmony_ci if (msg[0].flags & I2C_M_RD) { 978c2ecf20Sopenharmony_ci ddbwritel(dev, msg[0].len << 16, 988c2ecf20Sopenharmony_ci i2c->regs + I2C_TASKLENGTH); 998c2ecf20Sopenharmony_ci if (ddb_i2c_cmd(i2c, addr, 3)) 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci ddbcpyfrom(dev, msg[0].buf, 1028c2ecf20Sopenharmony_ci i2c->rbuf, msg[0].len); 1038c2ecf20Sopenharmony_ci return num; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len); 1068c2ecf20Sopenharmony_ci ddbwritel(dev, msg[0].len, i2c->regs + I2C_TASKLENGTH); 1078c2ecf20Sopenharmony_ci if (ddb_i2c_cmd(i2c, addr, 2)) 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci return num; 1108c2ecf20Sopenharmony_ci case 2: 1118c2ecf20Sopenharmony_ci if ((msg[0].flags & I2C_M_RD) == I2C_M_RD) 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci if ((msg[1].flags & I2C_M_RD) != I2C_M_RD) 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci if (msg[1].len > i2c->bsize) 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len); 1188c2ecf20Sopenharmony_ci ddbwritel(dev, msg[0].len | (msg[1].len << 16), 1198c2ecf20Sopenharmony_ci i2c->regs + I2C_TASKLENGTH); 1208c2ecf20Sopenharmony_ci if (ddb_i2c_cmd(i2c, addr, 1)) 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci ddbcpyfrom(dev, msg[1].buf, 1238c2ecf20Sopenharmony_ci i2c->rbuf, 1248c2ecf20Sopenharmony_ci msg[1].len); 1258c2ecf20Sopenharmony_ci return num; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci return -EIO; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic u32 ddb_i2c_functionality(struct i2c_adapter *adap) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct i2c_algorithm ddb_i2c_algo = { 1388c2ecf20Sopenharmony_ci .master_xfer = ddb_i2c_master_xfer, 1398c2ecf20Sopenharmony_ci .functionality = ddb_i2c_functionality, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid ddb_i2c_release(struct ddb *dev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int i; 1458c2ecf20Sopenharmony_ci struct ddb_i2c *i2c; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0; i < dev->i2c_num; i++) { 1488c2ecf20Sopenharmony_ci i2c = &dev->i2c[i]; 1498c2ecf20Sopenharmony_ci i2c_del_adapter(&i2c->adap); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void i2c_handler(void *priv) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct ddb_i2c *i2c = (struct ddb_i2c *)priv; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci complete(&i2c->completion); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int ddb_i2c_add(struct ddb *dev, struct ddb_i2c *i2c, 1618c2ecf20Sopenharmony_ci const struct ddb_regmap *regmap, int link, 1628c2ecf20Sopenharmony_ci int i, int num) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct i2c_adapter *adap; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci i2c->nr = i; 1678c2ecf20Sopenharmony_ci i2c->dev = dev; 1688c2ecf20Sopenharmony_ci i2c->link = link; 1698c2ecf20Sopenharmony_ci i2c->bsize = regmap->i2c_buf->size; 1708c2ecf20Sopenharmony_ci i2c->wbuf = DDB_LINK_TAG(link) | 1718c2ecf20Sopenharmony_ci (regmap->i2c_buf->base + i2c->bsize * i); 1728c2ecf20Sopenharmony_ci i2c->rbuf = i2c->wbuf; /* + i2c->bsize / 2 */ 1738c2ecf20Sopenharmony_ci i2c->regs = DDB_LINK_TAG(link) | 1748c2ecf20Sopenharmony_ci (regmap->i2c->base + regmap->i2c->size * i); 1758c2ecf20Sopenharmony_ci ddbwritel(dev, I2C_SPEED_100, i2c->regs + I2C_TIMING); 1768c2ecf20Sopenharmony_ci ddbwritel(dev, ((i2c->rbuf & 0xffff) << 16) | (i2c->wbuf & 0xffff), 1778c2ecf20Sopenharmony_ci i2c->regs + I2C_TASKADDRESS); 1788c2ecf20Sopenharmony_ci init_completion(&i2c->completion); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci adap = &i2c->adap; 1818c2ecf20Sopenharmony_ci i2c_set_adapdata(adap, i2c); 1828c2ecf20Sopenharmony_ci#ifdef I2C_ADAP_CLASS_TV_DIGITAL 1838c2ecf20Sopenharmony_ci adap->class = I2C_ADAP_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; 1848c2ecf20Sopenharmony_ci#else 1858c2ecf20Sopenharmony_ci#ifdef I2C_CLASS_TV_ANALOG 1868c2ecf20Sopenharmony_ci adap->class = I2C_CLASS_TV_ANALOG; 1878c2ecf20Sopenharmony_ci#endif 1888c2ecf20Sopenharmony_ci#endif 1898c2ecf20Sopenharmony_ci snprintf(adap->name, I2C_NAME_SIZE, "ddbridge_%02x.%x.%x", 1908c2ecf20Sopenharmony_ci dev->nr, i2c->link, i); 1918c2ecf20Sopenharmony_ci adap->algo = &ddb_i2c_algo; 1928c2ecf20Sopenharmony_ci adap->algo_data = (void *)i2c; 1938c2ecf20Sopenharmony_ci adap->dev.parent = dev->dev; 1948c2ecf20Sopenharmony_ci return i2c_add_adapter(adap); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciint ddb_i2c_init(struct ddb *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int stat = 0; 2008c2ecf20Sopenharmony_ci u32 i, j, num = 0, l, base; 2018c2ecf20Sopenharmony_ci struct ddb_i2c *i2c; 2028c2ecf20Sopenharmony_ci struct i2c_adapter *adap; 2038c2ecf20Sopenharmony_ci const struct ddb_regmap *regmap; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (l = 0; l < DDB_MAX_LINK; l++) { 2068c2ecf20Sopenharmony_ci if (!dev->link[l].info) 2078c2ecf20Sopenharmony_ci continue; 2088c2ecf20Sopenharmony_ci regmap = dev->link[l].info->regmap; 2098c2ecf20Sopenharmony_ci if (!regmap || !regmap->i2c) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci base = regmap->irq_base_i2c; 2128c2ecf20Sopenharmony_ci for (i = 0; i < regmap->i2c->num; i++) { 2138c2ecf20Sopenharmony_ci if (!(dev->link[l].info->i2c_mask & (1 << i))) 2148c2ecf20Sopenharmony_ci continue; 2158c2ecf20Sopenharmony_ci i2c = &dev->i2c[num]; 2168c2ecf20Sopenharmony_ci ddb_irq_set(dev, l, i + base, i2c_handler, i2c); 2178c2ecf20Sopenharmony_ci stat = ddb_i2c_add(dev, i2c, regmap, l, i, num); 2188c2ecf20Sopenharmony_ci if (stat) 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci num++; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci if (stat) { 2248c2ecf20Sopenharmony_ci for (j = 0; j < num; j++) { 2258c2ecf20Sopenharmony_ci i2c = &dev->i2c[j]; 2268c2ecf20Sopenharmony_ci adap = &i2c->adap; 2278c2ecf20Sopenharmony_ci i2c_del_adapter(adap); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci dev->i2c_num = num; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return stat; 2348c2ecf20Sopenharmony_ci} 235