cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ddbridge-i2c.c (5865B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * ddbridge-i2c.c: Digital Devices bridge i2c driver
      4 *
      5 * Copyright (C) 2010-2017 Digital Devices GmbH
      6 *                         Ralph Metzler <rjkm@metzlerbros.de>
      7 *                         Marcus Metzler <mocm@metzlerbros.de>
      8 *
      9 * This program is free software; you can redistribute it and/or
     10 * modify it under the terms of the GNU General Public License
     11 * version 2 only, as published by the Free Software Foundation.
     12 *
     13 * This program is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 * GNU General Public License for more details.
     17 */
     18
     19#include <linux/module.h>
     20#include <linux/init.h>
     21#include <linux/interrupt.h>
     22#include <linux/delay.h>
     23#include <linux/slab.h>
     24#include <linux/poll.h>
     25#include <linux/io.h>
     26#include <linux/pci.h>
     27#include <linux/pci_ids.h>
     28#include <linux/timer.h>
     29#include <linux/i2c.h>
     30#include <linux/swab.h>
     31#include <linux/vmalloc.h>
     32
     33#include "ddbridge.h"
     34#include "ddbridge-i2c.h"
     35#include "ddbridge-regs.h"
     36#include "ddbridge-io.h"
     37
     38/******************************************************************************/
     39
     40static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
     41{
     42	struct ddb *dev = i2c->dev;
     43	unsigned long stat;
     44	u32 val;
     45
     46	ddbwritel(dev, (adr << 9) | cmd, i2c->regs + I2C_COMMAND);
     47	stat = wait_for_completion_timeout(&i2c->completion, HZ);
     48	val = ddbreadl(dev, i2c->regs + I2C_COMMAND);
     49	if (stat == 0) {
     50		dev_err(dev->dev, "I2C timeout, card %d, port %d, link %u\n",
     51			dev->nr, i2c->nr, i2c->link);
     52		{
     53			u32 istat = ddbreadl(dev, INTERRUPT_STATUS);
     54
     55			dev_err(dev->dev, "DDBridge IRS %08x\n", istat);
     56			if (i2c->link) {
     57				u32 listat = ddbreadl(dev,
     58					DDB_LINK_TAG(i2c->link) |
     59					INTERRUPT_STATUS);
     60
     61				dev_err(dev->dev, "DDBridge link %u IRS %08x\n",
     62					i2c->link, listat);
     63			}
     64			if (istat & 1) {
     65				ddbwritel(dev, istat & 1, INTERRUPT_ACK);
     66			} else {
     67				u32 mon = ddbreadl(dev,
     68					i2c->regs + I2C_MONITOR);
     69
     70				dev_err(dev->dev, "I2C cmd=%08x mon=%08x\n",
     71					val, mon);
     72			}
     73		}
     74		return -EIO;
     75	}
     76	val &= 0x70000;
     77	if (val == 0x20000)
     78		dev_err(dev->dev, "I2C bus error\n");
     79	if (val)
     80		return -EIO;
     81	return 0;
     82}
     83
     84static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
     85			       struct i2c_msg msg[], int num)
     86{
     87	struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
     88	struct ddb *dev = i2c->dev;
     89	u8 addr = 0;
     90
     91	addr = msg[0].addr;
     92	if (msg[0].len > i2c->bsize)
     93		return -EIO;
     94	switch (num) {
     95	case 1:
     96		if (msg[0].flags & I2C_M_RD) {
     97			ddbwritel(dev, msg[0].len << 16,
     98				  i2c->regs + I2C_TASKLENGTH);
     99			if (ddb_i2c_cmd(i2c, addr, 3))
    100				break;
    101			ddbcpyfrom(dev, msg[0].buf,
    102				   i2c->rbuf, msg[0].len);
    103			return num;
    104		}
    105		ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len);
    106		ddbwritel(dev, msg[0].len, i2c->regs + I2C_TASKLENGTH);
    107		if (ddb_i2c_cmd(i2c, addr, 2))
    108			break;
    109		return num;
    110	case 2:
    111		if ((msg[0].flags & I2C_M_RD) == I2C_M_RD)
    112			break;
    113		if ((msg[1].flags & I2C_M_RD) != I2C_M_RD)
    114			break;
    115		if (msg[1].len > i2c->bsize)
    116			break;
    117		ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len);
    118		ddbwritel(dev, msg[0].len | (msg[1].len << 16),
    119			  i2c->regs + I2C_TASKLENGTH);
    120		if (ddb_i2c_cmd(i2c, addr, 1))
    121			break;
    122		ddbcpyfrom(dev, msg[1].buf,
    123			   i2c->rbuf,
    124			   msg[1].len);
    125		return num;
    126	default:
    127		break;
    128	}
    129	return -EIO;
    130}
    131
    132static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
    133{
    134	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
    135}
    136
    137static const struct i2c_algorithm ddb_i2c_algo = {
    138	.master_xfer   = ddb_i2c_master_xfer,
    139	.functionality = ddb_i2c_functionality,
    140};
    141
    142void ddb_i2c_release(struct ddb *dev)
    143{
    144	int i;
    145	struct ddb_i2c *i2c;
    146
    147	for (i = 0; i < dev->i2c_num; i++) {
    148		i2c = &dev->i2c[i];
    149		i2c_del_adapter(&i2c->adap);
    150	}
    151}
    152
    153static void i2c_handler(void *priv)
    154{
    155	struct ddb_i2c *i2c = (struct ddb_i2c *)priv;
    156
    157	complete(&i2c->completion);
    158}
    159
    160static int ddb_i2c_add(struct ddb *dev, struct ddb_i2c *i2c,
    161		       const struct ddb_regmap *regmap, int link,
    162		       int i, int num)
    163{
    164	struct i2c_adapter *adap;
    165
    166	i2c->nr = i;
    167	i2c->dev = dev;
    168	i2c->link = link;
    169	i2c->bsize = regmap->i2c_buf->size;
    170	i2c->wbuf = DDB_LINK_TAG(link) |
    171		(regmap->i2c_buf->base + i2c->bsize * i);
    172	i2c->rbuf = i2c->wbuf; /* + i2c->bsize / 2 */
    173	i2c->regs = DDB_LINK_TAG(link) |
    174		(regmap->i2c->base + regmap->i2c->size * i);
    175	ddbwritel(dev, I2C_SPEED_100, i2c->regs + I2C_TIMING);
    176	ddbwritel(dev, ((i2c->rbuf & 0xffff) << 16) | (i2c->wbuf & 0xffff),
    177		  i2c->regs + I2C_TASKADDRESS);
    178	init_completion(&i2c->completion);
    179
    180	adap = &i2c->adap;
    181	i2c_set_adapdata(adap, i2c);
    182#ifdef I2C_ADAP_CLASS_TV_DIGITAL
    183	adap->class = I2C_ADAP_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG;
    184#else
    185#ifdef I2C_CLASS_TV_ANALOG
    186	adap->class = I2C_CLASS_TV_ANALOG;
    187#endif
    188#endif
    189	snprintf(adap->name, I2C_NAME_SIZE, "ddbridge_%02x.%x.%x",
    190		 dev->nr, i2c->link, i);
    191	adap->algo = &ddb_i2c_algo;
    192	adap->algo_data = (void *)i2c;
    193	adap->dev.parent = dev->dev;
    194	return i2c_add_adapter(adap);
    195}
    196
    197int ddb_i2c_init(struct ddb *dev)
    198{
    199	int stat = 0;
    200	u32 i, j, num = 0, l, base;
    201	struct ddb_i2c *i2c;
    202	struct i2c_adapter *adap;
    203	const struct ddb_regmap *regmap;
    204
    205	for (l = 0; l < DDB_MAX_LINK; l++) {
    206		if (!dev->link[l].info)
    207			continue;
    208		regmap = dev->link[l].info->regmap;
    209		if (!regmap || !regmap->i2c)
    210			continue;
    211		base = regmap->irq_base_i2c;
    212		for (i = 0; i < regmap->i2c->num; i++) {
    213			if (!(dev->link[l].info->i2c_mask & (1 << i)))
    214				continue;
    215			i2c = &dev->i2c[num];
    216			ddb_irq_set(dev, l, i + base, i2c_handler, i2c);
    217			stat = ddb_i2c_add(dev, i2c, regmap, l, i, num);
    218			if (stat)
    219				break;
    220			num++;
    221		}
    222	}
    223	if (stat) {
    224		for (j = 0; j < num; j++) {
    225			i2c = &dev->i2c[j];
    226			adap = &i2c->adap;
    227			i2c_del_adapter(adap);
    228		}
    229	} else {
    230		dev->i2c_num = num;
    231	}
    232
    233	return stat;
    234}