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], ®s->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, ®s->cr_sr);136 137 /* Wait for transfer to complete (TIP = 0) */138 start_time = jiffies;139 status = ioread8(®s->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(®s->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, ®s->cr_sr);195 196 /* Wait for transfer to complete (TIP = 0) */197 start_time = jiffies;198 status = ioread8(®s->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(®s->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(®s->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, ®s->cr_sr);324 iowrite8(0, ®s->ctr);325 iowrite8(0, ®s->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(®s->cr_sr);337 } while (status & M00018_SR_BITMAP_TIP_MSK);338 339 /* Disable I2C */340 iowrite8(0, ®s->ctr);341 iowrite8(0, ®s->cr_sr);342 343 /* Calculate i2c prescaler */344 iowrite8(prescale & 0xff, ®s->prerlo);345 iowrite8((prescale >> 8) & 0xff, ®s->prerhi);346 /* Enable I2C, interrupts disabled */347 iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->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