| /* |
| * drivers/i2c/busses/i2c-comcerto.c |
| * |
| * Copyright (C) 2008 Mindspeed Technologies, Inc. |
| * |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/i2c.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/ktime.h> |
| #include <asm/io.h> |
| #include <asm/sizes.h> |
| #include <mach/i2c.h> |
| #include <mach/irqs.h> |
| #include <linux/err.h> |
| #include <linux/clk.h> |
| #include <mach/reset.h> |
| |
| MODULE_AUTHOR("Mindspeed Technologies, Inc."); |
| MODULE_DESCRIPTION("Comcerto I2C bus driver"); |
| MODULE_LICENSE("GPL"); |
| |
| #define SPEED_HIGH_KHZ 3400 |
| #define SPEED_FULL_KHZ 400 |
| #define SPEED_NORMAL_KHZ 100 |
| |
| static int force_poll = 0; |
| static struct clk *clk_i2c; |
| module_param(force_poll, bool, S_IRUGO); |
| MODULE_PARM_DESC(force_poll, "Force polling mode: 0=interrupt mode, polling mode otherwise"); |
| |
| static int speed = 0; |
| module_param(speed, int, S_IRUGO); |
| MODULE_PARM_DESC(speed, "I2C speed: 0=standard, 1=fast, 2=high speed"); |
| |
| struct comcerto_i2c |
| { |
| struct i2c_adapter *adapter; |
| struct device *dev; |
| unsigned long membase; |
| struct resource *io; |
| int irq; |
| u32 speed_khz; |
| |
| wait_queue_head_t wait; |
| struct i2c_msg *msg; |
| int msg_state; |
| int msg_status; /* < 0: error, == 0: success, > 0: message in progress */ |
| int msg_len; |
| int msg_retries; |
| ktime_t msg_start_time; |
| }; |
| |
| #define REG_ADDR(i2c, offset) ((i2c)->membase + (offset)) |
| #define RD_REG(i2c, offset) __raw_readb(REG_ADDR(i2c, offset)) |
| #define WR_REG(i2c, offset, byte) __raw_writeb(byte, REG_ADDR(i2c, offset)) |
| #define RD_DATA(i2c) RD_REG(i2c, COMCERTO_I2C_DATA) |
| #define WR_DATA(i2c, byte) WR_REG(i2c, COMCERTO_I2C_DATA, byte) |
| #define RD_CNTR(i2c) RD_REG(i2c, COMCERTO_I2C_CNTR) |
| #define WR_CNTR(i2c, byte) WR_REG(i2c, COMCERTO_I2C_CNTR, byte) |
| #define RD_STAT(i2c) RD_REG(i2c, COMCERTO_I2C_STAT) |
| #define WR_CCRFS(i2c, byte) WR_REG(i2c, COMCERTO_I2C_CCRFS, byte) |
| #define WR_CCRH(i2c, byte) WR_REG(i2c, COMCERTO_I2C_CCRH, byte) |
| #define WR_RESET(i2c, byte) WR_REG(i2c, COMCERTO_I2C_RESET, byte) |
| |
| enum |
| { |
| TR_IDLE = 0, |
| TR_START_ACK, |
| TR_ADDR_ACK, |
| TR_DATA_ACK, |
| RX_DATA_NACK, |
| }; |
| |
| static u8 comcerto_i2c_calculate_dividers(struct comcerto_i2c *i2c) |
| { |
| int m, n, hz, speed_hz; |
| int saved_n, saved_m, saved_hz; |
| u8 dividers; |
| unsigned int i2c_clk; |
| |
| /* Get the i2c clock rate */ |
| i2c_clk = clk_get_rate(clk_i2c); |
| |
| speed_hz = i2c->speed_khz * 1000; |
| saved_hz = saved_n = saved_m = 0; |
| |
| for (m = 0; m < 16; m++) { |
| for (n = 0; n < 8; n++) { |
| hz = i2c_clk / ((1 << n) * (m + 1) * 10); |
| if (!saved_hz || abs(speed_hz - hz) < abs(speed_hz - saved_hz)) { |
| saved_n = n; |
| saved_m = m; |
| saved_hz = hz; |
| } |
| } |
| } |
| |
| dividers = (saved_m << 3) | saved_n; |
| dev_dbg(i2c->dev, "%s: speed=%dkHz, M=%d, N=%d, dividers=0x%02x\n", __FUNCTION__, |
| saved_hz/1000, saved_m, saved_n, dividers); |
| printk("%s: speed=%dkHz, M=%d, N=%d, dividers=0x%02x\n", __FUNCTION__, saved_hz/1000, saved_m, saved_n, dividers); |
| |
| return dividers; |
| } |
| |
| /* |
| * Returns the timeout (in jiffies) for the given message. |
| */ |
| static int comcerto_i2c_calculate_timeout(struct comcerto_i2c *i2c, struct i2c_msg *msg) |
| { |
| int timeout; |
| |
| /* if no timeout was specified, calculate it */ |
| if (i2c->adapter->timeout <= 0) { |
| if (i2c->irq >= 0) { |
| /* for the interrupt mode calculate timeout for 'full' message */ |
| timeout = ((int)msg->len) * 10; /* convert approx. to bits */ |
| timeout /= i2c->speed_khz; /* convert to bits per ms (note of kHz scale) */ |
| timeout += timeout >> 1; /* add 50% */ |
| timeout = timeout*HZ / 1000; /* convert to jiffies */ |
| if (timeout < HZ / 5) /* at least 200ms */ |
| timeout = HZ / 5; |
| } |
| else |
| timeout = HZ; /* 1 second for the polling mode */ |
| } |
| else |
| timeout = i2c->adapter->timeout; |
| |
| return timeout; |
| } |
| |
| /* |
| * Initialize I2C core. Zero CNTR and DATA, try RESET. Short busy wait and check core status. |
| * After that set dividers for choosen speed. |
| */ |
| static void comcerto_i2c_reset(struct comcerto_i2c *i2c) |
| { |
| u8 status, dividers; |
| |
| dev_dbg(i2c->dev, "%s\n", __FUNCTION__); |
| |
| WR_CNTR(i2c, 0); |
| WR_DATA(i2c, 0); |
| WR_RESET(i2c, 1); |
| |
| udelay(10); |
| |
| status = RD_STAT(i2c); |
| if (status != STAT_NO_RELEVANT_INFO) |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected status after reset: 0x%02x\n", __FUNCTION__, status); |
| |
| /* dividers should be placed in CCRH for high-sped mode and in CCRFS for standard/full modes */ |
| dividers = comcerto_i2c_calculate_dividers(i2c); |
| if (i2c->speed_khz == SPEED_HIGH_KHZ) |
| WR_CCRH(i2c, dividers); |
| else |
| WR_CCRFS(i2c, dividers); |
| } |
| |
| static inline void comcerto_i2c_message_complete(struct comcerto_i2c *i2c, int status) |
| { |
| s64 d; |
| WR_CNTR(i2c, CNTR_STP); |
| |
| i2c->msg_status = status; |
| |
| if (i2c->msg_len < 10) { |
| d = ktime_us_delta(ktime_get(), i2c->msg_start_time); |
| if (d > 22000) { |
| i2c->msg_status = -ETIME; |
| dev_printk(KERN_DEBUG, i2c->dev, |
| "I2C transaction took too long: %lld us. Returning error.\n", |
| (long long) d); |
| } |
| } |
| } |
| |
| static inline int comcerto_i2c_message_in_progress(struct comcerto_i2c *i2c) |
| { |
| return i2c->msg_status > 0; |
| } |
| |
| /* |
| * Wait event. This function sleeps in polling mode, in interrupt |
| * mode it enables IRQ from I2C core and exits immediately. |
| */ |
| static int comcerto_i2c_wait(struct comcerto_i2c *i2c, u8 cntr) |
| { |
| cntr &= ~(CNTR_IFLG | CNTR_IEN); /* clear both IFLG and IEN */ |
| |
| if (i2c->irq < 0) { |
| ulong jiffies_mark = jiffies + comcerto_i2c_calculate_timeout(i2c, i2c->msg); |
| |
| WR_CNTR(i2c, cntr); |
| while ((RD_CNTR(i2c) & CNTR_IFLG) == 0) { |
| if (need_resched()) |
| schedule(); |
| |
| if (time_after(jiffies, jiffies_mark)) { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: polling transfer timeout\n", __FUNCTION__); |
| comcerto_i2c_message_complete(i2c, -ETIME); |
| comcerto_i2c_reset(i2c); |
| break; |
| } |
| } |
| } |
| else { |
| /* enable interrupt */ |
| WR_CNTR(i2c, cntr | CNTR_IEN); |
| } |
| |
| return 0; |
| } |
| |
| static void comcerto_i2c_state_idle(struct comcerto_i2c *i2c, u8 *cntr) |
| { |
| if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) { |
| i2c->msg_state = TR_ADDR_ACK; |
| } |
| else { |
| *cntr = CNTR_STP|CNTR_STA; /* SPT|STA to auto recover from bus error state transparently at the start of the transfer */ |
| i2c->msg_state = TR_START_ACK; |
| } |
| } |
| |
| static void comcerto_i2c_state_start_ack(struct comcerto_i2c *i2c, u8 *cntr) |
| { |
| u8 status, addr; |
| |
| *cntr = 0; /* zero IFLG, IEN (for the interrupt mode it will be enabled in wait function) */ |
| |
| status = RD_STAT(i2c); |
| |
| if (status == STAT_START || status == STAT_START_REPEATED) { |
| i2c->msg_state = TR_ADDR_ACK; |
| |
| addr = i2c->msg->addr << 1; |
| if (i2c->msg->flags & I2C_M_RD) |
| addr |= 1; |
| if (i2c->msg->flags & I2C_M_REV_DIR_ADDR) |
| addr ^= 1; /* invert RW bit if it's requested */ |
| |
| WR_DATA(i2c, addr); /* write address and read/write bit */ |
| } else { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on start phase, %s\n", |
| __FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting"); |
| |
| if (--i2c->msg_retries < 0) |
| comcerto_i2c_message_complete(i2c, -1); |
| else |
| comcerto_i2c_state_idle(i2c, cntr); |
| } |
| } |
| |
| static void comcerto_i2c_rx(struct comcerto_i2c *i2c) |
| { |
| u8 status, cntr = 0; |
| |
| restart: |
| switch (i2c->msg_state) { |
| case TR_IDLE: |
| comcerto_i2c_state_idle(i2c, &cntr); |
| if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) |
| goto restart; /* needed to avoid event loss in interrupt mode */ |
| break; |
| |
| case TR_START_ACK: |
| comcerto_i2c_state_start_ack(i2c, &cntr); |
| break; |
| |
| case TR_ADDR_ACK: |
| if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) { |
| /* we can enter this state if skip start/addr flag is set, so fake good ack */ |
| status = STAT_ADDR_RD_ACK; |
| } |
| else { |
| status = RD_STAT(i2c); |
| /* check whether we should ignore NACK */ |
| if (status == STAT_DATA_RD_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK)) |
| status = STAT_DATA_RD_ACK; |
| } |
| |
| if (likely(status == STAT_ADDR_RD_ACK)) { |
| /* start reception phase - wait until data is ready and loop in RX_DATA_ACK state |
| * until we read all the data, sending ACK after each byte (but the last) |
| */ |
| i2c->msg_len = 0; |
| if (i2c->msg->len > 1) { |
| i2c->msg_state = TR_DATA_ACK; |
| cntr = CNTR_AAK; |
| } |
| else if (i2c->msg->len == 1) { |
| i2c->msg_state = RX_DATA_NACK; |
| } |
| else { /* nothing to receive, send STOP and signal success */ |
| comcerto_i2c_message_complete(i2c, 0); |
| } |
| } |
| else { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on address phase, %s\n", |
| __FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting"); |
| |
| if (--i2c->msg_retries < 0) |
| comcerto_i2c_message_complete(i2c, -1); |
| else |
| comcerto_i2c_state_idle(i2c, &cntr); |
| } |
| break; |
| |
| case TR_DATA_ACK: |
| status = RD_STAT(i2c); |
| |
| if (likely(status == STAT_DATA_RD_ACK)) { |
| i2c->msg->buf[i2c->msg_len++] = RD_DATA(i2c); |
| if (likely(i2c->msg->len - i2c->msg_len > 1)) { |
| cntr = CNTR_AAK; |
| } |
| else { |
| i2c->msg_state = RX_DATA_NACK; |
| /* NACK should be transmitted on the last byte */ |
| } |
| } |
| else { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on read phase\n", __FUNCTION__, status); |
| comcerto_i2c_message_complete(i2c, -1); |
| } |
| break; |
| |
| case RX_DATA_NACK: |
| status = RD_STAT(i2c); |
| if (likely(status == STAT_DATA_RD_NACK)) { |
| i2c->msg->buf[i2c->msg_len++] = RD_DATA(i2c); |
| comcerto_i2c_message_complete(i2c, 0); |
| } |
| else { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on finishing read phase\n", __FUNCTION__, status); |
| comcerto_i2c_message_complete(i2c, -1); |
| } |
| } |
| |
| /* no wait if we completed message */ |
| if (comcerto_i2c_message_in_progress(i2c)) |
| comcerto_i2c_wait(i2c, cntr); |
| } |
| |
| static void comcerto_i2c_tx(struct comcerto_i2c *i2c) |
| { |
| u8 status, cntr = 0; |
| |
| restart: |
| switch (i2c->msg_state) { |
| case TR_IDLE: |
| comcerto_i2c_state_idle(i2c, &cntr); |
| if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) |
| goto restart; /* needed to avoid event loss in interrupt mode */ |
| break; |
| |
| case TR_START_ACK: |
| comcerto_i2c_state_start_ack(i2c, &cntr); |
| break; |
| |
| case TR_ADDR_ACK: |
| if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) { |
| /* we can enter this state if skip start/addr flag is set, so fake good ack */ |
| status = STAT_ADDR_WR_ACK; |
| } |
| else { |
| status = RD_STAT(i2c); |
| if (status == STAT_DATA_WR_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK)) |
| status = STAT_DATA_WR_ACK; |
| } |
| |
| if (likely(status == STAT_ADDR_WR_ACK)) { |
| /* start reception phase - wait until data is ready and loop in TX_DATA_ACK state |
| * until we read all the data, sending ACK after each byte (but the last) |
| */ |
| i2c->msg_state = TR_DATA_ACK; |
| i2c->msg_len = 0; |
| if (likely(i2c->msg->len != 0)) { |
| WR_DATA(i2c, i2c->msg->buf[i2c->msg_len++]); |
| //printk("comcerto_i2c_tx: i2c->msg->buf[i2c->msg_len - 1]=%d\n", i2c->msg->buf[i2c->msg_len - 1]); |
| } |
| else { |
| /* nothing to transmit, send STOP and signal success */ |
| comcerto_i2c_message_complete(i2c, 0); |
| } |
| } |
| else { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on address phase, %s\n", |
| __FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting"); |
| |
| if (--i2c->msg_retries < 0) |
| comcerto_i2c_message_complete(i2c, -1); |
| else |
| comcerto_i2c_state_idle(i2c, &cntr); |
| } |
| break; |
| |
| case TR_DATA_ACK: |
| status = RD_STAT(i2c); |
| if (status == STAT_DATA_WR_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK)) |
| status = STAT_DATA_WR_ACK; |
| |
| if (likely(status == STAT_DATA_WR_ACK)) { |
| if (i2c->msg->len > i2c->msg_len) |
| WR_DATA(i2c, i2c->msg->buf[i2c->msg_len++]); |
| else |
| comcerto_i2c_message_complete(i2c, 0); |
| } |
| else { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: unexpected state (%#x) on read data phase\n", __FUNCTION__, status); |
| comcerto_i2c_message_complete(i2c, -1); |
| } |
| break; |
| } |
| |
| if (comcerto_i2c_message_in_progress(i2c)) |
| comcerto_i2c_wait(i2c, cntr); |
| } |
| |
| static irqreturn_t comcerto_i2c_interrupt(int irq, void *dev_id) |
| { |
| struct comcerto_i2c *i2c = dev_id; |
| |
| if (!(RD_CNTR(i2c) & CNTR_IFLG)) |
| goto none; |
| |
| /* IRQ enable/disable logic is hidden in state handlers, all we need is to wake |
| * process when message completed. |
| */ |
| if (i2c->msg->flags & I2C_M_RD) |
| comcerto_i2c_rx(i2c); |
| else |
| comcerto_i2c_tx(i2c); |
| |
| if (!comcerto_i2c_message_in_progress(i2c)) { |
| WR_CNTR(i2c, RD_CNTR(i2c) & ~CNTR_IEN); /* disable interrupt unconditionally */ |
| wake_up(&i2c->wait); |
| } |
| |
| return IRQ_HANDLED; |
| none: |
| return IRQ_NONE; |
| } |
| |
| static void comcerto_i2c_message_process(struct comcerto_i2c *i2c, struct i2c_msg *msg) |
| { |
| i2c->msg = msg; |
| i2c->msg_state = TR_IDLE; |
| i2c->msg_status = 1; |
| i2c->msg_retries = i2c->adapter->retries; |
| i2c->msg_start_time = ktime_get(); |
| |
| polling_mode: |
| if (msg->flags & I2C_M_RD) |
| comcerto_i2c_rx(i2c); |
| else |
| comcerto_i2c_tx(i2c); |
| |
| if (i2c->irq < 0) { |
| /*if (i2c->msg != NULL)*/ |
| goto polling_mode; |
| } |
| else { |
| int timeout, res; |
| ulong flags; |
| |
| timeout = comcerto_i2c_calculate_timeout(i2c, msg); |
| |
| res = wait_event_timeout(i2c->wait, i2c->msg_status <= 0, timeout); |
| |
| local_irq_save(flags); |
| |
| /* check if we timed out and set respective error codes */ |
| if (res == 0) { |
| if (comcerto_i2c_message_in_progress(i2c)) { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: interrupt transfer timeout\n", __FUNCTION__); |
| comcerto_i2c_message_complete(i2c, -ETIME); |
| comcerto_i2c_reset(i2c); |
| } |
| } |
| |
| local_irq_restore(flags); |
| } |
| } |
| |
| /* |
| * Generic master transfer entrypoint. |
| * Returns the number of processed messages or error value |
| */ |
| static int comcerto_i2c_master_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) |
| { |
| struct comcerto_i2c *i2c = i2c_get_adapdata(adapter); |
| int i; |
| |
| dev_dbg(i2c->dev, "%s: %d messages to process\n", __FUNCTION__, num); |
| |
| for (i = 0; i < num; i++) { |
| dev_dbg(i2c->dev, "%s: message #%d: addr=%#x, flags=%#x, len=%u\n", __FUNCTION__, |
| i, msgs[i].addr, msgs[i].flags, msgs[i].len); |
| |
| comcerto_i2c_message_process(i2c, &msgs[i]); |
| |
| if (i2c->msg_status < 0) { |
| dev_printk(KERN_DEBUG, i2c->dev, "%s: transfer failed on message #%d (addr=%#x, flags=%#x, len=%u)\n", |
| __FUNCTION__, i, msgs[i].addr, msgs[i].flags, msgs[i].len); |
| break; |
| } |
| } |
| |
| if (i2c->msg_status == -1) |
| i2c->msg_status = -EIO; |
| |
| if (i2c->msg_status == 0) |
| i2c->msg_status = num; |
| |
| return i2c->msg_status; |
| } |
| |
| static u32 comcerto_i2c_functionality(struct i2c_adapter *adap) |
| { |
| return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL); |
| } |
| |
| static struct i2c_algorithm comcerto_i2c_algo = { |
| .master_xfer = comcerto_i2c_master_xfer, |
| .functionality = comcerto_i2c_functionality, |
| }; |
| |
| static struct i2c_adapter comcerto_i2c_adapter = { |
| .name = "comcerto_i2c", |
| .owner = THIS_MODULE, |
| .algo = &comcerto_i2c_algo, |
| .timeout = 0, /* <= zero means that we calculate timeout in run-time, can be changed with ioctl call */ |
| .retries = 0, /* no retries by default - let the user decide what's the best, can be changed with ioctl call */ |
| }; |
| |
| static int comcerto_i2c_probe(struct platform_device *pdev) |
| { |
| struct comcerto_i2c *i2c; |
| struct resource *irq; |
| int res = -1; |
| |
| dev_dbg(&pdev->dev, "%s\n", __FUNCTION__); |
| |
| /* Put the I2C device Out-Of-Reset*/ |
| c2000_block_reset(COMPONENT_AXI_I2C,0); |
| |
| /* Clock divider configuration, get the i2c clock*/ |
| clk_i2c = clk_get(NULL, "spi_i2c"); |
| if (IS_ERR(clk_i2c)){ |
| pr_err("%s: Unable to obtain i2c clock: %ld\n", __func__, PTR_ERR(clk_i2c)); |
| return PTR_ERR(clk_i2c); |
| } |
| |
| /*Enable the i2c clock */ |
| res = clk_enable(clk_i2c); |
| if (res){ |
| pr_err("%s: i2c clock failed to enable:\n", __func__); |
| goto err0; |
| } |
| |
| i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); |
| if (i2c == NULL) { |
| dev_err(&pdev->dev, "%s: failed allocate memory\n", __FUNCTION__); |
| res = -ENOMEM; |
| goto err0; |
| } |
| |
| i2c->adapter = &comcerto_i2c_adapter; |
| i2c->adapter->dev.parent = &pdev->dev; |
| i2c->dev = &pdev->dev; |
| |
| init_waitqueue_head(&i2c->wait); |
| |
| platform_set_drvdata(pdev, i2c); |
| i2c_set_adapdata(&comcerto_i2c_adapter, i2c); |
| |
| i2c->io = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (i2c->io == NULL) { |
| dev_err(&pdev->dev, "%s: no IO region specified\n", __FUNCTION__); |
| res = -ENOENT; |
| goto err1; |
| } |
| |
| if (!request_mem_region(i2c->io->start, i2c->io->end - i2c->io->start + 1, "I2C")) { |
| dev_err(i2c->dev, "%s: failed to request memory region\n", __FUNCTION__); |
| goto err1; |
| } |
| |
| i2c->membase = APB_VADDR(i2c->io->start); |
| |
| irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| if (irq == NULL && !force_poll) { |
| dev_warn(i2c->dev, "%s: no IRQ specified in resources, polling mode forced\n", __FUNCTION__); |
| force_poll = 1; |
| i2c->irq = -1; |
| } |
| |
| if (speed == 0) { |
| i2c->speed_khz = SPEED_NORMAL_KHZ; |
| } |
| else if (speed == 1) { |
| i2c->speed_khz = SPEED_FULL_KHZ; |
| } |
| else if (speed == 2) { |
| i2c->speed_khz = SPEED_HIGH_KHZ; |
| } |
| else { |
| dev_err(i2c->dev, "%s: invalid 'speed' module option provided (%d, must be 0,1,2 for normal/full/high modes)\n", |
| __FUNCTION__, speed); |
| goto err2; |
| } |
| |
| |
| comcerto_i2c_reset(i2c); |
| |
| if (!force_poll) { |
| i2c->irq = irq->start; |
| |
| res = request_irq(i2c->irq, comcerto_i2c_interrupt, IRQF_SHARED, "I2C", i2c); |
| if (res < 0) { |
| dev_warn(i2c->dev, "%s: failed to request IRQ%d, polling mode forced\n", __FUNCTION__, i2c->irq); |
| force_poll = 1; |
| i2c->irq = -1; |
| } |
| } |
| else |
| i2c->irq = -1; |
| |
| if (i2c_add_numbered_adapter(&comcerto_i2c_adapter) != 0) { |
| dev_err(i2c->dev, "%s: failed to add I2C adapter\n", __FUNCTION__); |
| goto err3; |
| } |
| |
| dev_dbg(&pdev->dev, "%s: I2C adapter registered\n", __FUNCTION__); |
| |
| return 0; |
| |
| err3: |
| if (i2c->irq >= 0) |
| free_irq(i2c->irq, i2c); |
| |
| err2: |
| release_mem_region(i2c->io->start, i2c->io->end - i2c->io->end + 1); |
| |
| err1: |
| kfree(i2c); |
| |
| err0: |
| return res; |
| } |
| |
| static int comcerto_i2c_remove(struct platform_device *pdev) |
| { |
| struct comcerto_i2c *i2c = platform_get_drvdata(pdev); |
| |
| dev_dbg(i2c->dev, "%s\n", __FUNCTION__); |
| |
| platform_set_drvdata(pdev, NULL); |
| |
| i2c_del_adapter(i2c->adapter); |
| |
| if (i2c->irq >= 0) |
| free_irq(i2c->irq, i2c); |
| |
| release_mem_region(i2c->io->start, i2c->io->end - i2c->io->start + 1); |
| |
| kfree(i2c); |
| |
| /* Disable the Clock */ |
| clk_disable(clk_i2c); |
| clk_put(clk_i2c); |
| |
| /* Put the I2C device in reset state*/ |
| c2000_block_reset(COMPONENT_AXI_I2C,1); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| static int comcerto_i2c_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| /* No need to suspend client drivers here. Because clients are |
| * children, client drivers get suspended before adapter driver |
| */ |
| |
| /* So do the Clock disable here , This clock is depends upon Legacy SPI*/ |
| clk_disable(clk_i2c); |
| return 0; |
| } |
| |
| static int comcerto_i2c_resume(struct platform_device *pdev) |
| { |
| int ret; |
| /* No need to suspend client drivers here. Because clients are |
| * children, client drivers get suspended before adapter driver |
| */ |
| |
| /* So do the Clock Enable here , This clock is depends upon Legacy SPI*/ |
| ret = clk_enable(clk_i2c); |
| if (ret) |
| { |
| pr_err("%s: I2C clock enable failed \n",__func__); |
| return ret; |
| } |
| return 0; |
| } |
| #endif |
| |
| static struct platform_driver comcerto_i2c_driver = { |
| .driver = { |
| .name = "comcerto_i2c", |
| .owner = THIS_MODULE, |
| }, |
| .probe = comcerto_i2c_probe, |
| .remove = comcerto_i2c_remove, |
| #ifdef CONFIG_PM |
| .suspend = comcerto_i2c_suspend, |
| .resume = comcerto_i2c_resume, |
| #endif |
| }; |
| |
| static int __init comcerto_i2c_init(void) |
| { |
| if (platform_driver_register(&comcerto_i2c_driver)) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void __exit comcerto_i2c_exit(void) |
| { |
| platform_driver_unregister(&comcerto_i2c_driver); |
| } |
| |
| module_init(comcerto_i2c_init); |
| module_exit(comcerto_i2c_exit); |