brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 10c9ee3 Raw
385 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  cobalt I2C functions4 *5 *  Derived from cx18-i2c.c6 *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			40000079#define ALT_CPU_FREQ			8333333380 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}385