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

cobalt-i2c.c (8929B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  cobalt I2C functions
      4 *
      5 *  Derived from cx18-i2c.c
      6 *
      7 *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
      8 *  All rights reserved.
      9 */
     10
     11#include "cobalt-driver.h"
     12#include "cobalt-i2c.h"
     13
     14struct cobalt_i2c_regs {
     15	/* Clock prescaler register lo-byte */
     16	u8 prerlo;
     17	u8 dummy0[3];
     18	/* Clock prescaler register high-byte */
     19	u8 prerhi;
     20	u8 dummy1[3];
     21	/* Control register */
     22	u8 ctr;
     23	u8 dummy2[3];
     24	/* Transmit/Receive register */
     25	u8 txr_rxr;
     26	u8 dummy3[3];
     27	/* Command and Status register */
     28	u8 cr_sr;
     29	u8 dummy4[3];
     30};
     31
     32/* CTR[7:0] - Control register */
     33
     34/* I2C Core enable bit */
     35#define M00018_CTR_BITMAP_EN_MSK	(1 << 7)
     36
     37/* I2C Core interrupt enable bit */
     38#define M00018_CTR_BITMAP_IEN_MSK	(1 << 6)
     39
     40/* CR[7:0] - Command register */
     41
     42/* I2C start condition */
     43#define M00018_CR_BITMAP_STA_MSK	(1 << 7)
     44
     45/* I2C stop condition */
     46#define M00018_CR_BITMAP_STO_MSK	(1 << 6)
     47
     48/* I2C read from slave */
     49#define M00018_CR_BITMAP_RD_MSK		(1 << 5)
     50
     51/* I2C write to slave */
     52#define M00018_CR_BITMAP_WR_MSK		(1 << 4)
     53
     54/* I2C ack */
     55#define M00018_CR_BITMAP_ACK_MSK	(1 << 3)
     56
     57/* I2C Interrupt ack */
     58#define M00018_CR_BITMAP_IACK_MSK	(1 << 0)
     59
     60/* SR[7:0] - Status register */
     61
     62/* Receive acknowledge from slave */
     63#define M00018_SR_BITMAP_RXACK_MSK	(1 << 7)
     64
     65/* Busy, I2C bus busy (as defined by start / stop bits) */
     66#define M00018_SR_BITMAP_BUSY_MSK	(1 << 6)
     67
     68/* Arbitration lost - core lost arbitration */
     69#define M00018_SR_BITMAP_AL_MSK		(1 << 5)
     70
     71/* Transfer in progress */
     72#define M00018_SR_BITMAP_TIP_MSK	(1 << 1)
     73
     74/* Interrupt flag */
     75#define M00018_SR_BITMAP_IF_MSK		(1 << 0)
     76
     77/* Frequency, in Hz */
     78#define I2C_FREQUENCY			400000
     79#define ALT_CPU_FREQ			83333333
     80
     81static struct cobalt_i2c_regs __iomem *
     82cobalt_i2c_regs(struct cobalt *cobalt, unsigned idx)
     83{
     84	switch (idx) {
     85	case 0:
     86	default:
     87		return (struct cobalt_i2c_regs __iomem *)
     88			(cobalt->bar1 + COBALT_I2C_0_BASE);
     89	case 1:
     90		return (struct cobalt_i2c_regs __iomem *)
     91			(cobalt->bar1 + COBALT_I2C_1_BASE);
     92	case 2:
     93		return (struct cobalt_i2c_regs __iomem *)
     94			(cobalt->bar1 + COBALT_I2C_2_BASE);
     95	case 3:
     96		return (struct cobalt_i2c_regs __iomem *)
     97			(cobalt->bar1 + COBALT_I2C_3_BASE);
     98	case 4:
     99		return (struct cobalt_i2c_regs __iomem *)
    100			(cobalt->bar1 + COBALT_I2C_HSMA_BASE);
    101	}
    102}
    103
    104/* Do low-level i2c byte transfer.
    105 * Returns -1 in case of an error or 0 otherwise.
    106 */
    107static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs,
    108		struct i2c_adapter *adap, bool start, bool stop,
    109		u8 *data, u16 len)
    110{
    111	unsigned long start_time;
    112	int status;
    113	int cmd;
    114	int i;
    115
    116	for (i = 0; i < len; i++) {
    117		/* Setup data */
    118		iowrite8(data[i], &regs->txr_rxr);
    119
    120		/* Setup command */
    121		if (i == 0 && start) {
    122			/* Write + Start */
    123			cmd = M00018_CR_BITMAP_WR_MSK |
    124			      M00018_CR_BITMAP_STA_MSK;
    125		} else if (i == len - 1 && stop) {
    126			/* Write + Stop */
    127			cmd = M00018_CR_BITMAP_WR_MSK |
    128			      M00018_CR_BITMAP_STO_MSK;
    129		} else {
    130			/* Write only */
    131			cmd = M00018_CR_BITMAP_WR_MSK;
    132		}
    133
    134		/* Execute command */
    135		iowrite8(cmd, &regs->cr_sr);
    136
    137		/* Wait for transfer to complete (TIP = 0) */
    138		start_time = jiffies;
    139		status = ioread8(&regs->cr_sr);
    140		while (status & M00018_SR_BITMAP_TIP_MSK) {
    141			if (time_after(jiffies, start_time + adap->timeout))
    142				return -ETIMEDOUT;
    143			cond_resched();
    144			status = ioread8(&regs->cr_sr);
    145		}
    146
    147		/* Verify ACK */
    148		if (status & M00018_SR_BITMAP_RXACK_MSK) {
    149			/* NO ACK! */
    150			return -EIO;
    151		}
    152
    153		/* Verify arbitration */
    154		if (status & M00018_SR_BITMAP_AL_MSK) {
    155			/* Arbitration lost! */
    156			return -EIO;
    157		}
    158	}
    159	return 0;
    160}
    161
    162/* Do low-level i2c byte read.
    163 * Returns -1 in case of an error or 0 otherwise.
    164 */
    165static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs,
    166		struct i2c_adapter *adap, bool start, bool stop,
    167		u8 *data, u16 len)
    168{
    169	unsigned long start_time;
    170	int status;
    171	int cmd;
    172	int i;
    173
    174	for (i = 0; i < len; i++) {
    175		/* Setup command */
    176		if (i == 0 && start) {
    177			/* Read + Start */
    178			cmd = M00018_CR_BITMAP_RD_MSK |
    179			      M00018_CR_BITMAP_STA_MSK;
    180		} else if (i == len - 1 && stop) {
    181			/* Read + Stop */
    182			cmd = M00018_CR_BITMAP_RD_MSK |
    183			      M00018_CR_BITMAP_STO_MSK;
    184		} else {
    185			/* Read only */
    186			cmd = M00018_CR_BITMAP_RD_MSK;
    187		}
    188
    189		/* Last byte to read, no ACK */
    190		if (i == len - 1)
    191			cmd |= M00018_CR_BITMAP_ACK_MSK;
    192
    193		/* Execute command */
    194		iowrite8(cmd, &regs->cr_sr);
    195
    196		/* Wait for transfer to complete (TIP = 0) */
    197		start_time = jiffies;
    198		status = ioread8(&regs->cr_sr);
    199		while (status & M00018_SR_BITMAP_TIP_MSK) {
    200			if (time_after(jiffies, start_time + adap->timeout))
    201				return -ETIMEDOUT;
    202			cond_resched();
    203			status = ioread8(&regs->cr_sr);
    204		}
    205
    206		/* Verify arbitration */
    207		if (status & M00018_SR_BITMAP_AL_MSK) {
    208			/* Arbitration lost! */
    209			return -EIO;
    210		}
    211
    212		/* Store data */
    213		data[i] = ioread8(&regs->txr_rxr);
    214	}
    215	return 0;
    216}
    217
    218/* Generate stop condition on i2c bus.
    219 * The m00018 stop isn't doing the right thing (wrong timing).
    220 * So instead send a start condition, 8 zeroes and a stop condition.
    221 */
    222static int cobalt_stop(struct cobalt_i2c_regs __iomem *regs,
    223		struct i2c_adapter *adap)
    224{
    225	u8 data = 0;
    226
    227	return cobalt_tx_bytes(regs, adap, true, true, &data, 1);
    228}
    229
    230static int cobalt_xfer(struct i2c_adapter *adap,
    231			struct i2c_msg msgs[], int num)
    232{
    233	struct cobalt_i2c_data *data = adap->algo_data;
    234	struct cobalt_i2c_regs __iomem *regs = data->regs;
    235	struct i2c_msg *pmsg;
    236	unsigned short flags;
    237	int ret = 0;
    238	int i, j;
    239
    240	for (i = 0; i < num; i++) {
    241		int stop = (i == num - 1);
    242
    243		pmsg = &msgs[i];
    244		flags = pmsg->flags;
    245
    246		if (!(pmsg->flags & I2C_M_NOSTART)) {
    247			u8 addr = pmsg->addr << 1;
    248
    249			if (flags & I2C_M_RD)
    250				addr |= 1;
    251			if (flags & I2C_M_REV_DIR_ADDR)
    252				addr ^= 1;
    253			for (j = 0; j < adap->retries; j++) {
    254				ret = cobalt_tx_bytes(regs, adap, true, false,
    255						      &addr, 1);
    256				if (!ret)
    257					break;
    258				cobalt_stop(regs, adap);
    259			}
    260			if (ret < 0)
    261				return ret;
    262			ret = 0;
    263		}
    264		if (pmsg->flags & I2C_M_RD) {
    265			/* read bytes into buffer */
    266			ret = cobalt_rx_bytes(regs, adap, false, stop,
    267					pmsg->buf, pmsg->len);
    268			if (ret < 0)
    269				goto bailout;
    270		} else {
    271			/* write bytes from buffer */
    272			ret = cobalt_tx_bytes(regs, adap, false, stop,
    273					pmsg->buf, pmsg->len);
    274			if (ret < 0)
    275				goto bailout;
    276		}
    277	}
    278	ret = i;
    279
    280bailout:
    281	if (ret < 0)
    282		cobalt_stop(regs, adap);
    283	return ret;
    284}
    285
    286static u32 cobalt_func(struct i2c_adapter *adap)
    287{
    288	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
    289}
    290
    291/* template for i2c-bit-algo */
    292static const struct i2c_adapter cobalt_i2c_adap_template = {
    293	.name = "cobalt i2c driver",
    294	.algo = NULL,                   /* set by i2c-algo-bit */
    295	.algo_data = NULL,              /* filled from template */
    296	.owner = THIS_MODULE,
    297};
    298
    299static const struct i2c_algorithm cobalt_algo = {
    300	.master_xfer	= cobalt_xfer,
    301	.functionality	= cobalt_func,
    302};
    303
    304/* init + register i2c algo-bit adapter */
    305int cobalt_i2c_init(struct cobalt *cobalt)
    306{
    307	int i, err;
    308	int status;
    309	int prescale;
    310	unsigned long start_time;
    311
    312	cobalt_dbg(1, "i2c init\n");
    313
    314	/* Define I2C clock prescaler */
    315	prescale = ((ALT_CPU_FREQ) / (5 * I2C_FREQUENCY)) - 1;
    316
    317	for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
    318		struct cobalt_i2c_regs __iomem *regs =
    319			cobalt_i2c_regs(cobalt, i);
    320		struct i2c_adapter *adap = &cobalt->i2c_adap[i];
    321
    322		/* Disable I2C */
    323		iowrite8(M00018_CTR_BITMAP_EN_MSK, &regs->cr_sr);
    324		iowrite8(0, &regs->ctr);
    325		iowrite8(0, &regs->cr_sr);
    326
    327		start_time = jiffies;
    328		do {
    329			if (time_after(jiffies, start_time + HZ)) {
    330				if (cobalt_ignore_err) {
    331					adap->dev.parent = NULL;
    332					return 0;
    333				}
    334				return -ETIMEDOUT;
    335			}
    336			status = ioread8(&regs->cr_sr);
    337		} while (status & M00018_SR_BITMAP_TIP_MSK);
    338
    339		/* Disable I2C */
    340		iowrite8(0, &regs->ctr);
    341		iowrite8(0, &regs->cr_sr);
    342
    343		/* Calculate i2c prescaler */
    344		iowrite8(prescale & 0xff, &regs->prerlo);
    345		iowrite8((prescale >> 8) & 0xff, &regs->prerhi);
    346		/* Enable I2C, interrupts disabled */
    347		iowrite8(M00018_CTR_BITMAP_EN_MSK, &regs->ctr);
    348		/* Setup algorithm for adapter */
    349		cobalt->i2c_data[i].cobalt = cobalt;
    350		cobalt->i2c_data[i].regs = regs;
    351		*adap = cobalt_i2c_adap_template;
    352		adap->algo = &cobalt_algo;
    353		adap->algo_data = &cobalt->i2c_data[i];
    354		adap->retries = 3;
    355		sprintf(adap->name + strlen(adap->name),
    356				" #%d-%d", cobalt->instance, i);
    357		i2c_set_adapdata(adap, &cobalt->v4l2_dev);
    358		adap->dev.parent = &cobalt->pci_dev->dev;
    359		err = i2c_add_adapter(adap);
    360		if (err) {
    361			if (cobalt_ignore_err) {
    362				adap->dev.parent = NULL;
    363				return 0;
    364			}
    365			while (i--)
    366				i2c_del_adapter(&cobalt->i2c_adap[i]);
    367			return err;
    368		}
    369		cobalt_info("registered bus %s\n", adap->name);
    370	}
    371	return 0;
    372}
    373
    374void cobalt_i2c_exit(struct cobalt *cobalt)
    375{
    376	int i;
    377
    378	cobalt_dbg(1, "i2c exit\n");
    379
    380	for (i = 0; i < COBALT_NUM_ADAPTERS; i++) {
    381		cobalt_err("unregistered bus %s\n", cobalt->i2c_adap[i].name);
    382		i2c_del_adapter(&cobalt->i2c_adap[i]);
    383	}
    384}