On 22/10/10 15:05, Alan Cox wrote: > I think this now addresses all the points raised in previous review, as well I'll go through and have a quick look through this to see if there is anything else to look at. > +/* Tables use: 0 Moorestown, 1 Medfield */ > +#define NUM_PLATFORMS 2 > +enum platform_enum { > + MOORESTOWN = 0, > + MEDFIELD = 1, > +}; > + > +struct intel_mid_i2c_private { > + struct i2c_adapter adap; > + /* Device for power management */ > + struct device *dev; > + /* Register base address */ > + void __iomem *base; > + /* Speed mode */ > + int speed; > + /* Transaction completion wait */ > + struct completion complete; > + /* Saved abort reason */ > + int abort; > + /* Working buffer */ > + u8 *rx_buf; > + int rx_buf_len; > + /* Adapter state machine condition */ > + int status; > + /* Message being processed */ > + struct i2c_msg *msg; > + /* Which Intel MID device are we today */ > + enum platform_enum platform; > + /* Serialize transactions */ > + struct mutex lock; > +}; would have prefered this as kerneldoc. > + > +#define NUM_SPEEDS 3 > + > +#define ACTIVE 0 > +#define STANDBY 1 > + > +#define STATUS_IDLE 0 > +#define STATUS_READ_START 1 > +#define STATUS_READ_IN_PROGRESS 2 > +#define STATUS_READ_SUCCESS 3 > +#define STATUS_WRITE_START 4 > +#define STATUS_WRITE_SUCCESS 5 > +#define STATUS_XFER_ABORT 6 > +#define STATUS_STANDBY 7 would have been nicer as an enum. > +/** > + * intel_mid_i2c_disable - Disable I2C controller > + * @adap: struct pointer to i2c_adapter > + * > + * Return Value: > + * 0 success > + * -EBUSY if device is busy > + * -ETIMEDOUT if i2c cannot be disabled within the given time > + * > + * I2C bus state should be checked prior to disabling the hardware. If bus is > + * not in idle state, an errno is returned. Write "0" to IC_ENABLE to disable > + * I2C controller. > + */ > +static int intel_mid_i2c_disable(struct i2c_adapter *adap) > +{ > + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); > + int err = 0; > + int count = 0; > + int ret1, ret2; > + static const u16 delay[NUM_SPEEDS] = {100, 25, 3}; > + > + /* Set IC_ENABLE to 0 */ > + writel(0, i2c->base + IC_ENABLE); > + > + /* Check if device is busy */ > + dev_dbg(&adap->dev, "mrst i2c disable\n"); > + while ((ret1 = readl(i2c->base + IC_ENABLE_STATUS) & 0x1) > + || (ret2 = readl(i2c->base + IC_STATUS) & 0x1)) { > + udelay(delay[i2c->speed]); > + writel(0, i2c->base + IC_ENABLE); > + dev_dbg(&adap->dev, "i2c is busy, count is %d speed %d\n", > + count, i2c->speed); > + if (count++ > 10) { > + err = -ETIMEDOUT; > + break; > + } > + } > + > + /* Clear all interrupts */ > + readl(i2c->base + IC_CLR_INTR); > + readl(i2c->base + IC_CLR_STOP_DET); > + readl(i2c->base + IC_CLR_START_DET); > + readl(i2c->base + IC_CLR_ACTIVITY); > + readl(i2c->base + IC_CLR_TX_ABRT); > + readl(i2c->base + IC_CLR_RX_OVER); > + readl(i2c->base + IC_CLR_RX_UNDER); > + readl(i2c->base + IC_CLR_TX_OVER); > + readl(i2c->base + IC_CLR_RX_DONE); > + readl(i2c->base + IC_CLR_GEN_CALL); > + > + /* Disable all interupts */ > + writel(0x0000, i2c->base + IC_INTR_MASK); > + > + return err; > +} > + > +/** > + * intel_mid_i2c_hwinit - Initialize the I2C hardware registers > + * @dev: pci device struct pointer > + * > + * This function will be called in intel_mid_i2c_probe() before device > + * registration. > + * > + * Return Values: > + * 0 success > + * -EBUSY i2c cannot be disabled > + * -ETIMEDOUT i2c cannot be disabled > + * -EFAULT If APB data width is not 32-bit wide > + * > + * I2C should be disabled prior to other register operation. If failed, an > + * errno is returned. Mask and Clear all interrpts, this should be done at > + * first. Set common registers which will not be modified during normal > + * transfers, including: controll register, FIFO threshold and clock freq. > + * Check APB data width at last. > + */ > +static int intel_mid_i2c_hwinit(struct intel_mid_i2c_private *i2c) > +{ > + int err; > + > + static const u16 hcnt[NUM_PLATFORMS][NUM_SPEEDS] = { > + { 0x75, 0x15, 0x07 }, > + { 0x04c, 0x10, 0x06 } > + }; > + static const u16 lcnt[NUM_PLATFORMS][NUM_SPEEDS] = { > + { 0x7C, 0x21, 0x0E }, > + { 0x053, 0x19, 0x0F } > + }; > + > + /* Disable i2c first */ > + err = intel_mid_i2c_disable(&i2c->adap); > + if (err) > + return err; > + > + /* > + * Setup clock frequency and speed mode > + * Enable restart condition, > + * enable master FSM, disable slave FSM, > + * use target address when initiating transfer > + */ > + > + writel((i2c->speed + 1) << 1 | SLV_DIS | RESTART | MASTER_EN, > + i2c->base + IC_CON); > + writel(hcnt[i2c->platform][i2c->speed], > + i2c->base + (IC_SS_SCL_HCNT + (i2c->speed << 3))); > + writel(lcnt[i2c->platform][i2c->speed], > + i2c->base + (IC_SS_SCL_LCNT + (i2c->speed << 3))); > + > + /* Set tranmit & receive FIFO threshold to zero */ > + writel(0x0, i2c->base + IC_RX_TL); > + writel(0x0, i2c->base + IC_TX_TL); > + > + return 0; > +} > + > +/** > + * intel_mid_i2c_func - Return the supported three I2C operations. > + * @adapter: i2c_adapter struct pointer > + */ > +static u32 intel_mid_i2c_func(struct i2c_adapter *adapter) > +{ > + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; > +} > + > +/** > + * intel_mid_i2c_address_neq - To check if the addresses for different i2c messages > + * are equal. > + * @p1: first i2c_msg > + * @p2: second i2c_msg > + * > + * Return Values: > + * 0 if addresses are equal > + * 1 if not equal > + * > + * Within a single transfer, the I2C client may need to send its address more > + * than once. So a check if the addresses match is needed. > + */ > +static inline bool intel_mid_i2c_address_neq(const struct i2c_msg *p1, > + const struct i2c_msg *p2) > +{ > + if (p1->addr != p2->addr) > + return 1; > + if ((p1->flags ^ p2->flags) & I2C_M_TEN) > + return 1; > + return 0; > +} > + > +/** > + * intel_mid_i2c_abort - To handle transfer abortions and print error messages. > + * @adap: i2c_adapter struct pointer > + * > + * By reading register IC_TX_ABRT_SOURCE, various transfer errors can be > + * distingushed. At present, no circumstances have been found out that > + * multiple errors would be occured simutaneously, so we simply use the > + * register value directly. > + * > + * At last the error bits are cleared. (Note clear ABRT_SBYTE_NORSTRT bit need > + * a few extra steps) > + */ > +static void intel_mid_i2c_abort(struct intel_mid_i2c_private *i2c) > +{ > + /* Read about source register */ > + int abort = i2c->abort; > + struct i2c_adapter *adap = &i2c->adap; > + > + /* Single transfer error check: > + * According to databook, TX/RX FIFOs would be flushed when > + * the abort interrupt occured. > + */ > + if (abort & ABRT_MASTER_DIS) > + dev_err(&adap->dev, > + "initiate master operation with master mode disabled.\n"); > + if (abort & ABRT_10B_RD_NORSTRT) > + dev_err(&adap->dev, > + "RESTART disabled and master sent READ cmd in 10-bit addressing.\n"); > + > + if (abort & ABRT_SBYTE_NORSTRT) { > + dev_err(&adap->dev, > + "RESTART disabled and user is trying to send START byte.\n"); > + writel(~ABRT_SBYTE_NORSTRT, i2c->base + IC_TX_ABRT_SOURCE); > + writel(RESTART, i2c->base + IC_CON); > + writel(~IC_TAR_SPECIAL, i2c->base + IC_TAR); > + } > + > + if (abort & ABRT_SBYTE_ACKDET) > + dev_err(&adap->dev, > + "START byte was acknowledged.\n"); surely the start+addr byte has to be acked? > + if (abort & ABRT_TXDATA_NOACK) > + dev_err(&adap->dev, > + "No acknowledgement received from slave.\n"); you'll get a pile of errors if the eeprom driver uses this method to test for eeprom busy. you may want to push this one back to dev_dbg. > + if (abort & ABRT_10ADDR2_NOACK) > + dev_dbg(&adap->dev, > + "The 2nd address byte of the 10-bit address was not acknowledged.\n"); > + if (abort & ABRT_10ADDR1_NOACK) > + dev_dbg(&adap->dev, > + "The 1st address byte of 10-bit address was not acknowledged.\n"); > + if (abort & ABRT_7B_ADDR_NOACK) > + dev_dbg(&adap->dev, > + "I2C slave device not acknowledged.\n"); > + /* Clear TX_ABRT bit */ > + readl(i2c->base + IC_CLR_TX_ABRT); > + i2c->status = STATUS_XFER_ABORT; > +} > + > +/** > + * xfer_read - Internal function to implement master read transfer. > + * @adap: i2c_adapter struct pointer > + * @buf: buffer in i2c_msg > + * @length: number of bytes to be read > + * > + * Return Values: > + * 0 if the read transfer succeeds > + * -ETIMEDOUT if cannot read the "raw" interrupt register > + * -EINVAL if a transfer abort occurred > + * > + * For every byte, a "READ" command will be loaded into IC_DATA_CMD prior to > + * data transfer. The actual "read" operation will be performed if an RX_FULL > + * interrupt occurred. > + * > + * Note there may be two interrupt signals captured, one should read > + * IC_RAW_INTR_STAT to separate between errors and actual data. > + */ > +static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) > +{ > + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); > + int i = length; > + int err; > + > + if (length >= 256) { > + dev_err(&adap->dev, > + "I2C FIFO cannot support larger than 256 bytes\n"); > + return -EMSGSIZE; > + } > + > + INIT_COMPLETION(i2c->complete); > + > + readl(i2c->base + IC_CLR_INTR); > + writel(0x0044, i2c->base + IC_INTR_MASK); > + > + i2c->status = STATUS_READ_START; > + > + while (i--) > + writel(IC_RD, i2c->base + IC_DATA_CMD); > + > + i2c->status = STATUS_READ_START; > + err = wait_for_completion_interruptible_timeout(&i2c->complete, HZ); > + if (!err) { > + dev_err(&adap->dev, "Timeout for ACK from I2C slave device\n"); > + intel_mid_i2c_hwinit(i2c); > + return -ETIMEDOUT; > + } else { as a note, you really didn't need the else here, you exit with the return at the end of the block. > + if (i2c->status == STATUS_READ_SUCCESS) > + return 0; > + else > + return -EIO; > + } > +} > +static int intel_mid_i2c_setup(struct i2c_adapter *adap, struct i2c_msg *pmsg) > +{ > + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); > + int err; > + u32 reg; > + u32 bit_mask; > + u32 mode; > + > + /* Disable device first */ > + err = intel_mid_i2c_disable(adap); > + if (err) { > + dev_err(&adap->dev, > + "Cannot disable i2c controller, timeout\n"); > + return err; > + } > + > + mode = (1 + i2c->speed) << 1; > + /* set the speed mode */ > + reg = readl(i2c->base + IC_CON); > + if ((reg & 0x06) != mode) { > + dev_dbg(&adap->dev, "set mode %d\n", i2c->speed); > + writel((reg & ~0x6) | mode, i2c->base + IC_CON); > + } > + > + reg = readl(i2c->base + IC_CON); > + /* use 7-bit addressing */ > + if (pmsg->flags & I2C_M_TEN) { > + if ((reg & ADDR_10BIT) != ADDR_10BIT) { > + dev_dbg(&adap->dev, "set i2c 10 bit address mode\n"); > + writel(reg | ADDR_10BIT, i2c->base + IC_CON); > + } > + } else { > + if ((reg & ADDR_10BIT) != 0x0) { > + dev_dbg(&adap->dev, "set i2c 7 bit address mode\n"); > + writel(reg & ~ADDR_10BIT, i2c->base + IC_CON); > + } > + } > + /* enable restart conditions */ > + reg = readl(i2c->base + IC_CON); > + if ((reg & RESTART) != RESTART) { > + dev_dbg(&adap->dev, "enable restart conditions\n"); > + writel(reg | RESTART, i2c->base + IC_CON); > + } > + > + /* enable master FSM */ > + reg = readl(i2c->base + IC_CON); > + dev_dbg(&adap->dev, "ic_con reg is 0x%x\n", reg); > + writel(reg | MASTER_EN, i2c->base + IC_CON); > + if ((reg & SLV_DIS) != SLV_DIS) { > + dev_dbg(&adap->dev, "enable master FSM\n"); > + writel(reg | SLV_DIS, i2c->base + IC_CON); > + dev_dbg(&adap->dev, "ic_con reg is 0x%x\n", reg); > + } > + > + /* use target address when initiating transfer */ > + reg = readl(i2c->base + IC_TAR); > + bit_mask = IC_TAR_SPECIAL | IC_TAR_GC_OR_START; > + > + if ((reg & bit_mask) != 0x0) { > + dev_dbg(&adap->dev, > + "WR: use target address when intiating transfer, i2c_tx_target\n"); > + writel(reg & ~bit_mask, i2c->base + IC_TAR); > + } > + > + /* set target address to the I2C slave address */ > + dev_dbg(&adap->dev, > + "set target address to the I2C slave address, addr is %x\n", > + pmsg->addr); > + writel(pmsg->addr | (pmsg->flags & I2C_M_TEN ? IC_TAR_10BIT_ADDR : 0), > + i2c->base + IC_TAR); > + > + /* Enable I2C controller */ > + writel(ENABLE, i2c->base + IC_ENABLE); > + > + return 0; > +} > + > +/** > + * intel_mid_i2c_xfer - Main master transfer routine. > + * @adap: i2c_adapter struct pointer > + * @pmsg: i2c_msg struct pointer > + * @num: number of i2c_msg > + * > + * Return Values: > + * + number of messages transfered > + * -ETIMEDOUT If cannot disable I2C controller or read IC_STATUS > + * -EINVAL If the address in i2c_msg is invalid > + * > + * This function will be registered in i2c-core and exposed to external > + * I2C clients. > + * 1. Disable I2C controller > + * 2. Unmask three interrupts: RX_FULL, TX_EMPTY, TX_ABRT > + * 3. Check if address in i2c_msg is valid > + * 4. Enable I2C controller > + * 5. Perform real transfer (call xfer_read or xfer_write) > + * 6. Wait until the current transfer is finished (check bus state) > + * 7. Mask and clear all interrupts > + */ > +static int intel_mid_i2c_xfer(struct i2c_adapter *adap, > + struct i2c_msg *pmsg, > + int num) > +{ > + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); > + int i, err; > + > + pm_runtime_get(i2c->dev); > + > + mutex_lock(&i2c->lock); > + dev_dbg(&adap->dev, "intel_mid_i2c_xfer, process %d msg(s)\n", num); > + dev_dbg(&adap->dev, "slave address is %x\n", pmsg->addr); > + > + if (i2c->status != STATUS_IDLE) { > + dev_err(&adap->dev, "Adapter %d in transfer/standby\n", > + adap->nr); > + mutex_unlock(&i2c->lock); > + pm_runtime_put(i2c->dev); > + return -1; > + } > + > + /* if number of messages equal 0*/ > + if (num == 0) { > + mutex_unlock(&i2c->lock); > + pm_runtime_put(i2c->dev); > + return 0; > + } you could have moved the check above the pm_runtime_get and avoided a pile of checks. not necessary to fix though. > + > + for (i = 1; i < num; i++) { > + /* Message address equal? */ > + if (unlikely(intel_mid_i2c_address_neq(&pmsg[0], &pmsg[i]))) { > + dev_err(&adap->dev, "Invalid address in msg[%d]\n", i); > + mutex_unlock(&i2c->lock); > + pm_runtime_put(i2c->dev); > + return -EINVAL; > + } > + } can you really not support having a different address in one of the message segments? surely if sending a restart then this should be possible? > + if (intel_mid_i2c_setup(adap, pmsg)) { > + mutex_unlock(&i2c->lock); > + pm_runtime_put(i2c->dev); > + return -EINVAL; > + } > + > + for (i = 0; i < num; i++) { > + i2c->msg = pmsg; > + i2c->status = STATUS_IDLE; > + /* Read or Write */ > + if (pmsg->flags & I2C_M_RD) { > + dev_dbg(&adap->dev, "I2C_M_RD\n"); > + err = xfer_read(adap, pmsg->buf, pmsg->len); > + } else { > + dev_dbg(&adap->dev, "I2C_M_WR\n"); > + err = xfer_write(adap, pmsg->buf, pmsg->len); > + } > + if (err < 0) > + goto err_1; > + dev_dbg(&adap->dev, "msg[%d] transfer complete\n", i); > + pmsg++; /* next message */ > + } > + goto exit; > + > +err_1: > + i = err; firstly, why assing i=err, you could just return err below. secondly, given xfer_read and xfer_write return 0 on success then you can just skip the goto exit stage after the loop. > +exit: > + /* Mask interrupts */ > + writel(0x0000, i2c->base + IC_INTR_MASK); > + /* Clear all interrupts */ > + readl(i2c->base + IC_CLR_INTR); > + > + i2c->status = STATUS_IDLE; > + mutex_unlock(&i2c->lock); > + pm_runtime_put(i2c->dev); > + > + return i; > +} > + > +static int intel_mid_i2c_runtime_suspend(struct device *dev) > +{ > + struct pci_dev *pdev = to_pci_dev(dev); > + struct intel_mid_i2c_private *i2c = > + (struct intel_mid_i2c_private *)pci_get_drvdata(pdev); no need to cast here. > + struct i2c_adapter *adap = to_i2c_adapter(dev); > + int err; > + > + if (i2c->status != STATUS_IDLE) > + return -1; > + > + intel_mid_i2c_disable(adap); > + > + err = pci_save_state(pdev); > + if (err) { > + dev_err(dev, "pci_save_state failed\n"); > + return err; > + } > + > + err = pci_set_power_state(pdev, PCI_D3hot); > + if (err) { > + dev_err(dev, "pci_set_power_state failed\n"); > + return err; > + } > + i2c->status = STATUS_STANDBY; > + > + return 0; > +} > + > +static int intel_mid_i2c_runtime_resume(struct device *dev) > +{ > + struct pci_dev *pdev = to_pci_dev(dev); > + struct intel_mid_i2c_private *i2c = > + (struct intel_mid_i2c_private *)pci_get_drvdata(pdev); ditto. > + int err; > + > + if (i2c->status != STATUS_STANDBY) > + return 0; > + > + pci_set_power_state(pdev, PCI_D0); > + pci_restore_state(pdev); > + err = pci_enable_device(pdev); > + if (err) { > + dev_err(dev, "pci_enable_device failed\n"); > + return err; > + } > + > + i2c->status = STATUS_IDLE; > + > + intel_mid_i2c_hwinit(i2c); > + return err; > +} > + > +static void i2c_isr_read(struct intel_mid_i2c_private *i2c) > +{ > + struct i2c_msg *msg = i2c->msg; > + int rx_num; > + u32 len; > + u8 *buf; > + > + if (!(msg->flags & I2C_M_RD)) > + return; > + > + if (i2c->status != STATUS_READ_IN_PROGRESS) { > + len = msg->len; > + buf = msg->buf; > + } else { > + len = i2c->rx_buf_len; > + buf = i2c->rx_buf; > + } > + > + rx_num = readl(i2c->base + IC_RXFLR); > + > + for (; len > 0 && rx_num > 0; len--, rx_num--) > + *buf++ = readl(i2c->base + IC_DATA_CMD); > + > + if (len > 0) { > + i2c->status = STATUS_READ_IN_PROGRESS; > + i2c->rx_buf_len = len; > + i2c->rx_buf = buf; > + } else > + i2c->status = STATUS_READ_SUCCESS; > + > + return; > +} > + > +static irqreturn_t intel_mid_i2c_isr(int this_irq, void *dev) > +{ > + struct intel_mid_i2c_private *i2c = dev; > + u32 stat = readl(i2c->base + IC_INTR_STAT); > + > + if (!stat) > + return IRQ_NONE; > + > + dev_dbg(&i2c->adap.dev, "%s, stat = 0x%x\n", __func__, stat); > + stat &= 0x54; > + > + if (i2c->status != STATUS_WRITE_START && > + i2c->status != STATUS_READ_START && > + i2c->status != STATUS_READ_IN_PROGRESS) > + goto err; > + > + if (stat & TX_ABRT) > + i2c->abort = readl(i2c->base + IC_TX_ABRT_SOURCE); > + > + readl(i2c->base + IC_CLR_INTR); > + > + if (stat & TX_ABRT) { > + intel_mid_i2c_abort(i2c); > + goto exit; > + } > + > + if (stat & RX_FULL) { > + i2c_isr_read(i2c); > + goto exit; > + } > + > + if (stat & TX_EMPTY) { > + if (readl(i2c->base + IC_STATUS) & 0x4) > + i2c->status = STATUS_WRITE_SUCCESS; > + } > + > +exit: > + if (i2c->status == STATUS_READ_SUCCESS || > + i2c->status == STATUS_WRITE_SUCCESS || > + i2c->status == STATUS_XFER_ABORT) { > + /* Clear all interrupts */ > + readl(i2c->base + IC_CLR_INTR); > + /* Mask interrupts */ > + writel(0, i2c->base + IC_INTR_MASK); > + complete(&i2c->complete); > + } > +err: > + return IRQ_HANDLED; > +} > + rest of the driver looks ok. -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html