| /* linux/drivers/i2c/busses/i2c-zx29.c |
| * |
| * Copyright (C) 2015 Sanechips-TSP |
| * |
| * ----------NOTICE------------- |
| * ZX29 serials I2C Controller driver used by zx297520, this driver |
| * do not support slave mode. bus clock rate should between 400KHz--100KHz. |
| * |
| * 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 |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| |
| #include <linux/i2c.h> |
| #include <linux/init.h> |
| #include <linux/time.h> |
| #include <linux/hrtimer.h> |
| #include <linux/ktime.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/errno.h> |
| #include <linux/err.h> |
| #include <linux/platform_device.h> |
| #include <linux/clk.h> |
| #include <linux/slab.h> |
| #include <linux/io.h> |
| //#include <linux/of_i2c.h> |
| #include <linux/of_gpio.h> |
| #include <linux/clk/zx29-clk.h> |
| |
| |
| #include <asm/irq.h> |
| #include <mach/i2c_private.h> |
| #include <mach/spinlock.h> |
| #include <linux/soc/zte/pm/drv_idle.h> |
| |
| /*#define DEBUG_I2C_ADAPTER*/ |
| |
| #ifdef DEBUG_I2C_ADAPTER |
| #define drv_printk(fmt, arg...) printk(KERN_DEBUG fmt, ##arg) |
| #pragma GCC optimize("O0") |
| #else |
| #define drv_printk(fmt, arg...) |
| #endif |
| |
| #define I2C_PSM_CONTROL (1) |
| #define I2C_LONG_TRANSFER (1) |
| |
| #define I2C_FIFO_DEPTH 32 |
| #define I2C_TIMEOUT (msecs_to_jiffies(1000)) |
| #define MAX_BUS_CLK 400000 |
| #define MIN_BUS_CLK 100000 |
| |
| #define I2C_WCLK_FREQ (26*1000*1000) |
| |
| #if I2C_PSM_CONTROL |
| static volatile unsigned int i2c_active_count = 0; |
| #endif |
| |
| #if I2C_LONG_TRANSFER |
| /* functions for hrtimer */ |
| |
| static enum hrtimer_restart zx29_i2c_timer_callback(struct hrtimer *timer) |
| { |
| struct zx29_i2c *i2c; |
| enum hrtimer_restart ret = HRTIMER_NORESTART; |
| |
| i2c = container_of(timer, struct zx29_i2c, hr_timer); |
| |
| if ((i2c->state == STATE_READ) && (i2c->buf_remaining > I2C_FIFO_DEPTH)) { |
| unsigned int len_rx = 0; |
| unsigned int len_diff = 0; |
| u8 *buf = i2c->msg->buf; |
| u8 val = 0; |
| |
| len_rx = i2c_get_rx_fifo_length(i2c); |
| len_diff = i2c->buf_remaining - I2C_FIFO_DEPTH; |
| if (len_rx < len_diff) { |
| ret = HRTIMER_RESTART; |
| hrtimer_forward_now(timer, i2c->ktime); |
| } else { |
| len_rx = len_diff; |
| } |
| |
| i2c->buf_remaining -= len_rx; |
| |
| /*read data from fifo*/ |
| while (len_rx--) { |
| val = (u8)i2c_read_data(i2c); |
| *(buf + i2c->msg_ptr) = val; |
| i2c->msg_ptr++; |
| } |
| } else if ((i2c->state == STATE_WRITE) && (i2c->buf_remaining > 0)) { |
| unsigned int len_tx = 0; |
| unsigned int len_diff = 0; |
| u8 *buf = i2c->msg->buf; |
| u8 val = 0; |
| |
| len_tx = i2c_get_tx_fifo_length(i2c); |
| len_tx = I2C_FIFO_DEPTH - len_tx; |
| len_diff = i2c->buf_remaining; |
| if (len_tx < len_diff) { |
| ret = HRTIMER_RESTART; |
| hrtimer_forward_now(timer, i2c->ktime); |
| } else { |
| len_tx = len_diff; |
| } |
| |
| i2c->buf_remaining -= len_tx; |
| |
| /* write data to fifo */ |
| while (len_tx--) { |
| val = *(buf + i2c->msg_ptr); |
| i2c_write_data(i2c, val); |
| i2c->msg_ptr++; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static void zx29_i2c_start_timer( struct zx29_i2c *i2c ) |
| { |
| unsigned long delay_in_us = (9*I2C_FIFO_DEPTH/2)*1000*1000 / i2c->clkrate; |
| |
| i2c->ktime = ktime_set(0, delay_in_us * 1000); |
| |
| hrtimer_init( &i2c->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL ); |
| |
| i2c->hr_timer.function = zx29_i2c_timer_callback; |
| |
| hrtimer_start( &i2c->hr_timer, i2c->ktime, HRTIMER_MODE_REL ); |
| } |
| |
| static void zx29_i2c_stop_timer( struct zx29_i2c *i2c ) |
| { |
| int ret; |
| |
| ret = hrtimer_cancel(&i2c->hr_timer); |
| if(ret){ |
| pr_info("%s return %d\n", __FUNCTION__, ret); |
| } |
| |
| return; |
| } |
| #endif |
| |
| /* functions for hrtimer end */ |
| |
| #if I2C_PSM_CONTROL |
| static void zx29_i2c_set_active(struct wake_lock *lock) |
| { |
| unsigned long flags; |
| |
| local_irq_save(flags); |
| |
| if(i2c_active_count == 0) |
| { |
| zx_cpuidle_set_busy(IDLE_FLAG_I2C); |
| } |
| i2c_active_count++; |
| |
| local_irq_restore(flags); |
| |
| wake_lock(lock); |
| } |
| |
| static void zx29_i2c_set_idle(struct wake_lock *lock) |
| { |
| unsigned long flags; |
| |
| local_irq_save(flags); |
| |
| i2c_active_count--; |
| if(i2c_active_count == 0) |
| { |
| zx_cpuidle_set_free(IDLE_FLAG_I2C); |
| } |
| |
| local_irq_restore(flags); |
| |
| wake_unlock(lock); |
| } |
| #endif |
| |
| #ifdef CONFIG_SYSFS |
| static ssize_t zx29_i2c_sysfs_show(struct device *dev, |
| struct device_attribute *attr, char *buf); |
| static ssize_t zx29_i2c_sysfs_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count); |
| |
| |
| DEVICE_ATTR(clk_rate, S_IWUSR | S_IRUGO, zx29_i2c_sysfs_show, zx29_i2c_sysfs_store); |
| |
| static struct attribute * zx29_i2c_sysfs_attrs[] = { |
| &dev_attr_clk_rate.attr, |
| NULL |
| }; |
| |
| static const struct attribute_group zx29_i2c_sysfs_attr_group = { |
| .attrs = zx29_i2c_sysfs_attrs, |
| }; |
| |
| static int zx29_i2c_sysfs_create_group(struct zx29_i2c *i2c) |
| { |
| return sysfs_create_group(&i2c->dev->kobj, &zx29_i2c_sysfs_attr_group); |
| } |
| |
| static void zx29_i2c_sysfs_remove_group(struct zx29_i2c *i2c) |
| { |
| sysfs_remove_group(&i2c->dev->kobj, &zx29_i2c_sysfs_attr_group); |
| } |
| |
| static ssize_t zx29_i2c_sysfs_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct zx29_i2c *i2c = dev_get_drvdata(dev); |
| |
| if (!i2c) |
| return -EINVAL; |
| |
| return scnprintf(buf, PAGE_SIZE, "%ld\n", i2c->clkrate); |
| } |
| |
| static ssize_t zx29_i2c_sysfs_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct zx29_i2c *i2c = dev_get_drvdata(dev); |
| unsigned long clock_rate = 0; |
| |
| if(strict_strtoul(buf, 0, &clock_rate)) |
| return -EINVAL; |
| if((clock_rate < MIN_BUS_CLK) || (clock_rate > MAX_BUS_CLK) || (NULL == i2c)) |
| return -EINVAL; |
| |
| i2c_lock_adapter(&i2c->adap); |
| i2c->clkrate = clock_rate; |
| i2c_unlock_adapter(&i2c->adap); |
| |
| pr_info("new i2c%d scl rate: %ld\n", i2c->id, i2c->clkrate); |
| |
| return count; |
| } |
| #else |
| static int zx29_i2c_sysfs_create_group(struct zx29_i2c *i2c) |
| { |
| return 0; |
| } |
| |
| static void zx29_i2c_sysfs_remove_group(struct zx29_i2c *i2c) |
| { |
| return; |
| } |
| #endif |
| |
| static emsf_lock_id i2c_get_sflock(unsigned int i2c_id) |
| { |
| emsf_lock_id softLock = I2C2_SFLOCK; |
| |
| switch(i2c_id) |
| { |
| case 0: //pmic i2c |
| softLock = I2C2_SFLOCK; |
| break; |
| case 1: |
| softLock = I2C1_SFLOCK; |
| break; |
| default: |
| BUG(); |
| } |
| |
| return softLock; |
| } |
| |
| /* |
| * zx29_i2c_init |
| * |
| * initialise the controller, set frequency |
| */ |
| static int zx29_i2c_init(struct zx29_i2c *i2c) |
| { |
| unsigned int bus_clk; |
| unsigned int pdiv; |
| emsf_lock_id softLock = i2c_get_sflock(i2c->id); |
| unsigned long actual_clk = 0; |
| |
| soft_spin_lock(softLock); |
| |
| /*reset*/ |
| zx29_i2c_reset_fifo(i2c); |
| udelay(1); |
| |
| /*calculate bus clock division, fix input clock is 26M*/ |
| bus_clk=i2c->clkrate/1000; /*in KHz*/ |
| pdiv=((I2C_WCLK_FREQ/1000)/4)/bus_clk - 1; |
| if(((I2C_WCLK_FREQ/1000)/4) % bus_clk != 0){ |
| pdiv++; |
| } |
| |
| actual_clk = (I2C_WCLK_FREQ/4)/(pdiv+1); /*actul bus clock rate*/ |
| if ((actual_clk > MAX_BUS_CLK) || (actual_clk < MIN_BUS_CLK)){ |
| pr_warn("bus clock rate error %ld!\n", actual_clk); |
| BUG(); |
| } |
| i2c_set_clk_div(i2c, pdiv); |
| |
| /*master mode, disable interrupt, clear interrupt*/ |
| i2c_set_cmd(i2c, 0x1); |
| |
| soft_spin_unlock(softLock); |
| |
| i2c->state = STATE_IDLE; |
| |
| return 0; |
| } |
| |
| /* |
| *zx29_i2c_start |
| * put the start of a message onto the bus |
| */ |
| static void zx29_i2c_start(struct zx29_i2c *i2c, uint8_t fmt) |
| { |
| unsigned int addr = i2c->msg->addr; |
| unsigned long ctrl = 0; |
| unsigned int addr_mode = 0; |
| |
| /*set slave device address*/ |
| addr_mode = i2c->msg->flags & I2C_M_TEN; |
| |
| i2c_set_addr(i2c, addr, addr_mode); |
| |
| i2c_clr_irq_ack(i2c); |
| |
| /*set cmd register for rx or tx*/ |
| ctrl = i2c_get_cmd(i2c); |
| ctrl &= ~CMD_I2C_FMT_MSK; |
| ctrl |= fmt; |
| |
| if (addr_mode){ |
| /*10 bit address mode :clear interrupt conditions, set master mode,set 10bit address mode and start*/ |
| ctrl |= CMD_I2C_START|CMD_I2C_MODE|CMD_TENBIT_MODE; |
| } |
| else{ |
| /*7 bit address mode:clear interrupt conditions, set master mode and start*/ |
| ctrl &= ~CMD_TENBIT_MODE; |
| ctrl |= CMD_I2C_START|CMD_I2C_MODE; |
| } |
| |
| i2c_set_cmd(i2c, ctrl); |
| } |
| |
| |
| /* |
| * zx29_i2c_stop |
| */ |
| |
| static inline void zx29_i2c_stop(struct zx29_i2c *i2c) |
| { |
| //unsigned long ctrl = ioread32(i2c->regs + I2C_CMD); |
| |
| /* stop the transfer ,clear interrupt conditions */ |
| //ctrl |= CMD_I2C_STOP|CMD_IRQ_ACK; |
| //iowrite32(ctrl, i2c->regs + I2C_CMD); |
| |
| i2c->state = STATE_STOP; |
| } |
| |
| /* |
| * check i2c busy or not |
| */ |
| static inline int zx29_i2c_is_busy(struct zx29_i2c *i2c) |
| { |
| unsigned int time_out=0xff; |
| |
| while(time_out--){ |
| if(i2c_get_bus_status(i2c)==0) |
| return 0; |
| else |
| udelay(1); |
| } |
| return -ETIMEDOUT; |
| } |
| |
| /* |
| * i2c fifo write |
| */ |
| static void zx29_i2c_prepare_write(struct zx29_i2c *i2c) |
| { |
| u8 val=0; |
| u8 *buf=i2c->msg->buf; |
| u16 len =i2c->buf_remaining; |
| u16 len_tx = 0; |
| unsigned int ret=0; |
| |
| /*this step must be done. because FIFO empty interrupt does not mean bus is idle*/ |
| ret=zx29_i2c_is_busy(i2c); |
| if(ret) |
| BUG(); |
| |
| zx29_i2c_reset_tx_fifo(i2c); |
| |
| if(len > I2C_FIFO_DEPTH) |
| len_tx = I2C_FIFO_DEPTH; |
| else |
| len_tx = len; |
| i2c->buf_remaining -= len_tx; |
| |
| i2c_set_tx_fifo_depth(i2c, len_tx-1); |
| |
| /*write data to fifo*/ |
| while(len_tx--){ |
| val = *(buf + i2c->msg_ptr); |
| i2c_write_data(i2c, val); |
| i2c->msg_ptr++; |
| } |
| barrier(); |
| |
| // zx29_i2c_start(i2c); |
| } |
| |
| /* |
| * i2c start read |
| */ |
| static void zx29_i2c_prepare_read(struct zx29_i2c *i2c) |
| { |
| u16 len =i2c->buf_remaining; |
| u16 len_rx = 0; |
| unsigned int ret=0; |
| |
| /*i2c can only be operated when bus is idle*/ |
| ret=zx29_i2c_is_busy(i2c); |
| if(ret) |
| BUG(); |
| |
| zx29_i2c_reset_rx_fifo(i2c); |
| |
| if(len > I2C_FIFO_DEPTH) |
| len_rx = I2C_FIFO_DEPTH; |
| else |
| len_rx = len; |
| |
| i2c_set_rx_fifo_depth(i2c, len_rx-1); |
| |
| // zx29_i2c_start(i2c); |
| } |
| |
| /* |
| * i2c fifo read |
| */ |
| static void zx29_i2c_read_fifo(struct zx29_i2c *i2c) |
| { |
| unsigned int len_rx=0; |
| u8 *buf=i2c->msg->buf; |
| u8 val=0; |
| |
| len_rx = i2c_get_rx_fifo_length(i2c); |
| |
| i2c->buf_remaining -= len_rx; |
| |
| /*read data from fifo*/ |
| while(len_rx--){ |
| val =(u8) i2c_read_data(i2c); |
| *(buf + i2c->msg_ptr) = val; |
| i2c->msg_ptr++; |
| } |
| } |
| |
| /* zx29_i2c_irq |
| * |
| * top level IRQ servicing routine |
| */ |
| static irqreturn_t zx29_i2c_irq(int irqno, void *dev_id) |
| { |
| struct zx29_i2c *i2c = dev_id; |
| unsigned long status; |
| |
| status = i2c_get_irq_status(i2c); |
| i2c->reg_status = (i2c->reg_status << 8) | status; |
| |
| /* |
| * if state is idle or stop, maybe other core on 7510 platform trigger interrupt |
| * it will be looked as spurious interrupt, nothing need to be done |
| */ |
| if ((i2c->state == STATE_IDLE) || (i2c->state == STATE_STOP)) |
| return IRQ_HANDLED; |
| |
| if (status & (IRQ_ERR_DEVICE | IRQ_ERR_DATA | IRQ_TRANS_DONE)) { |
| i2c_disable_irq(i2c); |
| disable_irq_nosync(i2c->irq); |
| |
| up(&i2c->msg_complete); |
| |
| return IRQ_HANDLED; |
| } |
| |
| if (status & IRQ_TIME_OUT) { |
| i2c_clr_irq_ack(i2c); |
| return IRQ_HANDLED; |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| |
| /* zx29_i2c_get_bus |
| * |
| * get the i2c bus for a master transaction |
| */ |
| static int zx29_i2c_get_bus(struct zx29_i2c *i2c) |
| { |
| int timeout = 400; |
| |
| while (timeout-- > 0) { |
| |
| if (!i2c_get_bus_status(i2c)) |
| return 0; |
| |
| msleep(1); |
| } |
| |
| return -ETIMEDOUT; |
| } |
| |
| /* zx29_i2c_doxfer |
| * |
| * this starts an i2c transfer |
| */ |
| static int zx29_i2c_doxfer(struct zx29_i2c *i2c, struct i2c_msg *msgs, bool cmb_rw) |
| { |
| int ret; |
| |
| if (i2c->suspended) |
| return -EIO; |
| |
| i2c->msg = msgs; |
| i2c->msg_ptr = 0; |
| i2c->buf_remaining = msgs->len; |
| // i2c->msg_idx = 0; |
| |
| // init_completion(&(i2c->msg_complete)); |
| if (unlikely(i2c->msg_complete.count)) { |
| pr_err("i2c sem %d\n", i2c->msg_complete.count); |
| } |
| i2c->reg_status = 0; |
| i2c_clr_irq_ack(i2c); |
| i2c_enable_irq(i2c); |
| enable_irq(i2c->irq); |
| |
| if (cmb_rw) { |
| zx29_i2c_prepare_write(i2c); |
| |
| i2c->msg = &msgs[1]; |
| i2c->msg_ptr = 0; |
| i2c->buf_remaining = msgs[1].len; |
| |
| i2c->state = STATE_READ; |
| memset(msgs[1].buf, 0, msgs[1].len); |
| zx29_i2c_prepare_read(i2c); |
| |
| zx29_i2c_start(i2c, CMD_I2C_FMT_CMB); |
| } else if (i2c->msg->flags & I2C_M_RD) { |
| i2c->state = STATE_READ; |
| memset(msgs->buf, 0, msgs->len); |
| zx29_i2c_prepare_read(i2c); |
| zx29_i2c_start(i2c, CMD_I2C_FMT_RD); |
| } else { |
| i2c->state = STATE_WRITE; |
| zx29_i2c_prepare_write(i2c); |
| zx29_i2c_start(i2c, CMD_I2C_FMT_WR); |
| } |
| |
| #if I2C_LONG_TRANSFER |
| if (i2c->msg->len > I2C_FIFO_DEPTH) { |
| zx29_i2c_start_timer(i2c); |
| } |
| #endif |
| |
| /* waiting for tranfer finished */ |
| ret = down_timeout(&i2c->msg_complete, I2C_TIMEOUT); |
| |
| #if I2C_LONG_TRANSFER |
| if (i2c->msg->len > I2C_FIFO_DEPTH) { |
| zx29_i2c_stop_timer(i2c); |
| } |
| #endif |
| |
| if (ret < 0) { |
| i2c_disable_irq(i2c); |
| disable_irq_nosync(i2c->irq); |
| |
| pr_err("i2c transfer timeout\n"); |
| |
| zx29_i2c_stop(i2c); |
| zx29_i2c_init(i2c); |
| |
| return -ETIMEDOUT; |
| } |
| |
| if (i2c->state == STATE_READ) { |
| zx29_i2c_read_fifo(i2c); |
| } |
| |
| /* slave no ack on address phase or data phase */ |
| if (i2c_get_irq_status(i2c) & (IRQ_ERR_DEVICE | IRQ_ERR_DATA)) { |
| pr_err("NACK by slave 0x%X, status %X\n", i2c->msg->addr, i2c_get_irq_status(i2c)); |
| |
| while (~ i2c_get_irq_status(i2c) & IRQ_TRANS_DONE); |
| i2c_clr_irq_ack(i2c); |
| |
| zx29_i2c_init(i2c); |
| |
| return -ECOMM; |
| } |
| |
| i2c_clr_irq_ack(i2c); |
| i2c->state = STATE_IDLE; |
| |
| #if I2C_LONG_TRANSFER |
| if(i2c->buf_remaining) { |
| pr_err("%s: %d/%d isn't transferred\n", __FUNCTION__, i2c->buf_remaining, i2c->msg->len); |
| return -EAGAIN; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| /* zx29_i2c_xfer |
| * |
| * first port of call from the i2c bus code when an message needs |
| * transferring across the i2c bus. |
| */ |
| static int zx29_i2c_xfer(struct i2c_adapter *adap, |
| struct i2c_msg *msgs, int num) |
| { |
| struct zx29_i2c *i2c = (struct zx29_i2c *)adap->algo_data; |
| int ret; |
| int i = 0; |
| bool combie_rw_applicable; |
| unsigned int pdiv; |
| emsf_lock_id softLock = i2c_get_sflock(i2c->id); |
| |
| if (i2c->suspended) |
| return -EIO; |
| |
| if (msgs->len == 0) |
| return -EINVAL; |
| |
| #if I2C_PSM_CONTROL |
| zx29_i2c_set_active(&i2c->psm_lock); |
| #endif |
| |
| soft_spin_lock(softLock); |
| |
| #ifdef CONFIG_ARCH_ZX297502 |
| clk_enable(i2c->clk); |
| #endif |
| |
| ret = zx29_i2c_get_bus(i2c); |
| if (ret != 0) { |
| dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); |
| |
| //soft_spin_unlock(I2C_SFLOCK); /* zhouqi */ |
| |
| ret = -EAGAIN; |
| goto exit; |
| } |
| |
| /* calculate bus clock division, fixed input clock is 26M */ |
| pdiv = (I2C_WCLK_FREQ / 4 / i2c->clkrate) - 1; |
| if((I2C_WCLK_FREQ / 4) % i2c->clkrate != 0) |
| pdiv++; |
| i2c_set_clk_div(i2c, pdiv); |
| |
| // i2c->msg_num = num; |
| /* check whether combined rw is applicable */ |
| if ((num == 2) && (msgs[0].addr == msgs[1].addr)) { |
| combie_rw_applicable = ((msgs[0].flags & I2C_M_TEN) == (msgs[1].flags & I2C_M_TEN)) \ |
| && (~msgs[0].flags & I2C_M_RD) \ |
| && (msgs[1].flags & I2C_M_RD) \ |
| && (msgs[0].len <= I2C_FIFO_DEPTH); |
| } else |
| combie_rw_applicable = 0; |
| |
| if (combie_rw_applicable) { |
| ret = zx29_i2c_doxfer(i2c, msgs, 1); |
| } else for (i = 0; i < num; i++) { |
| ret = zx29_i2c_doxfer(i2c, &msgs[i], 0); |
| // i2c->msg_idx=i; |
| if (ret) { |
| //printk(KERN_INFO "%s err code=%d\n", __FUNCTION__, ret); |
| break; |
| } |
| } |
| |
| exit: |
| #ifdef CONFIG_ARCH_ZX297502 |
| clk_disable(i2c->clk); |
| #endif |
| |
| soft_spin_unlock(softLock); |
| |
| #if I2C_PSM_CONTROL |
| zx29_i2c_set_idle(&i2c->psm_lock); |
| #endif |
| |
| return ret ? ret : num; |
| } |
| |
| /* declare our i2c functionality */ |
| static u32 zx29_i2c_func(struct i2c_adapter *adap) |
| { |
| return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK)| I2C_FUNC_10BIT_ADDR; |
| } |
| |
| /* i2c bus registration info */ |
| static const struct i2c_algorithm zx29_i2c_algorithm = { |
| .master_xfer = zx29_i2c_xfer, |
| .functionality = zx29_i2c_func, |
| }; |
| |
| #ifdef CONFIG_OF |
| /* zx29_i2c_parse_dt |
| * |
| * Parse the device tree node and retreive the platform data. |
| */ |
| static void |
| zx29_i2c_parse_dt(struct device_node *np, struct zx29_i2c *i2c) |
| { |
| struct zx29_i2c_platform_data *pdata = i2c->pdata; |
| |
| if (!np) |
| return; |
| of_property_read_u32(np, "Sanechips-tsp,i2c-slave-addr", &pdata->slave_addr); |
| of_property_read_u32(np, "Sanechips-tsp,i2c-max-bus-freq",(u32 *)&pdata->max_bus_clk); |
| } |
| #else |
| static void |
| zx29_i2c_parse_dt(struct device_node *np, struct zx29_i2c *i2c) |
| { |
| return; |
| } |
| #endif |
| |
| static int i2c_init_clks(struct platform_device *pdev, struct zx29_i2c *i2c) |
| { |
| /* find the clock and enable it */ |
| i2c->clk = clk_get(&pdev->dev, "work_clk"); |
| if (IS_ERR(i2c->clk)) { |
| dev_err(&pdev->dev, "cannot get work clock\n"); |
| return -ENOENT; |
| } |
| |
| /* set i2c work clock at 26MHz/1 */ |
| clk_set_rate(i2c->clk, I2C_WCLK_FREQ); |
| clk_enable(i2c->clk); |
| |
| i2c->pclk = clk_get(&pdev->dev, "apb_clk"); |
| if (IS_ERR(i2c->pclk)) { |
| dev_err(&pdev->dev, "cannot get apb clock\n"); |
| return -ENOENT; |
| } |
| clk_enable(i2c->pclk); |
| |
| clk_set_auto_gate(i2c->clk, true); |
| clk_set_auto_gate(i2c->pclk, true); |
| |
| return 0; |
| } |
| |
| /* zx29_i2c_probe |
| * |
| * called by the bus driver when a suitable device is found |
| */ |
| static int zx29_i2c_probe(struct platform_device *pdev) |
| { |
| struct zx29_i2c *i2c=NULL; |
| struct zx29_i2c_platform_data *pdata = NULL; |
| struct resource *res=NULL; |
| void __iomem *base=NULL; |
| const unsigned int *prop=NULL; |
| int ret=0; |
| emsf_lock_id softLock; |
| |
| /* irq®ister */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| dev_err(&pdev->dev, "no memory resource\n"); |
| ret = -EINVAL; |
| goto err_iomap; |
| } |
| base = (void __iomem *)(res->start); |
| |
| res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| if (!res) { |
| dev_err(&pdev->dev, "no irq resource\n"); |
| ret = -EINVAL; |
| goto err_iomap; |
| } |
| |
| i2c = devm_kzalloc(&pdev->dev, sizeof(struct zx29_i2c), GFP_KERNEL); |
| if (!i2c) { |
| dev_err(&pdev->dev, "no memory for state\n"); |
| ret = -ENOMEM; |
| goto err_iomap; |
| } |
| |
| if (!pdev->dev.of_node) { |
| pdata = pdev->dev.platform_data; |
| if (!pdata) { |
| dev_err(&pdev->dev, "no platform data\n"); |
| return -EINVAL; |
| } |
| } |
| |
| snprintf(i2c->name, sizeof(i2c->name), "zx29-i2c%d", pdev->id); |
| i2c->pdata = pdata; |
| i2c->id = pdev->id; |
| i2c->regs = base; |
| i2c->irq = res->start; |
| i2c->dev = &pdev->dev; |
| i2c->state = STATE_IDLE; |
| |
| i2c->clkrate = MIN_BUS_CLK; /* default clock rate */ |
| if (pdata) { |
| i2c->clkrate = pdata->bus_clk_rate; |
| } |
| else if (i2c->dev->of_node) { /* if there is a device tree node ... */ |
| prop = of_get_property(i2c->dev->of_node, |
| "clock-frequency", NULL); |
| if (prop) |
| i2c->clkrate = be32_to_cpup(prop); |
| } |
| |
| spin_lock_init(&i2c->lock); |
| |
| sema_init(&i2c->msg_complete, 0); |
| |
| softLock = i2c_get_sflock(i2c->id); |
| soft_spin_lock(softLock); |
| |
| i2c_config_pins(i2c); |
| |
| ret = i2c_init_clks(pdev, i2c); |
| if(ret < 0) |
| { |
| soft_spin_unlock(softLock); |
| goto err_free; |
| } |
| |
| /* initialise the i2c controller */ |
| ret = zx29_i2c_init(i2c); |
| if (ret != 0){ |
| soft_spin_unlock(softLock); |
| goto err_free; |
| } |
| |
| i2c_disable_irq(i2c); |
| /* find the IRQ for this unit (note, this relies on the init call to |
| * ensure no current IRQs pending |
| */ |
| ret = devm_request_irq(&pdev->dev, i2c->irq, zx29_i2c_irq, |
| IRQF_TRIGGER_HIGH | IRQF_NO_THREAD | IRQF_ONESHOT, |
| dev_name(&pdev->dev), i2c); |
| if (ret != 0) { |
| dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); |
| soft_spin_unlock(softLock); |
| goto err_clk; |
| } |
| disable_irq_nosync(i2c->irq); |
| |
| soft_spin_unlock(softLock); |
| |
| /* Note, previous versions of the driver used i2c_add_adapter() |
| * to add the bus at any number. We now pass the bus number via |
| * the platform data, so if unset it will now default to always |
| * being bus 0. |
| */ |
| zx29_i2c_parse_dt(pdev->dev.of_node, i2c); |
| strlcpy(i2c->adap.name, "zx29-i2c", sizeof(i2c->adap.name)); |
| i2c->adap.owner = THIS_MODULE; |
| i2c->adap.algo = &zx29_i2c_algorithm; |
| i2c->adap.retries = 3; |
| i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; |
| i2c->adap.algo_data = i2c; |
| i2c->adap.dev.parent = &pdev->dev; |
| i2c->adap.nr = pdev->id; |
| i2c->adap.dev.of_node = pdev->dev.of_node; |
| |
| #if I2C_PSM_CONTROL |
| wake_lock_init(&i2c->psm_lock, WAKE_LOCK_SUSPEND, i2c->name); |
| #endif |
| |
| ret = i2c_add_numbered_adapter(&i2c->adap); |
| if (ret < 0) { |
| dev_err(&pdev->dev, "failed to add bus to i2c core\n"); |
| goto err_irq; |
| } |
| |
| // of_i2c_register_devices(&i2c->adap); |
| platform_set_drvdata(pdev, i2c); |
| |
| zx29_i2c_sysfs_create_group(i2c); |
| |
| dev_info(&pdev->dev, "%s: ZX29 I2C adapter\n", dev_name(&i2c->adap.dev)); |
| |
| #ifdef CONFIG_ARCH_ZX297502 |
| clk_disable(i2c->clk); |
| #endif |
| return 0; |
| |
| /*err_cpufreq: |
| zx29_i2c_deregister_cpufreq(i2c);*/ |
| |
| err_irq: |
| #if I2C_PSM_CONTROL |
| wake_lock_destroy(&i2c->psm_lock); |
| #endif |
| devm_free_irq(&pdev->dev, i2c->irq, i2c); |
| |
| err_clk: |
| clk_disable(i2c->clk); |
| clk_put(i2c->clk); |
| clk_disable(i2c->pclk); |
| clk_put(i2c->pclk); |
| |
| |
| err_free: |
| kfree(i2c); |
| |
| err_iomap: |
| // iounmap(base); |
| |
| return ret; |
| } |
| |
| /* zx29_i2c_remove |
| * |
| * called when device is removed from the bus |
| */ |
| static int zx29_i2c_remove(struct platform_device *pdev) |
| { |
| struct zx29_i2c *i2c = platform_get_drvdata(pdev); |
| |
| zx29_i2c_sysfs_remove_group(i2c); |
| |
| i2c_del_adapter(&i2c->adap); |
| |
| clk_disable(i2c->clk); |
| clk_put(i2c->clk); |
| clk_disable(i2c->pclk); |
| clk_put(i2c->pclk); |
| |
| #if I2C_PSM_CONTROL |
| wake_lock_destroy(&i2c->psm_lock); |
| #endif |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| static int zx29_i2c_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| |
| #ifdef CONFIG_ARCH_ZX297502 |
| struct zx29_i2c *i2c = platform_get_drvdata(pdev); |
| |
| i2c_lock_adapter(&i2c->adap); |
| i2c->suspended = 1; |
| i2c_unlock_adapter(&i2c->adap); |
| #endif |
| |
| return 0; |
| } |
| |
| static int zx29_i2c_resume(struct platform_device *pdev) |
| { |
| |
| #ifdef CONFIG_ARCH_ZX297502 |
| struct zx29_i2c *i2c = platform_get_drvdata(pdev); |
| |
| i2c_lock_adapter(&i2c->adap); |
| clk_enable(i2c->clk); |
| zx29_i2c_init(i2c); |
| clk_disable(i2c->clk); |
| i2c->suspended = 0; |
| i2c_unlock_adapter(&i2c->adap); |
| #endif |
| |
| return 0; |
| } |
| #endif |
| |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id zx29_i2c_match[] = { |
| { .compatible = "Sanechips-TSP, zx29_i2c0" }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, zx29_i2c_match); |
| #else |
| #define zx29_i2c_match NULL |
| #endif |
| |
| static struct platform_driver zx29_i2c_driver = { |
| .probe = zx29_i2c_probe, |
| .remove = zx29_i2c_remove, |
| #ifdef CONFIG_PM |
| .suspend = zx29_i2c_suspend, |
| .resume = zx29_i2c_resume, |
| #endif |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = "zx29_i2c", |
| .of_match_table = zx29_i2c_match, |
| }, |
| }; |
| |
| static int __init i2c_adap_zx29_init(void) |
| { |
| int ret; |
| |
| ret=platform_driver_register(&zx29_i2c_driver); |
| if (ret<0) { |
| pr_err("zx29 i2c driver register fail\n"); |
| } |
| return ret; |
| } |
| subsys_initcall(i2c_adap_zx29_init); |
| |
| static void __exit i2c_adap_zx29_exit(void) |
| { |
| platform_driver_unregister(&zx29_i2c_driver); |
| } |
| module_exit(i2c_adap_zx29_exit); |
| |