/* Mantis PCI bridge driver Copyright (C) 2005 Manu Abraham (manu@kromtek.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include "mantis_common.h" #define I2C_HW_B_MANTIS 0x1c /* * Wait till we receive INT I2CRACK. I2CDONE will follow suit, * since we save the states, we would have both I2CRACK and I2CDONE set * we don't wait till eternity though. */ static int mantis_ack_wait(struct mantis_pci *mantis) { u8 timeout = 0; /* * I2CRACK will be reset when I2CDONE is set * * */ while (!(mantis->mantis_int_stat & MANTIS_INT_I2CRACK)) { udelay(1000); timeout++; dprintk(verbose, MANTIS_DEBUG, 0, "."); if (timeout > 50) return -1; } /* * Reset STATUS flags, before we leave. * since we save states. */ dprintk(verbose, MANTIS_DEBUG, 1, "Status ... MANTIS_INT_STAT=[0x%08x]", mantis->mantis_int_stat); mantis->mantis_int_stat &= (~MANTIS_INT_I2CRACK | ~MANTIS_INT_I2CDONE); return 0; } static inline void mantis_i2cint_set(struct mantis_pci *mantis) { /* * We don't have a mask for I2CRACK * I2CRACK is cleared on setting I2CDONE (?) */ // mmwrite(mmread(MANTIS_INT_STAT) & MANTIS_INT_I2CRACK, MANTIS_INT_STAT); mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK); udelay(1000); } static int mantis_i2c_pagewrite(struct mantis_pci *mantis, struct i2c_msg *msg) { u8 i; u32 txd = 0; dprintk(verbose, MANTIS_DEBUG, 1, "Writing to [0x%02x]", msg->addr); for (i = 0; i < msg->len; i++) { dprintk(verbose, MANTIS_DEBUG, 1, "Data=[0x%02x]", i, msg->buf[i]); txd |= (msg->addr << 25) | (msg->buf[i] << 8) | MANTIS_I2C_RATE_3 | MANTIS_I2C_STOP | MANTIS_I2C_PGMODE; if (i == 3) txd &= ~MANTIS_I2C_STOP; mantis_i2cint_set(mantis); mmwrite(txd, MANTIS_I2CDATA_CTL); if (mantis_ack_wait(mantis) < 0) { dprintk(verbose, MANTIS_DEBUG, 1, "ACK failed"); return -1; } udelay(10); } return 0; } /* FIXME! needs to be fixed, we need to put things back */ static int mantis_i2c_pageread(struct mantis_pci *mantis, struct i2c_msg *msg) { u8 i; u32 rxd = 0; for (i = 0; i < 2; i++) { rxd |= ((msg->addr << 25) | (1 << 24)) | MANTIS_I2C_RATE_3 | MANTIS_I2C_STOP | MANTIS_I2C_PGMODE; if (i == 1) rxd &= ~MANTIS_I2C_STOP; mmwrite(mmread(MANTIS_INT_STAT) | MANTIS_INT_I2CDONE, MANTIS_INT_STAT); mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK); udelay(1000); if (mantis_ack_wait(mantis) < 0) return -EIO; rxd = mmread(MANTIS_I2CDATA_CTL); udelay(500); } return 0; } ///* FIXME ! Right now writes only a byte */ static int mantis_i2c_writebyte(struct mantis_pci *mantis, struct i2c_msg *msg) { u32 txd = 0; mantis->mantis_int_stat = 0; txd = (msg->addr << 25) | (msg->buf[0] << 16) | (msg->buf[1] << 8) | MANTIS_I2C_RATE_3; dprintk(verbose, MANTIS_DEBUG, 1, "Writing to [0x%02x], Data=[%02x]", msg->addr, msg->buf[1]); mmwrite(txd, MANTIS_I2CDATA_CTL); if (mantis_ack_wait(mantis) < 0) { dprintk(verbose, MANTIS_DEBUG, 1, "Slave did not ACK !"); return -EIO; } return 0; } static int mantis_i2c_readbyte(struct mantis_pci *mantis, struct i2c_msg *msg) { u32 rxd; u16 i; u8 subaddr; /* * Clear saved previous states */ mantis->mantis_int_stat &= (~MANTIS_INT_I2CDONE | ~MANTIS_INT_I2CRACK); subaddr = msg->buf[0]; for (i = 0; i < msg->len; i++) { /* Rate settings */ rxd = ((msg->addr << 25) | (1 << 24)) | (subaddr << 16) | MANTIS_I2C_RATE_3; mmwrite(rxd, MANTIS_I2CDATA_CTL); /* * Enable INT_I2CDONE */ mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK); udelay(1000); /* Slave ACK */ if (mantis_ack_wait(mantis) < 0) return -EIO; rxd = mmread(MANTIS_I2CDATA_CTL); udelay(500); msg->buf[i] = (rxd >> 8) & 0xff; dprintk(verbose, MANTIS_DEBUG, 1, "Reading from [0x%02x], Data=[0x%02x]", msg->addr, i, msg->buf[i]); subaddr++; } return 0; } static int mantis_i2c_readbytes(struct mantis_pci *mantis, struct i2c_msg *msg) { u32 rxd; u16 i; mantis->mantis_int_stat = 0; for (i = 0; i < msg[1].len; i++) { /* Rate settings */ rxd = ((msg[0].addr << 25) | (1 << 24)) | (msg[0].buf[0] << 16) | MANTIS_I2C_RATE_3; mmwrite(rxd, MANTIS_I2CDATA_CTL); /* * Need to wait till I2CDONE, rather than slave I2CRACK */ mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE, MANTIS_INT_MASK); udelay(1000); if (mantis_ack_wait(mantis) < 0) return -EIO; rxd = mmread(MANTIS_I2CDATA_CTL); udelay(500); msg[1].buf[i] = (rxd >> 8) & 0xff; dprintk(verbose, MANTIS_DEBUG, 1, "Reading from [0x%02x], Data=[0x%02x]", msg[0].addr, i, msg[1].buf[i]); msg[0].buf[0]++; } return 0; } /* FIXME ! Right now writes only a byte */ static int mantis_i2c_writebytes(struct mantis_pci *mantis, struct i2c_msg *msg) { u32 txd = 0; mantis->mantis_int_stat = 0; txd = (msg->addr << 25) | (msg->buf[0] << 16) | (msg->buf[1] << 8) | MANTIS_I2C_RATE_3; dprintk(verbose, MANTIS_DEBUG, 1, "Writing to [0x%02x], Data=[%02x]", msg->addr, msg->buf[1]); mmwrite(txd, MANTIS_I2CDATA_CTL); if (mantis_ack_wait(mantis) < 0) return -EIO; return 0; } static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, int num) { int retval = 0; struct mantis_pci *mantis; mantis = i2c_get_adapdata(adapter); if (num == 4) { if (msg[0].flags & I2C_M_RD) { if ((retval = mantis_i2c_pageread(mantis, msg)) < 0) return retval; } else { if ((retval = mantis_i2c_pagewrite(mantis, msg)) < 0) return retval; } /* STV0299 operation (???) */ } else if (num == 2) { mantis_i2cint_set(mantis); /* Sanity check */ if (msg[0].buf != NULL && msg[1].buf != NULL) { /* I2C operation type */ if (msg[0].flags == 0 && msg[1].flags == I2C_M_RD) { /* Read */ if ((retval = mantis_i2c_readbytes(mantis, msg)) < 0) return retval; } else { /* Write */ if ((retval = mantis_i2c_writebytes(mantis, msg)) < 0) return retval; } } /* Normal case operation */ } else { mantis_i2cint_set(mantis); if (msg[0].flags & I2C_M_RD) { if ((retval = mantis_i2c_readbyte(mantis, msg)) < 0) return retval; } else { if ((retval = mantis_i2c_writebyte(mantis, msg)) < 0) return retval; } } return num; } static __u32 mantis_i2c_func(struct i2c_adapter *adapter) { return I2C_FUNC_SMBUS_EMUL; } static int mantis_attach_inform(struct i2c_client *client) { struct mantis_pci *mantis; mantis = i2c_get_adapdata(client->adapter); dprintk(verbose, MANTIS_ERROR, 1, "mantis: %s i2c attach [addr = 0x%x, client=%s]", client->driver->name, client->addr, i2c_clientname(client)); return 0; } static struct i2c_algorithm mantis_algo = { .name = "Mantis I2C", .id = I2C_HW_B_MANTIS, .master_xfer = mantis_i2c_xfer, .functionality = mantis_i2c_func, }; static struct i2c_adapter mantis_i2c_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_TV_DIGITAL, .algo = &mantis_algo, .client_register = mantis_attach_inform, }; int __devinit mantis_i2c_init(struct mantis_pci *mantis) { memcpy(&mantis->adapter, &mantis_i2c_adapter, sizeof (mantis_i2c_adapter)); i2c_set_adapdata(&mantis->adapter, mantis); mantis->i2c_rc = i2c_add_adapter(&mantis->adapter); if (mantis->i2c_rc < 0) return mantis->i2c_rc; dprintk(verbose, MANTIS_DEBUG, 1, "Initializing I2C .."); /* * I2CRACK will be reset when I2CDONE is set * Clear and setup I2C int mask */ mmwrite(mmread(MANTIS_INT_STAT) & ( ~MANTIS_INT_I2CDONE | ~MANTIS_INT_I2CRACK), MANTIS_INT_STAT); mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_I2CDONE | MANTIS_INT_I2CRACK, MANTIS_INT_MASK); dprintk(verbose, MANTIS_DEBUG, 1, "INT_STAT=[0x%08x], INT_MASK=[0x%08x]", mmread(MANTIS_INT_STAT), mmread(MANTIS_INT_MASK)); return 0; } int __devexit mantis_i2c_exit(struct mantis_pci *mantis) { dprintk(verbose, MANTIS_DEBUG, 1, "Removing I2C adapter"); return i2c_del_adapter(&mantis->adapter); }