/* * drivers/i2c/busses/i2c-sunxi.c * * Copyright (C) 2013 Allwinner. * Pan Nan * * SUNXI TWI Controller Driver * * 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. * * 2013.5.3 Mintow * Adapt to all the new chip of Allwinner. Support sun8i/sun9i */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "i2c-sunxi.h" /* For debug */ #define I2C_EXIT() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Exit") #define I2C_ENTER() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Enter ...") #define I2C_DBG(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg) #define I2C_ERR(fmt, arg...) pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg) #ifndef CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO static int bus_transfer_dbg = -1; module_param_named(transfer_debug, bus_transfer_dbg, int, S_IRUGO | S_IWUSR | S_IWGRP); #endif #define SUNXI_I2C_OK 0 #define SUNXI_I2C_FAIL -1 #define SUNXI_I2C_RETRY -2 #define SUNXI_I2C_SFAIL -3 /* start fail */ #define SUNXI_I2C_TFAIL -4 /* stop fail */ static int twi_used_mask; /* I2C transfer status */ enum { I2C_XFER_IDLE = 0x1, I2C_XFER_START = 0x2, I2C_XFER_RUNNING = 0x4, }; struct sunxi_i2c { int bus_num; unsigned int status; /* start, running, idle */ unsigned int suspended:1; struct i2c_adapter adap; struct device *dev; spinlock_t lock; /* syn */ wait_queue_head_t wait; struct i2c_msg *msg; unsigned int msg_num; unsigned int msg_idx; unsigned int msg_ptr; struct clk *mclk; unsigned int bus_freq; int irq; unsigned int debug_state; /* log the twi machine state */ void __iomem *base_addr; struct pinctrl *pctrl; }; /* clear the interrupt flag */ static inline void twi_clear_irq_flag(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); /* start and stop bit should be 0 */ reg_val |= TWI_CTL_INTFLG; reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP); writel(reg_val, base_addr + TWI_CTL_REG); /* read two more times to make sure that */ /* interrupt flag does really be cleared */ { unsigned int temp; temp = readl(base_addr + TWI_CTL_REG); temp |= readl(base_addr + TWI_CTL_REG); } } /* get data first, then clear flag */ static inline void twi_get_byte(void __iomem *base_addr, unsigned char *buffer) { *buffer = (unsigned char)(TWI_DATA_MASK & readl(base_addr + TWI_DATA_REG)); twi_clear_irq_flag(base_addr); } /* only get data, we will clear the flag when stop */ static inline void twi_get_last_byte(void __iomem *base_addr, unsigned char *buffer) { *buffer = (unsigned char)(TWI_DATA_MASK & readl(base_addr + TWI_DATA_REG)); } /* write data and clear irq flag to trigger send flow */ static inline void twi_put_byte(void __iomem *base_addr, const unsigned char *buffer) { writel((unsigned int)*buffer, base_addr + TWI_DATA_REG); twi_clear_irq_flag(base_addr); } static inline void twi_enable_irq(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); /* * 1 when enable irq for next operation, set intflag to 0 to prevent * to clear it by a mistake (intflag bit is write-1-to-clear bit) * 2 Similarly, mask START bit and STOP bit to prevent to set it * twice by a mistake (START bit and STOP bit are self-clear-to-0 bits) */ reg_val |= TWI_CTL_INTEN; reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP | TWI_CTL_INTFLG); writel(reg_val, base_addr + TWI_CTL_REG); } static inline void twi_disable_irq(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val &= ~TWI_CTL_INTEN; reg_val &= ~(TWI_CTL_STA | TWI_CTL_STP | TWI_CTL_INTFLG); writel(reg_val, base_addr + TWI_CTL_REG); } static inline void twi_disable_bus(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val &= ~TWI_CTL_BUSEN; writel(reg_val, base_addr + TWI_CTL_REG); } static inline void twi_enable_bus(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_BUSEN; writel(reg_val, base_addr + TWI_CTL_REG); } /* trigger start signal, the start bit will be cleared automatically */ static inline void twi_set_start(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_STA; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* get start bit status, poll if start signal is sent */ static inline unsigned int twi_get_start(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val >>= 5; return reg_val & 1; } /* trigger stop signal, the stop bit will be cleared automatically */ static inline void twi_set_stop(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_STP; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* get stop bit status, poll if stop signal is sent */ static inline unsigned int twi_get_stop(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val >>= 4; return reg_val & 1; } static inline void twi_disable_ack(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val &= ~TWI_CTL_ACK; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* when sending ack or nack, it will send ack automatically */ static inline void twi_enable_ack(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); reg_val |= TWI_CTL_ACK; reg_val &= ~TWI_CTL_INTFLG; writel(reg_val, base_addr + TWI_CTL_REG); } /* get the interrupt flag */ static inline unsigned int twi_query_irq_flag(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CTL_REG); return (reg_val & TWI_CTL_INTFLG);/* 0x 0000_1000 */ } /* get interrupt status */ static inline unsigned int twi_query_irq_status(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_STAT_REG); return (reg_val & TWI_STAT_MASK); } /* set twi clock * * clk_n: clock divider factor n * clk_m: clock divider factor m */ static inline void twi_clk_write_reg(unsigned int clk_n, unsigned int clk_m, void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_CLK_REG); I2C_DBG("%s: clk_n = %d, clk_m = %d\n", __func__, clk_n, clk_m); reg_val &= ~(TWI_CLK_DIV_M | TWI_CLK_DIV_N); reg_val |= (clk_n | (clk_m << 3)); writel(reg_val, base_addr + TWI_CLK_REG); } /* * Fin is APB CLOCK INPUT; * Fsample = F0 = Fin/2^CLK_N; * F1 = F0/(CLK_M+1); * Foscl = F1/10 = Fin/(2^CLK_N * (CLK_M+1)*10); * Foscl is clock SCL;100KHz or 400KHz * * clk_in: apb clk clock * sclk_req: freqence to set in HZ */ static int twi_set_clock(unsigned int clk_in, unsigned int sclk_req, void __iomem *base_addr) { unsigned int clk_m = 0; unsigned int clk_n = 0; unsigned int _2_pow_clk_n = 1; unsigned int src_clk = clk_in/10; unsigned int divider = src_clk/sclk_req; /* 400khz or 100khz */ unsigned int sclk_real = 0; /* the real clock frequency */ if (divider == 0) { clk_m = 1; goto set_clk; } /* * search clk_n and clk_m,from large to small value so * that can quickly find suitable m & n. */ while (clk_n < 8) { /* 3bits max value is 8 */ /* (m+1)*2^n = divider -->m = divider/2^n -1 */ clk_m = (divider/_2_pow_clk_n) - 1; /* clk_m = (divider >> (_2_pow_clk_n>>1))-1 */ while (clk_m < 16) { /* 4bits max value is 16 */ /* src_clk/((m+1)*2^n) */ sclk_real = src_clk/(clk_m + 1)/_2_pow_clk_n; if (sclk_real <= sclk_req) goto set_clk; else clk_m++; } clk_n++; _2_pow_clk_n *= 2; /* mutilple by 2 */ } set_clk: twi_clk_write_reg(clk_n, clk_m, base_addr); return 0; } /* soft reset twi */ static inline void twi_soft_reset(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_SRST_REG); reg_val |= TWI_SRST_SRST; writel(reg_val, base_addr + TWI_SRST_REG); } /* Enhanced Feature Register */ static inline void twi_set_efr(void __iomem *base_addr, unsigned int efr) { unsigned int reg_val = readl(base_addr + TWI_EFR_REG); reg_val &= ~TWI_EFR_MASK; efr &= TWI_EFR_MASK; reg_val |= efr; writel(reg_val, base_addr + TWI_EFR_REG); } static int sunxi_i2c_xfer_complete(struct sunxi_i2c *i2c, int code); static int sunxi_i2c_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num); static int twi_chan_is_enable(int _ch) { return twi_used_mask & SUNXI_TWI_CHAN_MASK(_ch); } static int twi_select_gpio_state(struct pinctrl *pctrl, char *name, u32 no) { int ret = 0; struct pinctrl_state *pctrl_state = NULL; pctrl_state = pinctrl_lookup_state(pctrl, name); if (IS_ERR(pctrl_state)) { I2C_ERR("TWI%d pinctrl_lookup_state(%s) failed! return %p\n", no, name, pctrl_state); return -1; } ret = pinctrl_select_state(pctrl, pctrl_state); if (ret < 0) I2C_ERR("TWI%d pinctrl_select_state(%s) failed! return %d\n", no, name, ret); return ret; } static int twi_request_gpio(struct sunxi_i2c *i2c) { I2C_DBG("Pinctrl init %d ... [%s]\n", i2c->bus_num, i2c->adap.dev.parent->init_name); if (!twi_chan_is_enable(i2c->bus_num)) return -1; i2c->pctrl = devm_pinctrl_get(i2c->adap.dev.parent); if (IS_ERR(i2c->pctrl)) { I2C_ERR("TWI%d devm_pinctrl_get() failed! return %ld\n", i2c->bus_num, PTR_ERR(i2c->pctrl)); return -1; } return twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_DEFAULT, i2c->bus_num); } static void twi_release_gpio(struct sunxi_i2c *i2c) { devm_pinctrl_put(i2c->pctrl); } /* function */ static int twi_start(void __iomem *base_addr, int bus_num) { unsigned int timeout = 0xff; twi_set_start(base_addr); while ((twi_get_start(base_addr) == 1) && (--timeout)) ; if (timeout == 0) { I2C_ERR("[i2c%d] START can't sendout!\n", bus_num); return SUNXI_I2C_FAIL; } return SUNXI_I2C_OK; } static int twi_restart(void __iomem *base_addr, int bus_num) { unsigned int timeout = 0xff; twi_set_start(base_addr); twi_clear_irq_flag(base_addr); while ((twi_get_start(base_addr) == 1) && (--timeout)) ; if (timeout == 0) { I2C_ERR("[i2c%d] Restart can't sendout!\n", bus_num); return SUNXI_I2C_FAIL; } return SUNXI_I2C_OK; } static int twi_stop(void __iomem *base_addr, int bus_num) { unsigned int timeout = 0xff; twi_set_stop(base_addr); twi_clear_irq_flag(base_addr); twi_get_stop(base_addr);/* it must delay 1 nop to check stop bit */ while ((twi_get_stop(base_addr) == 1) && (--timeout)) ; if (timeout == 0) { I2C_ERR("[i2c%d] STOP can't sendout!\n", bus_num); return SUNXI_I2C_TFAIL; } timeout = 0xff; while ((readl(base_addr + TWI_STAT_REG) != TWI_STAT_IDLE) && (--timeout)) ; if (timeout == 0) { I2C_ERR("[i2c%d] i2c state(0x%08x) isn't idle(0xf8)\n", bus_num, readl(base_addr + TWI_STAT_REG)); return SUNXI_I2C_TFAIL; } timeout = 0xff; while ((readl(base_addr + TWI_LCR_REG) != TWI_LCR_IDLE_STATUS && readl(base_addr + TWI_LCR_REG) != TWI_LCR_NORM_STATUS) && (--timeout)) ; if (timeout == 0) { I2C_ERR("[i2c%d] i2c lcr(0x%08x) isn't idle(0x3a)\n", bus_num, readl(base_addr + TWI_LCR_REG)); return SUNXI_I2C_TFAIL; } return SUNXI_I2C_OK; } /* get SDA state */ static unsigned int twi_get_sda(void __iomem *base_addr) { unsigned int status = 0; status = TWI_LCR_SDA_STATE_MASK & readl(base_addr + TWI_LCR_REG); status >>= 4; return (status&0x1); } /* set SCL level(high/low), only when SCL enable */ static void twi_set_scl(void __iomem *base_addr, unsigned int hi_lo) { unsigned int reg_val = readl(base_addr + TWI_LCR_REG); reg_val &= ~TWI_LCR_SCL_CTL; hi_lo &= 0x01; reg_val |= (hi_lo<<3); writel(reg_val, base_addr + TWI_LCR_REG); } /* enable SDA or SCL */ static void twi_enable_lcr(void __iomem *base_addr, unsigned int sda_scl) { unsigned int reg_val = readl(base_addr + TWI_LCR_REG); sda_scl &= 0x01; if (sda_scl) reg_val |= TWI_LCR_SCL_EN;/* enable scl line control */ else reg_val |= TWI_LCR_SDA_EN;/* enable sda line control */ writel(reg_val, base_addr + TWI_LCR_REG); } /* disable SDA or SCL */ static void twi_disable_lcr(void __iomem *base_addr, unsigned int sda_scl) { unsigned int reg_val = readl(base_addr + TWI_LCR_REG); sda_scl &= 0x01; if (sda_scl) reg_val &= ~TWI_LCR_SCL_EN;/* disable scl line control */ else reg_val &= ~TWI_LCR_SDA_EN;/* disable sda line control */ writel(reg_val, base_addr + TWI_LCR_REG); } /* send 9 clock to release sda */ static int twi_send_clk_9pulse(void __iomem *base_addr, int bus_num) { int twi_scl = 1; int low = 0; int high = 1; int cycle = 0; unsigned char status; /* enable scl control */ twi_enable_lcr(base_addr, twi_scl); while (cycle < 9) { if (twi_get_sda(base_addr) && twi_get_sda(base_addr) && twi_get_sda(base_addr)) { break; } /* twi_scl -> low */ twi_set_scl(base_addr, low); udelay(1000); /* twi_scl -> high */ twi_set_scl(base_addr, high); udelay(1000); cycle++; } if (twi_get_sda(base_addr)) { twi_disable_lcr(base_addr, twi_scl); status = SUNXI_I2C_OK; } else { I2C_ERR("[i2c%d] SDA is still Stuck Low, failed.\n", bus_num); twi_disable_lcr(base_addr, twi_scl); status = SUNXI_I2C_FAIL; } return status; } static int twi_regulator_request(struct sunxi_i2c_platform_data *pdata) { struct regulator *regu = NULL; /* Consider "n*" as nocare. Support "none", "nocare", "null", "" etc. */ if ((pdata->regulator_id[0] == 'n') || (pdata->regulator_id[0] == 0)) return 0; regu = regulator_get(NULL, pdata->regulator_id); if (IS_ERR(regu)) { I2C_ERR("[i2c%d] get regulator %s failed!\n", pdata->bus_num, pdata->regulator_id); return -1; } pdata->regulator = regu; return 0; } static int twi_regulator_release(struct sunxi_i2c_platform_data *pdata) { if (pdata->regulator == NULL) return 0; regulator_put(pdata->regulator); pdata->regulator = NULL; return 1; } static int twi_regulator_enable(struct sunxi_i2c_platform_data *pdata) { if (pdata->regulator == NULL) return 0; if (regulator_enable(pdata->regulator) != 0) { I2C_ERR("[i2c%d] enable regulator %s failed!\n", pdata->bus_num, pdata->regulator_id); return -1; } return 0; } static int twi_regulator_disable(struct sunxi_i2c_platform_data *pdata) { if (pdata->regulator == NULL) return 0; if (regulator_disable(pdata->regulator) != 0) { I2C_ERR("[i2c%d] enable regulator %s failed!\n", pdata->bus_num, pdata->regulator_id); return -1; } return 0; } /* **************************************************************************** * * FunctionName: sunxi_i2c_addr_byte * * Description: * 7bits addr: 7-1bits addr+0 bit r/w * 10bits addr: 1111_11xx_xxxx_xxxx-->1111_0xx_rw,xxxx_xxxx * send the 7 bits addr,or the first part of 10 bits addr * Parameters: * * * Return value: * �� * Notes: * **************************************************************************** */ static void sunxi_i2c_addr_byte(struct sunxi_i2c *i2c) { unsigned char addr = 0; unsigned char tmp = 0; if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) { /* 0111_10xx,ten bits address--9:8bits */ tmp = 0x78 | (((i2c->msg[i2c->msg_idx].addr)>>8) & 0x03); addr = tmp << 1; /*1111_0xx0*/ /* how about the second part of ten bits addr? */ /* Answer: deal at twi_core_process() */ } else /* 7-1bits addr, xxxx_xxx0 */ addr = (i2c->msg[i2c->msg_idx].addr & 0x7f) << 1; /* read, default value is write */ if (i2c->msg[i2c->msg_idx].flags & I2C_M_RD) addr |= 1; #ifdef CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO if (i2c->bus_num == CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO_WITH_BUS_NUM) { if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) I2C_DBG("[i2c%d] first part of 10bits = 0x%x\n", i2c->bus_num, addr); I2C_DBG("[i2c%d] 7bits+r/w = 0x%x\n", i2c->bus_num, addr); } #else if (unlikely(bus_transfer_dbg != -1)) { if (i2c->bus_num == bus_transfer_dbg) { if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) I2C_DBG("[i2c%d] first part of 10bits = 0x%x\n", i2c->bus_num, addr); I2C_DBG("[i2c%d] 7bits+r/w = 0x%x\n", i2c->bus_num, addr); } } #endif /* send 7bits+r/w or the first part of 10bits */ twi_put_byte(i2c->base_addr, &addr); } static int sunxi_i2c_core_process(struct sunxi_i2c *i2c) { void __iomem *base_addr = i2c->base_addr; int ret = SUNXI_I2C_OK; int err_code = 0; unsigned char state = 0; unsigned char tmp = 0; unsigned long flags = 0; state = twi_query_irq_status(base_addr); spin_lock_irqsave(&i2c->lock, flags); #ifdef CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO if (i2c->bus_num == CONFIG_SUNXI_I2C_PRINT_TRANSFER_INFO_WITH_BUS_NUM) I2C_DBG("[i2c%d][slave address = (0x%x), state = (0x%x)]\n", i2c->bus_num, i2c->msg->addr, state); #else if (unlikely(bus_transfer_dbg != -1)) { if (i2c->bus_num == bus_transfer_dbg) I2C_DBG("[i2c%d][slave address = (0x%x), state = (0x%x)]\n", i2c->bus_num, i2c->msg->addr, state); } #endif if (i2c->msg == NULL) { I2C_ERR("[i2c%d] i2c message is NULL, err_code = 0xfe\n", i2c->bus_num); err_code = 0xfe; goto msg_null; } switch (state) { case 0xf8: /* On reset or stop the bus is idle, use only at poll method */ err_code = 0xf8; goto err_out; case 0x08: /* A START condition has been transmitted */ case 0x10: /* A repeated start condition has been transmitted */ sunxi_i2c_addr_byte(i2c);/* send slave address */ break; case 0xd8: /* second addr has transmitted, ACK not received! */ case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ err_code = 0x20; goto err_out; case 0x18: /* SLA+W has been transmitted; ACK has been received */ /* if any, send second part of 10 bits addr */ if (i2c->msg[i2c->msg_idx].flags & I2C_M_TEN) { /* the remaining 8 bits of address */ tmp = i2c->msg[i2c->msg_idx].addr & 0xff; twi_put_byte(base_addr, &tmp); /* case 0xd0: */ break; } /* for 7 bit addr, then directly send data byte--case 0xd0: */ case 0xd0: /* second addr has transmitted,ACK received! */ case 0x28: /* Data byte in DATA REG has been transmitted; */ /* ACK has been received */ /* after send register address then START send write data */ if (i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) { twi_put_byte(base_addr, &(i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr])); i2c->msg_ptr++; break; } i2c->msg_idx++; /* the other msg */ i2c->msg_ptr = 0; if (i2c->msg_idx == i2c->msg_num) { err_code = SUNXI_I2C_OK;/* Success,wakeup */ goto ok_out; } else if (i2c->msg_idx < i2c->msg_num) { /* for restart pattern, read spec, two msgs */ ret = twi_restart(base_addr, i2c->bus_num); if (ret == SUNXI_I2C_FAIL) { err_code = SUNXI_I2C_SFAIL; goto err_out;/* START can't sendout */ } } else { err_code = SUNXI_I2C_FAIL; goto err_out; } break; case 0x30: /* Data byte in I2CDAT has been transmitted; */ /* NOT ACK has been received */ err_code = 0x30; /*err,wakeup the thread*/ goto err_out; case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ err_code = 0x38; /*err,wakeup the thread*/ goto err_out; case 0x40: /* SLA+R has been transmitted; ACK has been received */ /* with Restart,needn't to send second part of 10 bits addr */ /* refer-"I2C-SPEC v2.1" */ /* enable A_ACK need it(receive data len) more than 1. */ if (i2c->msg[i2c->msg_idx].len > 1) { /* send register addr complete,then enable the A_ACK */ /* and get ready for receiving data */ twi_enable_ack(base_addr); twi_clear_irq_flag(base_addr);/* jump to case 0x50 */ } else if (i2c->msg[i2c->msg_idx].len == 1) { twi_clear_irq_flag(base_addr);/* jump to case 0x58 */ } break; case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ err_code = 0x48; /*err,wakeup the thread*/ goto err_out; case 0x50: /* Data bytes has been received; ACK has been transmitted */ /* receive first data byte */ if (i2c->msg_ptr < i2c->msg[i2c->msg_idx].len) { /* more than 2 bytes, the last byte need not to send ACK */ if ((i2c->msg_ptr + 2) == i2c->msg[i2c->msg_idx].len) /* last byte no ACK */ twi_disable_ack(base_addr); /* get data then clear flag,then next data coming */ twi_get_byte(base_addr, &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]); i2c->msg_ptr++; break; } /* err process, the last byte should be @case 0x58 */ err_code = SUNXI_I2C_FAIL;/* err, wakeup */ goto err_out; case 0x58: /* Data byte has been received; NOT ACK has been transmitted */ /* received the last byte */ if (i2c->msg_ptr == i2c->msg[i2c->msg_idx].len - 1) { twi_get_last_byte(base_addr, &i2c->msg[i2c->msg_idx].buf[i2c->msg_ptr]); i2c->msg_idx++; i2c->msg_ptr = 0; if (i2c->msg_idx == i2c->msg_num) { /* succeed,wakeup the thread */ err_code = SUNXI_I2C_OK; goto ok_out; } else if (i2c->msg_idx < i2c->msg_num) { /* repeat start */ ret = twi_restart(base_addr, i2c->bus_num); if (ret == SUNXI_I2C_FAIL) {/* START fail */ err_code = SUNXI_I2C_SFAIL; goto err_out; } break; } } else { err_code = 0x58; goto err_out; } case 0x00: /* Bus error during master or slave mode due to illegal level condition */ err_code = 0xff; goto err_out; default: err_code = state; goto err_out; } i2c->debug_state = state;/* just for debug */ spin_unlock_irqrestore(&i2c->lock, flags); return ret; ok_out: err_out: if (twi_stop(base_addr, i2c->bus_num) == SUNXI_I2C_TFAIL) I2C_ERR("[i2c%d] STOP failed!\n", i2c->bus_num); msg_null: ret = sunxi_i2c_xfer_complete(i2c, err_code);/* wake up */ i2c->debug_state = state;/* just for debug */ spin_unlock_irqrestore(&i2c->lock, flags); return ret; } static irqreturn_t sunxi_i2c_handler(int this_irq, void *dev_id) { struct sunxi_i2c *i2c = (struct sunxi_i2c *)dev_id; if (!twi_query_irq_flag(i2c->base_addr)) { I2C_ERR("unknown interrupt!\n"); return IRQ_NONE; } /* disable irq */ twi_disable_irq(i2c->base_addr); /* twi core process */ sunxi_i2c_core_process(i2c); /* enable irq only when twi is transferring, otherwise disable irq */ if (i2c->status != I2C_XFER_IDLE) twi_enable_irq(i2c->base_addr); return IRQ_HANDLED; } static int sunxi_i2c_xfer_complete(struct sunxi_i2c *i2c, int code) { int ret = SUNXI_I2C_OK; i2c->msg = NULL; i2c->msg_num = 0; i2c->msg_ptr = 0; i2c->status = I2C_XFER_IDLE; /* i2c->msg_idx store the information */ if (code == SUNXI_I2C_FAIL) { I2C_ERR("[i2c%d] Maybe Logic Error, debug it!\n", i2c->bus_num); i2c->msg_idx = code; ret = SUNXI_I2C_FAIL; } else if (code != SUNXI_I2C_OK) { i2c->msg_idx = code; ret = SUNXI_I2C_FAIL; } wake_up(&i2c->wait); return ret; } static int sunxi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct sunxi_i2c *i2c = (struct sunxi_i2c *)adap->algo_data; int ret = SUNXI_I2C_FAIL; int i = 0; ret = pm_runtime_get_sync(i2c->dev); if (ret < 0) goto out; for (i = 1; i <= adap->retries; i++) { ret = sunxi_i2c_do_xfer(i2c, msgs, num); if (ret != SUNXI_I2C_RETRY) goto out; I2C_DBG("[i2c%d] Retrying transmission %d\n", i2c->adap.nr, i); udelay(100); } ret = -EREMOTEIO; out: pm_runtime_mark_last_busy(i2c->dev); pm_runtime_put_autosuspend(i2c->dev); return ret; } static int sunxi_i2c_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num) { unsigned long timeout = 0; int ret = SUNXI_I2C_FAIL; unsigned long flags = 0; twi_soft_reset(i2c->base_addr); udelay(100); /* test the bus is free,already protect by the semaphore at DEV layer */ while (twi_query_irq_status(i2c->base_addr) != TWI_STAT_IDLE && twi_query_irq_status(i2c->base_addr) != TWI_STAT_BUS_ERR && twi_query_irq_status(i2c->base_addr) != TWI_STAT_ARBLOST_SLAR_ACK) { I2C_DBG("[i2c%d] bus is busy, status = %x\n", i2c->bus_num, twi_query_irq_status(i2c->base_addr)); if (twi_send_clk_9pulse(i2c->base_addr, i2c->bus_num) != SUNXI_I2C_OK) { ret = SUNXI_I2C_RETRY; goto out; } else break; } /* may conflict with xfer_complete */ spin_lock_irqsave(&i2c->lock, flags); i2c->msg = msgs; i2c->msg_num = num; i2c->msg_ptr = 0; i2c->msg_idx = 0; i2c->status = I2C_XFER_START; twi_enable_irq(i2c->base_addr); /* enable irq */ twi_disable_ack(i2c->base_addr); /* disabe ACK */ /* set the special function register,default:0. */ twi_set_efr(i2c->base_addr, 0); spin_unlock_irqrestore(&i2c->lock, flags); /* START signal, needn't clear int flag */ ret = twi_start(i2c->base_addr, i2c->bus_num); if (ret == SUNXI_I2C_FAIL) { twi_soft_reset(i2c->base_addr); twi_disable_irq(i2c->base_addr); /* disable irq */ i2c->status = I2C_XFER_IDLE; ret = SUNXI_I2C_RETRY; goto out; } i2c->status = I2C_XFER_RUNNING; /* sleep and wait,do the transfer at interrupt handler,timeout = 5*HZ */ timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, i2c->adap.timeout); /* return code,if(msg_idx == num) succeed */ ret = i2c->msg_idx; if (timeout == 0) { I2C_ERR("[i2c%d] xfer timeout (dev addr:0x%x)\n", i2c->bus_num, msgs->addr); spin_lock_irqsave(&i2c->lock, flags); i2c->msg = NULL; spin_unlock_irqrestore(&i2c->lock, flags); ret = -ETIME; } else if (ret != num) { I2C_ERR("[i2c%d] incomplete xfer (status: 0x%x, dev addr: 0x%x)\n", i2c->bus_num, ret, msgs->addr); ret = -ECOMM; } out: return ret; } static unsigned int sunxi_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C|I2C_FUNC_10BIT_ADDR|I2C_FUNC_SMBUS_EMUL; } static const struct i2c_algorithm sunxi_i2c_algorithm = { .master_xfer = sunxi_i2c_xfer, .functionality = sunxi_i2c_functionality, }; static int sunxi_i2c_clk_init(struct sunxi_i2c *i2c) { unsigned int apb_clk = 0; if (clk_prepare_enable(i2c->mclk)) { I2C_ERR("[i2c%d] enable apb_twi clock failed!\n", i2c->bus_num); return -1; } /* sunxi_periph_reset_deassert(i2c->mclk); */ /* enable twi bus */ twi_enable_bus(i2c->base_addr); /* set twi module clock */ apb_clk = clk_get_rate(i2c->mclk); if (apb_clk == 0) { I2C_ERR("[i2c%d] get i2c source clock frequency failed!\n", i2c->bus_num); return -1; } #ifdef CONFIG_EVB_PLATFORM twi_set_clock(apb_clk, i2c->bus_freq, i2c->base_addr); #else twi_set_clock(24000000, i2c->bus_freq, i2c->base_addr); #endif return 0; } static int sunxi_i2c_clk_exit(struct sunxi_i2c *i2c) { /* disable twi bus */ twi_disable_bus(i2c->base_addr); /* disable clk */ if (!IS_ERR_OR_NULL(i2c->mclk)) clk_disable_unprepare(i2c->mclk); else { clk_disable_unprepare(i2c->mclk); I2C_ERR("[i2c%d] i2c mclk handle is invalid, just return!\n", i2c->bus_num); return -1; } return 0; } static int sunxi_i2c_hw_init(struct sunxi_i2c *i2c, struct sunxi_i2c_platform_data *pdata) { int ret = 0; ret = twi_regulator_request(pdata); if (ret < 0) { I2C_ERR("[i2c%d] request regulator failed!\n", i2c->bus_num); return -1; } ret = twi_request_gpio(i2c); if (ret < 0) { I2C_ERR("[i2c%d] request i2c gpio failed!\n", i2c->bus_num); return -1; } if (sunxi_i2c_clk_init(i2c)) { I2C_ERR("[i2c%d] init i2c clock failed!\n", i2c->bus_num); return -1; } twi_soft_reset(i2c->base_addr); return ret; } static void sunxi_i2c_hw_exit(struct sunxi_i2c *i2c, struct sunxi_i2c_platform_data *pdata) { if (sunxi_i2c_clk_exit(i2c)) { I2C_ERR("[i2c%d] exit i2c clock failed!\n", i2c->bus_num); return; } twi_release_gpio(i2c); twi_regulator_disable(pdata); twi_regulator_release(pdata); } static void sunxi_i2c_release(struct device *dev) { I2C_ENTER(); } static ssize_t sunxi_i2c_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct sunxi_i2c_platform_data *pdata = dev->platform_data; return snprintf(buf, PAGE_SIZE, "pdev->id = %d\n" "pdev->name = %s\n" "pdev->num_resources = %u\n" "pdev->resource.mem = [%pa, %pa]\n" "pdev->resource.irq = %pa\n" "pdev->dev.platform_data.bus_num = %d\n" "pdev->dev.platform_data.freqency = %d\n" "pdev->dev.platform_data.regulator= 0x%p\n" "pdev->dev.platform_data.regulator_id = %s\n", pdev->id, pdev->name, pdev->num_resources, &pdev->resource[0].start, &pdev->resource[0].end, &pdev->resource[1].start, pdata->bus_num, pdata->frequency, pdata->regulator, pdata->regulator_id); } static struct device_attribute sunxi_i2c_info_attr = __ATTR(info, S_IRUGO, sunxi_i2c_info_show, NULL); static ssize_t sunxi_i2c_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sunxi_i2c *i2c = dev_get_drvdata(dev); static char const *i2c_status[] = {"Unknown", "Idle", "Start", "Unknown", "Running"}; if (i2c == NULL) return snprintf(buf, PAGE_SIZE, "%s\n", "sunxi_i2c is NULL!"); return snprintf(buf, PAGE_SIZE, "i2c->bus_num = %d\n" "i2c->status = [%d] %s\n" "i2c->suspended = %d\n" "i2c->msg_num = %d, ->msg_idx = %d, ->msg_ptr = %d\n" "i2c->bus_freq = %d\n" "i2c->irq = %d\n" "i2c->debug_state = %d\n" "i2c->base_addr = 0x%p, the TWI control register:\n" "[ADDR] 0x%02x = 0x%08x, [XADDR] 0x%02x = 0x%08x\n" "[DATA] 0x%02x = 0x%08x, [CNTR] 0x%02x = 0x%08x\n" "[STAT] 0x%02x = 0x%08x, [CCR] 0x%02x = 0x%08x\n" "[SRST] 0x%02x = 0x%08x, [EFR] 0x%02x = 0x%08x\n" "[LCR] 0x%02x = 0x%08x\n", i2c->bus_num, i2c->status, i2c_status[i2c->status], i2c->suspended, i2c->msg_num, i2c->msg_idx, i2c->msg_ptr, i2c->bus_freq, i2c->irq, i2c->debug_state, i2c->base_addr, TWI_ADDR_REG, readl(i2c->base_addr + TWI_ADDR_REG), TWI_XADDR_REG, readl(i2c->base_addr + TWI_XADDR_REG), TWI_DATA_REG, readl(i2c->base_addr + TWI_DATA_REG), TWI_CTL_REG, readl(i2c->base_addr + TWI_CTL_REG), TWI_STAT_REG, readl(i2c->base_addr + TWI_STAT_REG), TWI_CLK_REG, readl(i2c->base_addr + TWI_CLK_REG), TWI_SRST_REG, readl(i2c->base_addr + TWI_SRST_REG), TWI_EFR_REG, readl(i2c->base_addr + TWI_EFR_REG), TWI_LCR_REG, readl(i2c->base_addr + TWI_LCR_REG)); } static struct device_attribute sunxi_i2c_status_attr = __ATTR(status, S_IRUGO, sunxi_i2c_status_show, NULL); static void sunxi_i2c_sysfs(struct platform_device *_pdev) { device_create_file(&_pdev->dev, &sunxi_i2c_info_attr); device_create_file(&_pdev->dev, &sunxi_i2c_status_attr); } static int sunxi_i2c_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct sunxi_i2c *i2c = NULL; struct resource *mem_res = NULL; struct sunxi_i2c_platform_data *pdata = NULL; int ret, irq; unsigned long int_flag = 0; const char *str_vcc_twi; if (np == NULL) { I2C_ERR("I2C failed to get of node\n"); return -ENODEV; } i2c = kzalloc(sizeof(struct sunxi_i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; pdata = kzalloc(sizeof(struct sunxi_i2c_platform_data), GFP_KERNEL); if (pdata == NULL) { kfree(i2c); return -ENOMEM; } i2c->dev = &pdev->dev; pdev->dev.platform_data = pdata; pdev->dev.driver_data = i2c; pdev->id = of_alias_get_id(np, "twi"); if (pdev->id < 0) { I2C_ERR("I2C failed to get alias id\n"); ret = -EINVAL; goto emem; } pdata->bus_num = pdev->id; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem_res == NULL) { I2C_ERR("[I2C%d] failed to get MEM res\n", pdev->id); ret = -ENXIO; goto emem; } if (!request_mem_region(mem_res->start, resource_size(mem_res), mem_res->name)) { I2C_ERR("[I2C%d] failed to request mem region\n", pdev->id); ret = -EINVAL; goto emem; } i2c->base_addr = ioremap(mem_res->start, resource_size(mem_res)); if (!i2c->base_addr) { ret = -EIO; goto eiomap; } irq = platform_get_irq(pdev, 0); if (irq < 0) { I2C_ERR("[I2C%d] failed to get irq\n", pdev->id); ret = -EINVAL; goto eiomap; } ret = of_property_read_u32(np, "clock-frequency", &pdata->frequency); if (ret) { I2C_ERR("[I2C%d] failed to get clock frequency\n", pdev->id); ret = -EINVAL; goto eiomap; } ret = of_property_read_string(np, "twi_regulator", &str_vcc_twi); if (ret) I2C_ERR("[I2C%d] failed to get regulator id\n", pdev->id); else { I2C_DBG("[I2C%d] twi_regulator: %s\n", pdev->id, str_vcc_twi); strcpy(pdata->regulator_id, str_vcc_twi); } pdev->dev.release = sunxi_i2c_release; i2c->adap.owner = THIS_MODULE; i2c->adap.nr = pdata->bus_num; i2c->adap.retries = 3; i2c->adap.timeout = 5*HZ; i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->bus_freq = pdata->frequency; i2c->irq = irq; i2c->bus_num = pdata->bus_num; i2c->status = I2C_XFER_IDLE; i2c->suspended = 0; snprintf(i2c->adap.name, sizeof(i2c->adap.name), SUNXI_TWI_DEV_NAME"%u", i2c->adap.nr); pdev->dev.init_name = i2c->adap.name; spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait); i2c->mclk = of_clk_get(np, 0); if (IS_ERR_OR_NULL(i2c->mclk)) { I2C_ERR("[i2c%d] request TWI clock failed\n", i2c->bus_num); ret = -EIO; goto eremap; } i2c->adap.algo = &sunxi_i2c_algorithm; #ifndef CONFIG_SUNXI_ARISC /* SUNXI_ARISC will only use twi0, enable gic interrupt when suspend */ if (i2c->adap.nr == 0) int_flag |= IRQF_NO_SUSPEND; #endif ret = request_irq(irq, sunxi_i2c_handler, int_flag, i2c->adap.name, i2c); if (ret) { I2C_ERR("[i2c%d] requeset irq failed!\n", i2c->bus_num); goto ereqirq; } i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.of_node = pdev->dev.of_node; twi_used_mask |= SUNXI_TWI_CHAN_MASK(pdev->id); if (sunxi_i2c_hw_init(i2c, pdata)) { ret = -EIO; goto ehwinit; } pm_runtime_enable(i2c->dev); pm_runtime_set_autosuspend_delay(i2c->dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(i2c->dev); pm_runtime_set_active(i2c->dev); ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { I2C_ERR("[i2c%d] failed to add adapter\n", i2c->bus_num); pm_runtime_set_suspended(i2c->dev); pm_runtime_disable(i2c->dev); goto eadapt; } platform_set_drvdata(pdev, i2c); sunxi_i2c_sysfs(pdev); I2C_DBG("I2C: %s: sunxi I2C adapter\n", dev_name(&i2c->adap.dev)); I2C_DBG("TWI_CTL 0x%p: 0x%08x\n", i2c->base_addr + 0x0c, readl(i2c->base_addr + 0x0c)); I2C_DBG("TWI_STAT 0x%p: 0x%08x\n", i2c->base_addr + 0x10, readl(i2c->base_addr + 0x10)); I2C_DBG("TWI_CLK 0x%p: 0x%08x\n", i2c->base_addr + 0x14, readl(i2c->base_addr + 0x14)); I2C_DBG("TWI_SRST 0x%p: 0x%08x\n", i2c->base_addr + 0x18, readl(i2c->base_addr + 0x18)); I2C_DBG("TWI_EFR 0x%p: 0x%08x\n", i2c->base_addr + 0x1c, readl(i2c->base_addr + 0x1c)); return 0; eadapt: clk_disable_unprepare(i2c->mclk); ehwinit: free_irq(irq, i2c); ereqirq: iounmap(i2c->base_addr); eremap: if (!IS_ERR_OR_NULL(i2c->mclk)) { clk_put(i2c->mclk); i2c->mclk = NULL; } eiomap: release_mem_region(mem_res->start, resource_size(mem_res)); emem: kfree(pdata); kfree(i2c); return ret; } static int sunxi_i2c_remove(struct platform_device *pdev) { struct sunxi_i2c *i2c = platform_get_drvdata(pdev); I2C_DBG("[i2c.%d] remove ...\n", i2c->bus_num); platform_set_drvdata(pdev, NULL); i2c_del_adapter(&i2c->adap); free_irq(i2c->irq, i2c); pm_runtime_set_suspended(i2c->dev); pm_runtime_disable(i2c->dev); /* disable clock and release gpio */ sunxi_i2c_hw_exit(i2c, pdev->dev.platform_data); if (!IS_ERR_OR_NULL(i2c->mclk)) { clk_put(i2c->mclk); i2c->mclk = NULL; } else { I2C_ERR("i2c mclk handle is invalid, just return!\n"); return -1; } iounmap(i2c->base_addr); kfree(i2c); kfree(pdev->dev.platform_data); I2C_EXIT(); return 0; } #ifdef CONFIG_PM static int sunxi_i2c_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_i2c *i2c = platform_get_drvdata(pdev); #ifndef CONFIG_SUNXI_ARISC /* SUNXI_ARISC will only use twi0 */ if (i2c->adap.nr == 0) return 0; #endif if (sunxi_i2c_clk_exit(i2c)) { I2C_ERR("[i2c%d] suspend failed..\n", i2c->bus_num); return -1; } twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_SLEEP, i2c->bus_num); twi_regulator_disable(dev->platform_data); I2C_DBG("[i2c%d] suspend okay..\n", i2c->bus_num); return 0; } static int sunxi_i2c_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_i2c *i2c = platform_get_drvdata(pdev); #ifndef CONFIG_SUNXI_ARISC /* SUNXI_ARISC will only use twi0 */ if (i2c->adap.nr == 0) return 0; #endif twi_regulator_enable(dev->platform_data); twi_select_gpio_state(i2c->pctrl, PINCTRL_STATE_DEFAULT, i2c->bus_num); if (sunxi_i2c_clk_init(i2c)) { I2C_ERR("[i2c%d] resume failed..\n", i2c->bus_num); return -1; } twi_soft_reset(i2c->base_addr); I2C_DBG("[i2c%d] resume okay..\n", i2c->bus_num); return 0; } static int sunxi_i2c_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_i2c *i2c = platform_get_drvdata(pdev); if (!pm_runtime_status_suspended(dev)) { sunxi_i2c_runtime_suspend(dev); I2C_DBG("[i2c%d] system suspend ok..\n", i2c->bus_num); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_enable(dev); } return 0; } static int sunxi_i2c_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_i2c *i2c = platform_get_drvdata(pdev); if (pm_runtime_active(dev)) { sunxi_i2c_runtime_resume(dev); I2C_DBG("[i2c%d] system resume ok..\n", i2c->bus_num); pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); } return 0; } static const struct dev_pm_ops sunxi_i2c_dev_pm_ops = { .suspend_noirq = sunxi_i2c_suspend_noirq, .resume_noirq = sunxi_i2c_resume_noirq, .runtime_suspend = sunxi_i2c_runtime_suspend, .runtime_resume = sunxi_i2c_runtime_resume, }; #define SUNXI_I2C_DEV_PM_OPS (&sunxi_i2c_dev_pm_ops) #else #define SUNXI_I2C_DEV_PM_OPS NULL #endif static const struct of_device_id sunxi_i2c_match[] = { { .compatible = "allwinner,sun8i-twi", }, { .compatible = "allwinner,sun50i-twi", }, {}, }; MODULE_DEVICE_TABLE(of, sunxi_i2c_match); static struct platform_driver sunxi_i2c_driver = { .probe = sunxi_i2c_probe, .remove = sunxi_i2c_remove, .driver = { .name = SUNXI_TWI_DEV_NAME, .owner = THIS_MODULE, .pm = SUNXI_I2C_DEV_PM_OPS, .of_match_table = sunxi_i2c_match, }, }; static int __init sunxi_i2c_adap_init(void) { I2C_DBG("Sunxi I2C adapt init\n"); return platform_driver_register(&sunxi_i2c_driver); } static void __exit sunxi_i2c_adap_exit(void) { I2C_ENTER(); platform_driver_unregister(&sunxi_i2c_driver); } fs_initcall(sunxi_i2c_adap_init); module_exit(sunxi_i2c_adap_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:i2c-sunxi"); MODULE_DESCRIPTION("SUNXI I2C Bus Driver"); MODULE_AUTHOR("pannan");