123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- /*
- * cobalt I2C functions
- *
- * Derived from cx18-i2c.c
- *
- * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
- * All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- #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 != 0) {
- /* Write + Start */
- cmd = M00018_CR_BITMAP_WR_MSK |
- M00018_CR_BITMAP_STA_MSK;
- } else if (i == len - 1 && stop != 0) {
- /* 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 != 0) {
- /* Read + Start */
- cmd = M00018_CR_BITMAP_RD_MSK |
- M00018_CR_BITMAP_STA_MSK;
- } else if (i == len - 1 && stop != 0) {
- /* 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 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 & 0xff, ®s->prerlo);
- iowrite8((prescale >> 8) & 0xff, ®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]);
- }
- }
|