Quelle cobalt-i2c.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-only
/*
* cobalt I2C functions
*
* Derived from cx18 - i2c . c
*
* Copyright 2012 - 2015 Cisco Systems , Inc . and / or its affiliates .
* All rights reserved .
*/
#include "cobalt-driver.h"
#include "cobalt-i2c.h"
struct cobalt_i2c_regs {
/* Clock prescaler register lo-byte */
u8 prerlo;
u8 dummy0[3 ];
/* Clock prescaler register high-byte */
u8 prerhi;
u8 dummy1[3 ];
/* Control register */
u8 ctr;
u8 dummy2[3 ];
/* Transmit/Receive register */
u8 txr_rxr;
u8 dummy3[3 ];
/* Command and Status register */
u8 cr_sr;
u8 dummy4[3 ];
};
/* CTR[7:0] - Control register */
/* I2C Core enable bit */
#define M00018_CTR_BITMAP_EN_MSK (1 << 7 )
/* I2C Core interrupt enable bit */
#define M00018_CTR_BITMAP_IEN_MSK (1 << 6 )
/* CR[7:0] - Command register */
/* I2C start condition */
#define M00018_CR_BITMAP_STA_MSK (1 << 7 )
/* I2C stop condition */
#define M00018_CR_BITMAP_STO_MSK (1 << 6 )
/* I2C read from slave */
#define M00018_CR_BITMAP_RD_MSK (1 << 5 )
/* I2C write to slave */
#define M00018_CR_BITMAP_WR_MSK (1 << 4 )
/* I2C ack */
#define M00018_CR_BITMAP_ACK_MSK (1 << 3 )
/* I2C Interrupt ack */
#define M00018_CR_BITMAP_IACK_MSK (1 << 0 )
/* SR[7:0] - Status register */
/* Receive acknowledge from slave */
#define M00018_SR_BITMAP_RXACK_MSK (1 << 7 )
/* Busy, I2C bus busy (as defined by start / stop bits) */
#define M00018_SR_BITMAP_BUSY_MSK (1 << 6 )
/* Arbitration lost - core lost arbitration */
#define M00018_SR_BITMAP_AL_MSK (1 << 5 )
/* Transfer in progress */
#define M00018_SR_BITMAP_TIP_MSK (1 << 1 )
/* Interrupt flag */
#define M00018_SR_BITMAP_IF_MSK (1 << 0 )
/* Frequency, in Hz */
#define I2C_FREQUENCY 400000
#define ALT_CPU_FREQ 83333333
static struct cobalt_i2c_regs __iomem *
cobalt_i2c_regs(struct cobalt *cobalt, unsigned idx)
{
switch (idx) {
case 0 :
default :
return (struct cobalt_i2c_regs __iomem *)
(cobalt->bar1 + COBALT_I2C_0_BASE);
case 1 :
return (struct cobalt_i2c_regs __iomem *)
(cobalt->bar1 + COBALT_I2C_1_BASE);
case 2 :
return (struct cobalt_i2c_regs __iomem *)
(cobalt->bar1 + COBALT_I2C_2_BASE);
case 3 :
return (struct cobalt_i2c_regs __iomem *)
(cobalt->bar1 + COBALT_I2C_3_BASE);
case 4 :
return (struct cobalt_i2c_regs __iomem *)
(cobalt->bar1 + COBALT_I2C_HSMA_BASE);
}
}
/* Do low-level i2c byte transfer.
* Returns - 1 in case of an error or 0 otherwise .
*/
static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs,
struct i2c_adapter *adap, bool start, bool stop,
u8 *data, u16 len)
{
unsigned long start_time;
int status;
int cmd;
int i;
for (i = 0 ; i < len; i++) {
/* Setup data */
iowrite8(data[i], ®s->txr_rxr);
/* Setup command */
if (i == 0 && start) {
/* Write + Start */
cmd = M00018_CR_BITMAP_WR_MSK |
M00018_CR_BITMAP_STA_MSK;
} else if (i == len - 1 && stop) {
/* Write + Stop */
cmd = M00018_CR_BITMAP_WR_MSK |
M00018_CR_BITMAP_STO_MSK;
} else {
/* Write only */
cmd = M00018_CR_BITMAP_WR_MSK;
}
/* Execute command */
iowrite8(cmd, ®s->cr_sr);
/* Wait for transfer to complete (TIP = 0) */
start_time = jiffies;
status = ioread8(®s->cr_sr);
while (status & M00018_SR_BITMAP_TIP_MSK) {
if (time_after(jiffies, start_time + adap->timeout))
return -ETIMEDOUT;
cond_resched();
status = ioread8(®s->cr_sr);
}
/* Verify ACK */
if (status & M00018_SR_BITMAP_RXACK_MSK) {
/* NO ACK! */
return -EIO;
}
/* Verify arbitration */
if (status & M00018_SR_BITMAP_AL_MSK) {
/* Arbitration lost! */
return -EIO;
}
}
return 0 ;
}
/* Do low-level i2c byte read.
* Returns - 1 in case of an error or 0 otherwise .
*/
static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs,
struct i2c_adapter *adap, bool start, bool stop,
u8 *data, u16 len)
{
unsigned long start_time;
int status;
int cmd;
int i;
for (i = 0 ; i < len; i++) {
/* Setup command */
if (i == 0 && start) {
/* Read + Start */
cmd = M00018_CR_BITMAP_RD_MSK |
M00018_CR_BITMAP_STA_MSK;
} else if (i == len - 1 && stop) {
/* Read + Stop */
cmd = M00018_CR_BITMAP_RD_MSK |
M00018_CR_BITMAP_STO_MSK;
} else {
/* Read only */
cmd = M00018_CR_BITMAP_RD_MSK;
}
/* Last byte to read, no ACK */
if (i == len - 1 )
cmd |= M00018_CR_BITMAP_ACK_MSK;
/* Execute command */
iowrite8(cmd, ®s->cr_sr);
/* Wait for transfer to complete (TIP = 0) */
start_time = jiffies;
status = ioread8(®s->cr_sr);
while (status & M00018_SR_BITMAP_TIP_MSK) {
if (time_after(jiffies, start_time + adap->timeout))
return -ETIMEDOUT;
cond_resched();
status = ioread8(®s->cr_sr);
}
/* Verify arbitration */
if (status & M00018_SR_BITMAP_AL_MSK) {
/* Arbitration lost! */
return -EIO;
}
/* Store data */
data[i] = ioread8(®s->txr_rxr);
}
return 0 ;
}
/* Generate stop condition on i2c bus.
* The m00018 stop isn ' t doing the right thing ( wrong timing ) .
* So instead send a start condition , 8 zeroes and a stop condition .
*/
static int cobalt_stop(struct cobalt_i2c_regs __iomem *regs,
struct i2c_adapter *adap)
{
u8 data = 0 ;
return cobalt_tx_bytes(regs, adap, true , true , &data, 1 );
}
static int cobalt_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)
{
struct cobalt_i2c_data *data = adap->algo_data;
struct cobalt_i2c_regs __iomem *regs = data->regs;
struct i2c_msg *pmsg;
unsigned short flags;
int ret = 0 ;
int i, j;
for (i = 0 ; i < num; i++) {
int stop = (i == num - 1 );
pmsg = &msgs[i];
flags = pmsg->flags;
if (!(pmsg->flags & I2C_M_NOSTART)) {
u8 addr = pmsg->addr << 1 ;
if (flags & I2C_M_RD)
addr |= 1 ;
if (flags & I2C_M_REV_DIR_ADDR)
addr ^= 1 ;
for (j = 0 ; j < adap->retries; j++) {
ret = cobalt_tx_bytes(regs, adap, true , false ,
&addr, 1 );
if (!ret)
break ;
cobalt_stop(regs, adap);
}
if (ret < 0 )
return ret;
ret = 0 ;
}
if (pmsg->flags & I2C_M_RD) {
/* read bytes into buffer */
ret = cobalt_rx_bytes(regs, adap, false , stop,
pmsg->buf, pmsg->len);
if (ret < 0 )
goto bailout;
} else {
/* write bytes from buffer */
ret = cobalt_tx_bytes(regs, adap, false , stop,
pmsg->buf, pmsg->len);
if (ret < 0 )
goto bailout;
}
}
ret = i;
bailout:
if (ret < 0 )
cobalt_stop(regs, adap);
return ret;
}
static u32 cobalt_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
/* template for i2c-bit-algo */
static const struct i2c_adapter cobalt_i2c_adap_template = {
.name = "cobalt i2c driver" ,
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
.owner = THIS_MODULE,
};
static const struct i2c_algorithm cobalt_algo = {
.master_xfer = cobalt_xfer,
.functionality = cobalt_func,
};
/* init + register i2c algo-bit adapter */
int cobalt_i2c_init(struct cobalt *cobalt)
{
int i, err;
int status;
int prescale;
unsigned long start_time;
cobalt_dbg(1 , "i2c init\n" );
/* Define I2C clock prescaler */
prescale = ((ALT_CPU_FREQ) / (5 * I2C_FREQUENCY)) - 1 ;
for (i = 0 ; i < COBALT_NUM_ADAPTERS; i++) {
struct cobalt_i2c_regs __iomem *regs =
cobalt_i2c_regs(cobalt, i);
struct i2c_adapter *adap = &cobalt->i2c_adap[i];
/* Disable I2C */
iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->cr_sr);
iowrite8(0 , ®s->ctr);
iowrite8(0 , ®s->cr_sr);
start_time = jiffies;
do {
if (time_after(jiffies, start_time + HZ)) {
if (cobalt_ignore_err) {
adap->dev.parent = NULL;
return 0 ;
}
return -ETIMEDOUT;
}
status = ioread8(®s->cr_sr);
} while (status & M00018_SR_BITMAP_TIP_MSK);
/* Disable I2C */
iowrite8(0 , ®s->ctr);
iowrite8(0 , ®s->cr_sr);
/* Calculate i2c prescaler */
iowrite8(prescale & 0 xff, ®s->prerlo);
iowrite8((prescale >> 8 ) & 0 xff, ®s->prerhi);
/* Enable I2C, interrupts disabled */
iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->ctr);
/* Setup algorithm for adapter */
cobalt->i2c_data[i].cobalt = cobalt;
cobalt->i2c_data[i].regs = regs;
*adap = cobalt_i2c_adap_template;
adap->algo = &cobalt_algo;
adap->algo_data = &cobalt->i2c_data[i];
adap->retries = 3 ;
sprintf(adap->name + strlen(adap->name),
" #%d-%d" , cobalt->instance, i);
i2c_set_adapdata(adap, &cobalt->v4l2_dev);
adap->dev.parent = &cobalt->pci_dev->dev;
err = i2c_add_adapter(adap);
if (err) {
if (cobalt_ignore_err) {
adap->dev.parent = NULL;
return 0 ;
}
while (i--)
i2c_del_adapter(&cobalt->i2c_adap[i]);
return err;
}
cobalt_info("registered bus %s\n" , adap->name);
}
return 0 ;
}
void cobalt_i2c_exit(struct cobalt *cobalt)
{
int i;
cobalt_dbg(1 , "i2c exit\n" );
for (i = 0 ; i < COBALT_NUM_ADAPTERS; i++) {
cobalt_err("unregistered bus %s\n" , cobalt->i2c_adap[i].name);
i2c_del_adapter(&cobalt->i2c_adap[i]);
}
}
Messung V0.5 in Prozent C=96 H=92 G=93
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland
2026-10-11
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1127926
#Domains=2039723