18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ngene-i2c.c: nGene PCIe bridge driver i2c functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Micronas 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de> 88c2ecf20Sopenharmony_ci * Modifications for new nGene firmware, 98c2ecf20Sopenharmony_ci * support for EEPROM-copying, 108c2ecf20Sopenharmony_ci * support for new dual DVB-S2 card prototype 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* FIXME - some of these can probably be removed */ 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/poll.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <asm/div64.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 238c2ecf20Sopenharmony_ci#include <linux/timer.h> 248c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h> 258c2ecf20Sopenharmony_ci#include <linux/firmware.h> 268c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "ngene.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Firmware command for i2c operations */ 318c2ecf20Sopenharmony_cistatic int ngene_command_i2c_read(struct ngene *dev, u8 adr, 328c2ecf20Sopenharmony_ci u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct ngene_command com; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci com.cmd.hdr.Opcode = CMD_I2C_READ; 378c2ecf20Sopenharmony_ci com.cmd.hdr.Length = outlen + 3; 388c2ecf20Sopenharmony_ci com.cmd.I2CRead.Device = adr << 1; 398c2ecf20Sopenharmony_ci memcpy(com.cmd.I2CRead.Data, out, outlen); 408c2ecf20Sopenharmony_ci com.cmd.I2CRead.Data[outlen] = inlen; 418c2ecf20Sopenharmony_ci com.cmd.I2CRead.Data[outlen + 1] = 0; 428c2ecf20Sopenharmony_ci com.in_len = outlen + 3; 438c2ecf20Sopenharmony_ci com.out_len = inlen + 1; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (ngene_command(dev, &com) < 0) 468c2ecf20Sopenharmony_ci return -EIO; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if ((com.cmd.raw8[0] >> 1) != adr) 498c2ecf20Sopenharmony_ci return -EIO; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (flag) 528c2ecf20Sopenharmony_ci memcpy(in, com.cmd.raw8, inlen + 1); 538c2ecf20Sopenharmony_ci else 548c2ecf20Sopenharmony_ci memcpy(in, com.cmd.raw8 + 1, inlen); 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int ngene_command_i2c_write(struct ngene *dev, u8 adr, 598c2ecf20Sopenharmony_ci u8 *out, u8 outlen) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct ngene_command com; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci com.cmd.hdr.Opcode = CMD_I2C_WRITE; 658c2ecf20Sopenharmony_ci com.cmd.hdr.Length = outlen + 1; 668c2ecf20Sopenharmony_ci com.cmd.I2CRead.Device = adr << 1; 678c2ecf20Sopenharmony_ci memcpy(com.cmd.I2CRead.Data, out, outlen); 688c2ecf20Sopenharmony_ci com.in_len = outlen + 1; 698c2ecf20Sopenharmony_ci com.out_len = 1; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (ngene_command(dev, &com) < 0) 728c2ecf20Sopenharmony_ci return -EIO; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (com.cmd.raw8[0] == 1) 758c2ecf20Sopenharmony_ci return -EIO; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void ngene_i2c_set_bus(struct ngene *dev, int bus) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (!(dev->card_info->i2c_access & 2)) 838c2ecf20Sopenharmony_ci return; 848c2ecf20Sopenharmony_ci if (dev->i2c_current_bus == bus) 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci switch (bus) { 888c2ecf20Sopenharmony_ci case 0: 898c2ecf20Sopenharmony_ci ngene_command_gpio_set(dev, 3, 0); 908c2ecf20Sopenharmony_ci ngene_command_gpio_set(dev, 2, 1); 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci case 1: 948c2ecf20Sopenharmony_ci ngene_command_gpio_set(dev, 2, 0); 958c2ecf20Sopenharmony_ci ngene_command_gpio_set(dev, 3, 1); 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci dev->i2c_current_bus = bus; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int ngene_i2c_master_xfer(struct i2c_adapter *adapter, 1028c2ecf20Sopenharmony_ci struct i2c_msg msg[], int num) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct ngene_channel *chan = 1058c2ecf20Sopenharmony_ci (struct ngene_channel *)i2c_get_adapdata(adapter); 1068c2ecf20Sopenharmony_ci struct ngene *dev = chan->dev; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci mutex_lock(&dev->i2c_switch_mutex); 1098c2ecf20Sopenharmony_ci ngene_i2c_set_bus(dev, chan->number); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) 1128c2ecf20Sopenharmony_ci if (!ngene_command_i2c_read(dev, msg[0].addr, 1138c2ecf20Sopenharmony_ci msg[0].buf, msg[0].len, 1148c2ecf20Sopenharmony_ci msg[1].buf, msg[1].len, 0)) 1158c2ecf20Sopenharmony_ci goto done; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (num == 1 && !(msg[0].flags & I2C_M_RD)) 1188c2ecf20Sopenharmony_ci if (!ngene_command_i2c_write(dev, msg[0].addr, 1198c2ecf20Sopenharmony_ci msg[0].buf, msg[0].len)) 1208c2ecf20Sopenharmony_ci goto done; 1218c2ecf20Sopenharmony_ci if (num == 1 && (msg[0].flags & I2C_M_RD)) 1228c2ecf20Sopenharmony_ci if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0, 1238c2ecf20Sopenharmony_ci msg[0].buf, msg[0].len, 0)) 1248c2ecf20Sopenharmony_ci goto done; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mutex_unlock(&dev->i2c_switch_mutex); 1278c2ecf20Sopenharmony_ci return -EIO; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cidone: 1308c2ecf20Sopenharmony_ci mutex_unlock(&dev->i2c_switch_mutex); 1318c2ecf20Sopenharmony_ci return num; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic u32 ngene_i2c_functionality(struct i2c_adapter *adap) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic const struct i2c_algorithm ngene_i2c_algo = { 1418c2ecf20Sopenharmony_ci .master_xfer = ngene_i2c_master_xfer, 1428c2ecf20Sopenharmony_ci .functionality = ngene_i2c_functionality, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciint ngene_i2c_init(struct ngene *dev, int dev_nr) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci i2c_set_adapdata(adap, &(dev->channel[dev_nr])); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci strscpy(adap->name, "nGene", sizeof(adap->name)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci adap->algo = &ngene_i2c_algo; 1548c2ecf20Sopenharmony_ci adap->algo_data = (void *)&(dev->channel[dev_nr]); 1558c2ecf20Sopenharmony_ci adap->dev.parent = &dev->pci_dev->dev; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return i2c_add_adapter(adap); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 160