📄 i2c-pnx.c
字号:
/* * Provides I2C support for Philips PNX010x/PNX4008 boards. * * Authors: Dennis Kovalev <dkovalev@ru.mvista.com> * Vitaly Wool <vwool@ru.mvista.com> * * 2004-2006 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */#include <linux/module.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/i2c.h>#include <linux/timer.h>#include <linux/completion.h>#include <linux/platform_device.h>#include <linux/i2c-pnx.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/uaccess.h>#define I2C_PNX_TIMEOUT 10 /* msec */#define I2C_PNX_SPEED_KHZ 100#define I2C_PNX_REGION_SIZE 0x100#define PNX_DEFAULT_FREQ 13 /* MHz */static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data){ while (timeout > 0 && (ioread32(I2C_REG_STS(data)) & mstatus_active)) { mdelay(1); timeout--; } return (timeout <= 0);}static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data){ while (timeout > 0 && (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { mdelay(1); timeout--; } return (timeout <= 0);}static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap){ struct i2c_pnx_algo_data *data = adap->algo_data; struct timer_list *timer = &data->mif.timer; int expires = I2C_PNX_TIMEOUT / (1000 / HZ); del_timer_sync(timer); dev_dbg(&adap->dev, "Timer armed at %lu plus %u jiffies.\n", jiffies, expires); timer->expires = jiffies + expires; timer->data = (unsigned long)adap; add_timer(timer);}/** * i2c_pnx_start - start a device * @slave_addr: slave address * @adap: pointer to adapter structure * * Generate a START signal in the desired mode. */static int i2c_pnx_start(unsigned char slave_addr, struct i2c_adapter *adap){ struct i2c_pnx_algo_data *alg_data = adap->algo_data; dev_dbg(&adap->dev, "%s(): addr 0x%x mode %d\n", __FUNCTION__, slave_addr, alg_data->mif.mode); /* Check for 7 bit slave addresses only */ if (slave_addr & ~0x7f) { dev_err(&adap->dev, "%s: Invalid slave address %x. " "Only 7-bit addresses are supported\n", adap->name, slave_addr); return -EINVAL; } /* First, make sure bus is idle */ if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) { /* Somebody else is monopolizing the bus */ dev_err(&adap->dev, "%s: Bus busy. Slave addr = %02x, " "cntrl = %x, stat = %x\n", adap->name, slave_addr, ioread32(I2C_REG_CTL(alg_data)), ioread32(I2C_REG_STS(alg_data))); return -EBUSY; } else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) { /* Sorry, we lost the bus */ dev_err(&adap->dev, "%s: Arbitration failure. " "Slave addr = %02x\n", adap->name, slave_addr); return -EIO; } /* * OK, I2C is enabled and we have the bus. * Clear the current TDI and AFI status flags. */ iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data)); dev_dbg(&adap->dev, "%s(): sending %#x\n", __FUNCTION__, (slave_addr << 1) | start_bit | alg_data->mif.mode); /* Write the slave address, START bit and R/W bit */ iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode, I2C_REG_TX(alg_data)); dev_dbg(&adap->dev, "%s(): exit\n", __FUNCTION__); return 0;}/** * i2c_pnx_stop - stop a device * @adap: pointer to I2C adapter structure * * Generate a STOP signal to terminate the master transaction. */static void i2c_pnx_stop(struct i2c_adapter *adap){ struct i2c_pnx_algo_data *alg_data = adap->algo_data; /* Only 1 msec max timeout due to interrupt context */ long timeout = 1000; dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data))); /* Write a STOP bit to TX FIFO */ iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data)); /* Wait until the STOP is seen. */ while (timeout > 0 && (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) { /* may be called from interrupt context */ udelay(1); timeout--; } dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data)));}/** * i2c_pnx_master_xmit - transmit data to slave * @adap: pointer to I2C adapter structure * * Sends one byte of data to the slave */static int i2c_pnx_master_xmit(struct i2c_adapter *adap){ struct i2c_pnx_algo_data *alg_data = adap->algo_data; u32 val; dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data))); if (alg_data->mif.len > 0) { /* We still have something to talk about... */ val = *alg_data->mif.buf++; if (alg_data->mif.len == 1) { val |= stop_bit; if (!alg_data->last) val |= start_bit; } alg_data->mif.len--; iowrite32(val, I2C_REG_TX(alg_data)); dev_dbg(&adap->dev, "%s(): xmit %#x [%d]\n", __FUNCTION__, val, alg_data->mif.len + 1); if (alg_data->mif.len == 0) { if (alg_data->last) { /* Wait until the STOP is seen. */ if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) dev_err(&adap->dev, "The bus is still " "active after timeout\n"); } /* Disable master interrupts */ iowrite32(ioread32(I2C_REG_CTL(alg_data)) & ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), I2C_REG_CTL(alg_data)); del_timer_sync(&alg_data->mif.timer); dev_dbg(&adap->dev, "%s(): Waking up xfer routine.\n", __FUNCTION__); complete(&alg_data->mif.complete); } } else if (alg_data->mif.len == 0) { /* zero-sized transfer */ i2c_pnx_stop(adap); /* Disable master interrupts. */ iowrite32(ioread32(I2C_REG_CTL(alg_data)) & ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), I2C_REG_CTL(alg_data)); /* Stop timer. */ del_timer_sync(&alg_data->mif.timer); dev_dbg(&adap->dev, "%s(): Waking up xfer routine after " "zero-xfer.\n", __FUNCTION__); complete(&alg_data->mif.complete); } dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data))); return 0;}/** * i2c_pnx_master_rcv - receive data from slave * @adap: pointer to I2C adapter structure * * Reads one byte data from the slave */static int i2c_pnx_master_rcv(struct i2c_adapter *adap){ struct i2c_pnx_algo_data *alg_data = adap->algo_data; unsigned int val = 0; u32 ctl = 0; dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data))); /* Check, whether there is already data, * or we didn't 'ask' for it yet. */ if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { dev_dbg(&adap->dev, "%s(): Write dummy data to fill " "Rx-fifo...\n", __FUNCTION__); if (alg_data->mif.len == 1) { /* Last byte, do not acknowledge next rcv. */ val |= stop_bit; if (!alg_data->last) val |= start_bit; /* * Enable interrupt RFDAIE (data in Rx fifo), * and disable DRMIE (need data for Tx) */ ctl = ioread32(I2C_REG_CTL(alg_data)); ctl |= mcntrl_rffie | mcntrl_daie; ctl &= ~mcntrl_drmie; iowrite32(ctl, I2C_REG_CTL(alg_data)); } /* * Now we'll 'ask' for data: * For each byte we want to receive, we must * write a (dummy) byte to the Tx-FIFO. */ iowrite32(val, I2C_REG_TX(alg_data)); return 0; } /* Handle data. */ if (alg_data->mif.len > 0) { val = ioread32(I2C_REG_RX(alg_data)); *alg_data->mif.buf++ = (u8) (val & 0xff); dev_dbg(&adap->dev, "%s(): rcv 0x%x [%d]\n", __FUNCTION__, val, alg_data->mif.len); alg_data->mif.len--; if (alg_data->mif.len == 0) { if (alg_data->last) /* Wait until the STOP is seen. */ if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) dev_err(&adap->dev, "The bus is still " "active after timeout\n"); /* Disable master interrupts */ ctl = ioread32(I2C_REG_CTL(alg_data)); ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie | mcntrl_daie); iowrite32(ctl, I2C_REG_CTL(alg_data)); /* Kill timer. */ del_timer_sync(&alg_data->mif.timer); complete(&alg_data->mif.complete); } } dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data))); return 0;}static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id){ u32 stat, ctl; struct i2c_adapter *adap = dev_id; struct i2c_pnx_algo_data *alg_data = adap->algo_data; dev_dbg(&adap->dev, "%s(): mstat = %x mctrl = %x, mode = %d\n", __FUNCTION__, ioread32(I2C_REG_STS(alg_data)), ioread32(I2C_REG_CTL(alg_data)), alg_data->mif.mode); stat = ioread32(I2C_REG_STS(alg_data)); /* let's see what kind of event this is */ if (stat & mstatus_afi) { /* We lost arbitration in the midst of a transfer */ alg_data->mif.ret = -EIO; /* Disable master interrupts. */ ctl = ioread32(I2C_REG_CTL(alg_data)); ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie); iowrite32(ctl, I2C_REG_CTL(alg_data)); /* Stop timer, to prevent timeout. */ del_timer_sync(&alg_data->mif.timer); complete(&alg_data->mif.complete); } else if (stat & mstatus_nai) { /* Slave did not acknowledge, generate a STOP */ dev_dbg(&adap->dev, "%s(): " "Slave did not acknowledge, generating a STOP.\n", __FUNCTION__); i2c_pnx_stop(adap); /* Disable master interrupts. */ ctl = ioread32(I2C_REG_CTL(alg_data)); ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie); iowrite32(ctl, I2C_REG_CTL(alg_data)); /* Our return value. */ alg_data->mif.ret = -EIO; /* Stop timer, to prevent timeout. */ del_timer_sync(&alg_data->mif.timer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -