/* * 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 #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_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 */ #define DMA_THRESHOLD 32 #define MAX_FIFO 32 #define DMA_TIMEOUT 2000 static u32 debug_mask; #define dprintk(level_mask, fmt, arg...) \ do { \ if (unlikely(debug_mask & level_mask)) \ pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \ } while (0) static int twi_used_mask; /* I2C transfer status */ enum { I2C_XFER_IDLE = 0x1, I2C_XFER_START = 0x2, I2C_XFER_RUNNING = 0x4, }; struct sunxi_i2c_dma { struct dma_chan *chan; dma_addr_t dma_buf; unsigned int dma_len; enum dma_transfer_direction dma_transfer_dir; enum dma_data_direction dma_data_dir; }; struct sunxi_i2c { struct platform_device *pdev; int bus_num; unsigned int status; /* start, running, idle */ struct i2c_adapter adap; struct device *dev; spinlock_t lock; /* syn */ wait_queue_head_t wait; struct completion cmd_complete; 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; bool twi_drv_used; unsigned char result; struct sunxi_i2c_dma *dma_tx; struct sunxi_i2c_dma *dma_rx; struct sunxi_i2c_dma *dma_using; }; static void dump_reg(void __iomem *base_addr, u32 offset, u32 len) { u32 i, j; for (i = 0; i < len; i += 0x10) { pr_warn("0x%04x: ", offset + i); for (j = 0; j < 0x10; j += 0x04) pr_warn("0x%08x ", readl(base_addr + offset + i + j)); pr_warn("\n"); } } /* 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(void __iomem *base_addr, unsigned int reg, unsigned int mask) { unsigned int reg_val = readl(base_addr + reg); reg_val &= ~mask; writel(reg_val, base_addr + reg); dprintk(DEBUG_INFO, "[i2c0] reg: 0x%x value: 0x%x\n", reg, readl(base_addr + reg)); } static inline void twi_enable(void __iomem *base_addr, unsigned int reg, unsigned int mask) { unsigned int reg_val = readl(base_addr + reg); reg_val |= mask; writel(reg_val, base_addr + reg); dprintk(DEBUG_INFO, "[i2c0] reg: 0x%x value: 0x%x\n", reg, readl(base_addr + 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 void twi_clk_write_reg(struct sunxi_i2c *i2c, unsigned int reg_clk, unsigned int clk_m, unsigned int clk_n, unsigned int mask_clk_m, unsigned int mask_clk_n) { unsigned int reg_val = readl(i2c->base_addr + reg_clk); dprintk(DEBUG_INFO, "[i2c%d] reg_clk = 0x%x, clk_m = %u, clk_n = %u," "mask_clk_m = %u, mask_clk_n = %u\n", i2c->bus_num, reg_clk, clk_m, clk_n, mask_clk_m, mask_clk_n); if (reg_clk == TWI_DRIVER_BUSC) { reg_val &= ~(mask_clk_m | mask_clk_n); reg_val |= ((clk_m | (clk_n << 3)) << 8); writel(reg_val, i2c->base_addr + reg_clk); dprintk(DEBUG_INFO, "[i2c%d] reg: 0x%x value: 0x%x\n", i2c->bus_num, reg_clk, readl(i2c->base_addr + reg_clk)); } else { reg_val &= ~(mask_clk_m | mask_clk_n); reg_val |= ((clk_m << 3) | clk_n); writel(reg_val, i2c->base_addr + reg_clk); dprintk(DEBUG_INFO, "[i2c%d] reg: 0x%x value: 0x%x\n", i2c->bus_num, reg_clk, readl(i2c->base_addr + reg_clk)); } } /* * 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(struct sunxi_i2c *i2c, unsigned int reg_clk, unsigned int clk_in, unsigned int sclk_req, unsigned int mask_clk_m, unsigned int mask_clk_n) { 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(i2c, reg_clk, clk_m, clk_n, mask_clk_m, mask_clk_n); return 0; } /* soft reset twi */ static inline void twi_soft_reset(void __iomem *base_addr, unsigned int reg, unsigned int mask) { unsigned int reg_val = readl(base_addr + reg); reg_val |= mask; writel(reg_val, base_addr + 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) { dprintk(DEBUG_INFO, "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%0x) 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%0x) 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 unsigned int twi_drv_query_irq_status(void __iomem *base_addr) { unsigned int reg_val = readl(base_addr + TWI_DRIVER_INTC); return (reg_val & TWI_DRV_STAT_MASK); } static void twi_drv_clear_irq_flag(u32 pending_bit, void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_INTC); pending_bit &= TWI_DRV_STAT_MASK; reg_val |= pending_bit; writel(reg_val, base_addr + TWI_DRIVER_INTC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC, readl(base_addr + TWI_DRIVER_INTC)); } static void i2c_drv_clear_pending(void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_INTC); reg_val |= TWI_DRV_STAT_MASK; writel(reg_val, base_addr + TWI_DRIVER_INTC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC, readl(base_addr + TWI_DRIVER_INTC)); } /* start i2c transfer */ static void i2c_start_xfer(void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_CTRL); reg_val |= START_TRAN; writel(reg_val, base_addr + TWI_DRIVER_CTRL); } /* * send DMA RX Req when the data byte number in RECV_FIFO reaches RX_TRIG * or Read Packet Tansmission completed with RECV_FIFO not empty */ static void i2c_set_rx_trig_level(u32 val, void __iomem *base_addr) { u32 mask = TRIG_MASK; u32 reg_val = readl(base_addr + TWI_DRIVER_DMAC); val = (val & mask) << 16; reg_val &= ~(mask << 16); reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_DMAC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_DMAC, readl(base_addr + TWI_DRIVER_DMAC)); } /* bytes be send as slave device reg address */ static void i2c_set_packet_addr_byte(u32 val, void __iomem *base_addr) { u32 mask = ADDR_BYTE; u32 reg_val = readl(base_addr + TWI_DRIVER_FMT); reg_val &= ~mask; val = (val << 16) & mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_FMT); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FMT, readl(base_addr + TWI_DRIVER_FMT)); } /* bytes be send/received as data */ static void i2c_set_packet_data_byte(u32 val, void __iomem *base_addr) { u32 mask = DATA_BYTE; u32 reg_val = readl(base_addr + TWI_DRIVER_FMT); reg_val &= ~mask; val &= mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_FMT); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FMT, readl(base_addr + TWI_DRIVER_FMT)); } /* interval between each packet in 32*Fscl cycles */ static void i2c_set_packet_interval(u32 val, void __iomem *base_addr) { u32 mask = INTERVAL_MASK; u32 reg_val = readl(base_addr + TWI_DRIVER_CFG); reg_val &= ~mask; val <<= 16; val &= INTERVAL_MASK; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_CFG); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CFG, readl(base_addr + TWI_DRIVER_CFG)); } /* FIFO data be transmitted as PACKET_CNT packets in current format */ static void i2c_set_packet_cnt(u32 val, void __iomem *base_addr) { u32 mask = PACKET_MASK; u32 reg_val = readl(base_addr + TWI_DRIVER_CFG); reg_val &= ~mask; val &= mask; reg_val |= val; writel(reg_val, base_addr + TWI_DRIVER_CFG); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CFG, readl(base_addr + TWI_DRIVER_CFG)); } /* do not send slave_id +W */ static void i2c_enable_read_tran_mode(void __iomem *base_addr) { u32 mask = READ_TRAN; u32 reg_val = readl(base_addr + TWI_DRIVER_CTRL); reg_val |= mask; writel(reg_val, base_addr + TWI_DRIVER_CTRL); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CTRL, readl(base_addr + TWI_DRIVER_CTRL)); } /* send slave_id + W */ static void i2c_disable_read_tran_mode(void __iomem *base_addr) { u32 mask = READ_TRAN; u32 reg_val = readl(base_addr + TWI_DRIVER_CTRL); reg_val &= ~mask; writel(reg_val, base_addr + TWI_DRIVER_CTRL); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_CTRL, readl(base_addr + TWI_DRIVER_CTRL)); } static void i2c_drv_enable_tran_irq(u32 bitmap, void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_INTC); bitmap &= TWI_DRV_INT_MASK; reg_val |= bitmap; writel(reg_val, base_addr + TWI_DRIVER_INTC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC, readl(base_addr + TWI_DRIVER_INTC)); } static void i2c_drv_disable_tran_irq(u32 bitmap, void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_INTC); bitmap &= TWI_DRV_INT_MASK; reg_val &= ~bitmap; writel(reg_val, base_addr + TWI_DRIVER_INTC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_INTC, readl(base_addr + TWI_DRIVER_INTC)); } static void i2c_drv_enable_dma_irq(u32 bitmap, void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_DMAC); bitmap &= I2C_DRQEN_MASK; reg_val |= bitmap; writel(reg_val, base_addr + TWI_DRIVER_DMAC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_DMAC, readl(base_addr + TWI_DRIVER_DMAC)); } static void i2c_drv_disable_dma_irq(u32 bitmap, void __iomem *base_addr) { u32 reg_val = readl(base_addr + TWI_DRIVER_DMAC); bitmap &= I2C_DRQEN_MASK; reg_val &= ~bitmap; writel(reg_val, base_addr + TWI_DRIVER_DMAC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_DMAC, readl(base_addr + TWI_DRIVER_DMAC)); } static void sunxi_i2c_drv_slave_addr(struct sunxi_i2c *i2c, struct i2c_msg *msgs) { unsigned int val = 0, cmd = 0; /* read, default value is write */ if (msgs->flags & I2C_M_RD) cmd = SLV_CMD; if (msgs->flags & I2C_M_TEN) { /* SLV_ID | CMD | SLV_ID_X */ val = ((0x78 | ((msgs->addr >> 8) & 0x03)) << 9) | cmd | (msgs->addr & 0xff); } else { val = ((msgs->addr & 0x7f) << 9) | cmd; } writel(val, i2c->base_addr + TWI_DRIVER_SLV); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_SLV, readl(i2c->base_addr + TWI_DRIVER_SLV)); } /* the number of data in SEND_FIFO */ static int i2c_query_txfifo(void __iomem *base_addr) { unsigned int reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC) & SEND_FIFO_CONT; return reg_val; } /* the number of data in RECV_FIFO */ static int i2c_query_rxfifo(void __iomem *base_addr) { unsigned int reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC) & RECV_FIFO_CONT; reg_val >>= 16; return reg_val; } static void i2c_clear_txfifo(void __iomem *base_addr) { unsigned int reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC); reg_val |= SEND_FIFO_CLEAR; writel(reg_val, base_addr + TWI_DRIVER_FIFOC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FIFOC, readl(base_addr + TWI_DRIVER_FIFOC)); } static void i2c_clear_rxfifo(void __iomem *base_addr) { unsigned int reg_val; reg_val = readl(base_addr + TWI_DRIVER_FIFOC); reg_val |= RECV_FIFO_CLEAR; writel(reg_val, base_addr + TWI_DRIVER_FIFOC); dprintk(DEBUG_INFO, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_FIFOC, readl(base_addr + TWI_DRIVER_FIFOC)); } static int i2c_sunxi_send_msgs(struct sunxi_i2c *i2c, struct i2c_msg *msgs) { unsigned int i; unsigned char time = 0xff; dprintk(DEBUG_INFO, "[i2c%d] msgs->len = %d\n", i2c->bus_num, msgs->len); for (i = 0; i < msgs->len; i++) { while ((i2c_query_txfifo(i2c->base_addr) >= MAX_FIFO) && time--) ; dprintk(DEBUG_INFO, "[i2c%d] time = %d\n", i2c->bus_num, time); if (time) { writeb(msgs->buf[i], i2c->base_addr + TWI_DRIVER_SENDF); dprintk(DEBUG_INFO, "[i2c%d] writeb: Byte[0] = 0x%x," "fifo len = %d\n", i2c->bus_num, msgs->buf[i], i2c_query_txfifo(i2c->base_addr)); } else { dprintk(DEBUG_INFO, "[i2c%d] SEND FIFO overflow. timeout\n", i2c->bus_num); return -EINVAL; } } return 0; } static unsigned int i2c_sunxi_recv_msgs(struct sunxi_i2c *i2c, struct i2c_msg *msgs) { unsigned int i; unsigned char time = 0xff; dprintk(DEBUG_INFO, "[i2c%d] recving data\n", i2c->bus_num); for (i = 0; i < msgs->len; i++) { while (!i2c_query_rxfifo(i2c->base_addr) && time--) ; if (time) { msgs->buf[i] = readb(i2c->base_addr + TWI_DRIVER_RECVF); dprintk(DEBUG_INFO, "[i2c%d] readb: Byte[%d] = 0x%x\n", i2c->bus_num, i, msgs->buf[i]); } else return 0; } return msgs->len; } static int sunxi_i2c_drv_core_process(struct sunxi_i2c *i2c) { void __iomem *base_addr = i2c->base_addr; unsigned long flags = 0; unsigned int status, code; unsigned int fifolen; spin_lock_irqsave(&i2c->lock, flags); status = twi_drv_query_irq_status(base_addr); dprintk(DEBUG_INFO, "[i2c%d] irq status = 0x%x\n", i2c->bus_num, status); twi_drv_clear_irq_flag(status, base_addr); if ((status & RX_REQ_PD) && (i2c->msg->len < DMA_THRESHOLD)) { dprintk(DEBUG_INFO, "[i2c%d] i2c->msg->len = %d\n", i2c->bus_num, i2c->msg->len); fifolen = (readl(base_addr + TWI_DRIVER_FIFOC) & RECV_FIFO_CONT) >> 16; dprintk(DEBUG_INFO, "[i2c%d] fifo len = %d\n", i2c->bus_num, fifolen); if (i2c_sunxi_recv_msgs(i2c, i2c->msg)) i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT, i2c->base_addr); fifolen = (readl(base_addr + TWI_DRIVER_FIFOC) & RECV_FIFO_CONT) >> 16; dprintk(DEBUG_INFO, "[i2c%d] fifo len = %d\n", i2c->bus_num, fifolen); } if (status & TRAN_COM_PD) { dprintk(DEBUG_INFO, "[i2c%d] packet transmission completed\n", i2c->bus_num); dprintk(DEBUG_INFO, "[i2c%d] fifo len = %d\n", i2c->bus_num, i2c_query_txfifo(i2c->base_addr)); i2c->result = RESULT_COMPLETE; wake_up(&i2c->wait); spin_unlock_irqrestore(&i2c->lock, flags); } if (status & TRAN_ERR_PD) { dprintk(DEBUG_INFO, "[i2c%d] packet transmission failed\n", i2c->bus_num); i2c->result = RESULT_ERR; code = readl(base_addr + TWI_DRIVER_CTRL); code = (code & TWI_DRV_STA) >> 16; switch (code) { case 0x00: I2C_ERR("[i2c%d] bus error\n", i2c->bus_num); break; case 0x01: I2C_ERR("[i2c%d] Timeout when sending 9th SCL clk\n", i2c->bus_num); break; case 0x20: I2C_ERR("[i2c%d] Address + Write bit transmitted," "ACK not received\n", i2c->bus_num); break; case 0x30: I2C_ERR("[i2c%d] Data byte transmitted in master mode," "ACK not received\n", i2c->bus_num); break; case 0x38: I2C_ERR("[i2c%d] Arbitration lost in address" "or data byte\n", i2c->bus_num); break; case 0x48: I2C_ERR("[i2c%d] Address + Read bit transmitted," "ACK not received\\n", i2c->bus_num); break; case 0x58: I2C_ERR("[i2c%d] Data byte received in master mode," "ACK not received\n", i2c->bus_num); break; default: I2C_ERR("[i2c%d] unknown error\n", i2c->bus_num); break; } i2c->msg_idx = code; wake_up(&i2c->wait); spin_unlock_irqrestore(&i2c->lock, flags); return code; } return 0; } /* Functions for DMA support */ static void sunxi_i2c_dma_request(struct sunxi_i2c *i2c, dma_addr_t phy_addr) { struct sunxi_i2c_dma *dma_tx, *dma_rx; struct dma_slave_config dma_sconfig; struct device *dev = &i2c->adap.dev; dma_cap_mask_t mask_tx, mask_rx; int ret; dma_tx = devm_kzalloc(dev, sizeof(*dma_tx), GFP_KERNEL); dma_rx = devm_kzalloc(dev, sizeof(*dma_rx), GFP_KERNEL); if (!dma_tx || !dma_rx) return; dma_cap_zero(mask_tx); dma_cap_set(DMA_SLAVE, mask_tx); dma_tx->chan = dma_request_channel(mask_tx, NULL, NULL); if (!dma_tx->chan) { I2C_ERR("[i2c%d] can't request DMA tx channel\n", i2c->bus_num); goto fail_al; } dma_sconfig.dst_addr = phy_addr + TWI_DRIVER_SENDF; dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.src_maxburst = 16; dma_sconfig.dst_maxburst = 16; #if defined(CONFIG_ARCH_SUN8IW18) dma_sconfig.slave_id = sunxi_slave_id(SUNXI_TWI_DRQ_TX(i2c->bus_num), DRQSRC_SDRAM); #endif dma_sconfig.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(dma_tx->chan, &dma_sconfig); if (ret < 0) { I2C_ERR("[i2c%d] can't configure tx channel\n", i2c->bus_num); goto fail_tx; } i2c->dma_tx = dma_tx; dma_cap_zero(mask_rx); dma_cap_set(DMA_SLAVE, mask_rx); dma_rx->chan = dma_request_channel(mask_rx, NULL, NULL); if (!dma_rx->chan) { I2C_ERR("[i2c%d] can't request DMA rx channel\n", i2c->bus_num); goto fail_tx; } dma_sconfig.src_addr = phy_addr + TWI_DRIVER_RECVF; dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.src_maxburst = 16; dma_sconfig.dst_maxburst = 16; #if defined(CONFIG_ARCH_SUN8IW18) dma_sconfig.slave_id = sunxi_slave_id(DRQDST_SDRAM, SUNXI_TWI_DRQ_RX(i2c->bus_num)); #endif dma_sconfig.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(dma_rx->chan, &dma_sconfig); if (ret < 0) { I2C_ERR("[i2c%d] can't configure rx channel\n", i2c->bus_num); goto fail_rx; } i2c->dma_rx = dma_rx; init_completion(&i2c->cmd_complete); dprintk(DEBUG_INIT, "[i2c%d] using %s (tx) and %s (rx)" "for DMA transfers\n", i2c->bus_num, dma_chan_name(i2c->dma_tx->chan), dma_chan_name(i2c->dma_rx->chan)); return; fail_rx: dma_release_channel(i2c->dma_rx->chan); fail_tx: dma_release_channel(i2c->dma_tx->chan); fail_al: devm_kfree(dev, dma_tx); devm_kfree(dev, dma_rx); dprintk(DEBUG_INIT, "[i2c%d] can't use DMA, using PIO instead\n", i2c->bus_num); } static void sunxi_i2c_dma_callback(void *arg) { struct sunxi_i2c *i2c = (struct sunxi_i2c *)arg; if (i2c->dma_using == i2c->dma_tx) dprintk(DEBUG_INFO, "[i2c%d] dma write data end\n", i2c->bus_num); else if (i2c->dma_using == i2c->dma_rx) dprintk(DEBUG_INFO, "[i2c%d] dma read data end\n", i2c->bus_num); dma_unmap_single(i2c->dma_using->chan->device->dev, i2c->dma_using->dma_buf, i2c->dma_using->dma_len, i2c->dma_using->dma_data_dir); complete(&i2c->cmd_complete); dprintk(DEBUG_INFO, "[i2c%d] callback complete\n", i2c->bus_num); } static void sunxi_i2c_dma_free(struct sunxi_i2c_dma *dma) { dma->dma_buf = 0; dma->dma_len = 0; dma_release_channel(dma->chan); dma->chan = NULL; } static int i2c_sunxi_drv_complete(struct sunxi_i2c *i2c) { unsigned long timeout = 0; timeout = wait_event_timeout(i2c->wait, i2c->result, 2*HZ); if (timeout == 0) { I2C_ERR("[i2c%d] twi driver xfer timeout (dev addr:0x%x)\n", i2c->bus_num, i2c->msg->addr); dump_reg(i2c->base_addr, 0x200, 0x40); i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT | TX_REQ_INT, i2c->base_addr); i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr); return -ETIME; } else if (i2c->result == RESULT_ERR) { I2C_ERR("[i2c%d] incomplete xfer" "(status: 0x%x, dev addr: 0x%x)\n", i2c->bus_num, i2c->msg_idx, i2c->msg->addr); dump_reg(i2c->base_addr, 0x200, 0x40); i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT | TX_REQ_INT, i2c->base_addr); i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr); return -ECOMM; } dprintk(DEBUG_INFO, "[i2c%d] xfer complete\n", i2c->bus_num); i2c->result = 0; return 0; } static int i2c_sunxi_dma_xfer(struct sunxi_i2c *i2c, unsigned char *buf) { unsigned long time_left; struct sunxi_i2c_dma *dma = i2c->dma_using; struct dma_async_tx_descriptor *dma_desc; struct device *chan_dev = dma->chan->device->dev; dma->dma_buf = dma_map_single(chan_dev, buf, dma->dma_len, dma->dma_data_dir); if (dma_mapping_error(chan_dev, dma->dma_buf)) { I2C_ERR("DMA mapping failed\n"); goto err_map; } dma_desc = dmaengine_prep_slave_single(dma->chan, dma->dma_buf, dma->dma_len, dma->dma_transfer_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!dma_desc) { I2C_ERR("Not able to get desc for DMA xfer\n"); goto err_desc; } dma_desc->callback = sunxi_i2c_dma_callback; dma_desc->callback_param = i2c; if (dma_submit_error(dmaengine_submit(dma_desc))) { I2C_ERR("[i2c%d] DMA submit failed\n", i2c->bus_num); goto err_submit; } reinit_completion(&i2c->cmd_complete); dma_async_issue_pending(dma->chan); dprintk(DEBUG_INFO, "[i2c%d] dma issue pending\n", i2c->bus_num); time_left = wait_for_completion_timeout( &i2c->cmd_complete, msecs_to_jiffies(DMA_TIMEOUT)); dprintk(DEBUG_INFO, "[i2c%d] time_left = %lu\n", i2c->bus_num, time_left); return 0; err_submit: err_desc: dma_unmap_single(chan_dev, dma->dma_buf, dma->dma_len, dma->dma_data_dir); err_map: return -EINVAL; } static int sunxi_i2c_drv_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs) { sunxi_i2c_drv_slave_addr(i2c, msgs); if (msgs->len == 1) { i2c_set_packet_addr_byte(0, i2c->base_addr); i2c_set_packet_data_byte(msgs->len, i2c->base_addr); } else { i2c_set_packet_addr_byte(1, i2c->base_addr); i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr); } i2c_set_packet_cnt(1, i2c->base_addr); i2c_drv_clear_pending(i2c->base_addr); i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr); i2c_start_xfer(i2c->base_addr); i2c_sunxi_send_msgs(i2c, msgs); return 0; } static int sunxi_i2c_drv_dma_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs) { int ret = 0; sunxi_i2c_drv_slave_addr(i2c, msgs); i2c_set_packet_addr_byte(1, i2c->base_addr); i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr); i2c_set_packet_cnt(1, i2c->base_addr); i2c_drv_clear_pending(i2c->base_addr); i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr); i2c_drv_enable_dma_irq(DMA_TX, i2c->base_addr); i2c_start_xfer(i2c->base_addr); i2c->dma_using = i2c->dma_tx; i2c->dma_using->dma_transfer_dir = DMA_MEM_TO_DEV; i2c->dma_using->dma_data_dir = DMA_TO_DEVICE; i2c->dma_using->dma_len = msgs->len; ret = i2c_sunxi_dma_xfer(i2c, msgs->buf); return ret; } static int sunxi_i2c_drv_mulpk_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num) { int i; sunxi_i2c_drv_slave_addr(i2c, msgs); i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr); i2c_set_packet_interval(0x30, i2c->base_addr); i2c_set_packet_cnt(num, i2c->base_addr); i2c_drv_clear_pending(i2c->base_addr); i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr); i2c_start_xfer(i2c->base_addr); for (i = 0; i < num; i++) i2c_sunxi_send_msgs(i2c, msgs + i); return 0; } static int sunxi_i2c_drv_dma_mulpk_write(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num) { int i, ret = 0; unsigned char *buf; unsigned int tlen = num * msgs->len; sunxi_i2c_drv_slave_addr(i2c, msgs); i2c_set_packet_data_byte(msgs->len - 1, i2c->base_addr); i2c_set_packet_interval(0x30, i2c->base_addr); i2c_set_packet_cnt(num, i2c->base_addr); buf = kzalloc(tlen, GFP_KERNEL); if (!buf) { I2C_ERR("[i2c%d] kzalloc failed\n", i2c->bus_num); return -ENOMEM; } for (i = 0; i < num; i++) { memcpy(buf, msgs->buf, msgs->len); msgs++; buf += msgs->len; } i2c_drv_clear_pending(i2c->base_addr); i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr); i2c_drv_enable_dma_irq(DMA_TX, i2c->base_addr); i2c_start_xfer(i2c->base_addr); i2c->dma_using = i2c->dma_tx; i2c->dma_using->dma_transfer_dir = DMA_MEM_TO_DEV; i2c->dma_using->dma_data_dir = DMA_TO_DEVICE; i2c->dma_using->dma_len = tlen; ret = i2c_sunxi_dma_xfer(i2c, buf); kfree(buf); return ret; } static int sunxi_i2c_drv_read(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num) { struct i2c_msg *wmsgs, *rmsgs; if (num == 1) { wmsgs = NULL; rmsgs = msgs; } else if (num == 2) { wmsgs = msgs; rmsgs = msgs + 1; } i2c->msg = rmsgs; sunxi_i2c_drv_slave_addr(i2c, rmsgs); i2c_set_packet_cnt(1, i2c->base_addr); i2c_set_packet_data_byte(rmsgs->len, i2c->base_addr); if (rmsgs->len > MAX_FIFO) i2c_set_rx_trig_level(MAX_FIFO, i2c->base_addr); else i2c_set_rx_trig_level(rmsgs->len, i2c->base_addr); if (i2c_query_rxfifo(i2c->base_addr)) i2c_clear_rxfifo(i2c->base_addr); i2c_drv_clear_pending(i2c->base_addr); i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT, i2c->base_addr); i2c_start_xfer(i2c->base_addr); if (wmsgs) i2c_sunxi_send_msgs(i2c, wmsgs); return 0; } static int sunxi_i2c_drv_dma_read(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num) { int ret = 0; struct i2c_msg *wmsgs, *rmsgs; if (num == 1) { wmsgs = NULL; rmsgs = msgs; } else if (num == 2) { wmsgs = msgs; rmsgs = msgs + 1; } i2c->msg = rmsgs; sunxi_i2c_drv_slave_addr(i2c, rmsgs); i2c_set_packet_data_byte(rmsgs->len, i2c->base_addr); i2c_set_packet_cnt(1, i2c->base_addr); i2c_set_rx_trig_level(MAX_FIFO/2, i2c->base_addr); if (i2c_query_rxfifo(i2c->base_addr)) i2c_clear_rxfifo(i2c->base_addr); i2c_drv_clear_pending(i2c->base_addr); i2c_drv_enable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT, i2c->base_addr); i2c_drv_enable_dma_irq(DMA_RX, i2c->base_addr); i2c_start_xfer(i2c->base_addr); if (wmsgs) i2c_sunxi_send_msgs(i2c, wmsgs); i2c->dma_using = i2c->dma_rx; i2c->dma_using->dma_transfer_dir = DMA_DEV_TO_MEM; i2c->dma_using->dma_data_dir = DMA_FROM_DEVICE; i2c->dma_using->dma_len = rmsgs->len; ret = i2c_sunxi_dma_xfer(i2c, rmsgs->buf); return ret; } /** * sunxi_i2c_drv_do_xfer - twi driver transmission control * @i2c: struct of sunxi_i2c * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. */ static int sunxi_i2c_drv_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num) { int ret; i2c_drv_clear_pending(i2c->base_addr); i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT | RX_REQ_INT | TX_REQ_INT, i2c->base_addr); i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr); if (i2c_query_txfifo(i2c->base_addr)) i2c_clear_txfifo(i2c->base_addr); if (num == 1) { if (msgs->flags & I2C_M_RD) { /* 1 msgs read */ i2c_enable_read_tran_mode(i2c->base_addr); i2c_set_packet_addr_byte(0, i2c->base_addr); if (i2c->dma_rx && (msgs->len >= DMA_THRESHOLD)) { dprintk(DEBUG_INFO, "[i2c%d] master dma read\n", i2c->bus_num); ret = sunxi_i2c_drv_dma_read(i2c, msgs, num); } else { dprintk(DEBUG_INFO, "[i2c%d] master cpu read\n", i2c->bus_num); ret = sunxi_i2c_drv_read(i2c, msgs, num); } } else { /* 1 msgs write */ i2c_disable_read_tran_mode(i2c->base_addr); if (i2c->dma_tx && (msgs->len >= DMA_THRESHOLD)) { dprintk(DEBUG_INFO, "[i2c%d] master dma write\n", i2c->bus_num); ret = sunxi_i2c_drv_dma_write(i2c, msgs); } else { dprintk(DEBUG_INFO, "[i2c%d] master cpu write\n", i2c->bus_num); ret = sunxi_i2c_drv_write(i2c, msgs); } } } else if ((num == 2) && ((msgs + 1)->flags & I2C_M_RD)) { /* 2 msgs read */ i2c_disable_read_tran_mode(i2c->base_addr); i2c_set_packet_addr_byte(1, i2c->base_addr); if (i2c->dma_rx && ((msgs + 1)->len >= DMA_THRESHOLD)) { dprintk(DEBUG_INFO, "[i2c%d] master dma read\n", i2c->bus_num); ret = sunxi_i2c_drv_dma_read(i2c, msgs, num); } else { dprintk(DEBUG_INFO, "[i2c%d] master cpu read\n", i2c->bus_num); ret = sunxi_i2c_drv_read(i2c, msgs, num); } } else { /* multiple write with the same format packet */ i2c_disable_read_tran_mode(i2c->base_addr); i2c_set_packet_addr_byte(1, i2c->base_addr); if (i2c->dma_tx && ((num * msgs->len) >= DMA_THRESHOLD)) { dprintk(DEBUG_INFO, "[i2c%d] master dma multiple packet write\n", i2c->bus_num); ret = sunxi_i2c_drv_dma_mulpk_write(i2c, msgs, num); } else { dprintk(DEBUG_INFO, "[i2c%d] master cpu multiple packet write\n", i2c->bus_num); ret = sunxi_i2c_drv_mulpk_write(i2c, msgs, num); } } if (ret) return ret; ret = i2c_sunxi_drv_complete(i2c); if (ret) return ret; else return num; } /* **************************************************************************** * * 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) dprintk(DEBUG_INFO, "[i2c%d] first part of 10bits = 0x%x\n", i2c->bus_num, addr); dprintk(DEBUG_INFO, "[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) dprintk(DEBUG_INFO, "[i2c%d] first part of 10bits = 0x%x\n", i2c->bus_num, addr); dprintk(DEBUG_INFO, "[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) dprintk(DEBUG_INFO, "[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) dprintk(DEBUG_INFO, "[i2c%d][slave address = (0x%x)," "state = (0x%x)]\n", i2c->bus_num, i2c->msg->addr, state); } #endif dprintk(DEBUG_INFO, "[i2c%d][slave address = (0x%x), state = (0x%x)]\n", i2c->bus_num, i2c->msg->addr, state); 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) { I2C_ERR("[i2c%d] twi_regulator: %s\n", i2c->bus_num, ((struct sunxi_i2c_platform_data *)(i2c->adap.dev.parent->platform_data))->regulator_id); 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 */ I2C_ERR("[i2c%d] twi_regulator: %s\n", i2c->bus_num, ((struct sunxi_i2c_platform_data *)(i2c->adap.dev.parent->platform_data))->regulator_id); 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; dprintk(DEBUG_INFO, "[i2c%d] run to interrupt\n", i2c->bus_num); if (i2c->twi_drv_used) sunxi_i2c_drv_core_process(i2c); else { 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; unsigned long diff_time; struct timeval start, end; do_gettimeofday(&start); dprintk(DEBUG_INFO, "[i2c%d] num = %d\n", i2c->bus_num, num); for (i = 0; i < num; i++) { dprintk(DEBUG_INFO, "[i2c%d] msgs[%d].addr = 0x%x\n", i2c->bus_num, i, msgs[i].addr); dprintk(DEBUG_INFO, "[i2c%d] msgs[%d].flags = 0x%x\n", i2c->bus_num, i, msgs[i].flags); dprintk(DEBUG_INFO, "[i2c%d] msgs[%d].len = %u\n", i2c->bus_num, i, msgs[i].len); } ret = pm_runtime_get_sync(i2c->dev); if (ret < 0) goto out; if (i2c->twi_drv_used) { dprintk(DEBUG_INFO, "[i2c%d] twi driver xfer\n", i2c->bus_num); ret = sunxi_i2c_drv_do_xfer(i2c, msgs, num); } else { dprintk(DEBUG_INFO, "[i2c%d] twi engine xfer\n", i2c->bus_num); for (i = 1; i <= adap->retries; i++) { ret = sunxi_i2c_do_xfer(i2c, msgs, num); if (ret != SUNXI_I2C_RETRY) goto out; dprintk(DEBUG_INFO, "[i2c%d] Retrying transmission %d\n", i2c->adap.nr, i); udelay(100); } ret = -EREMOTEIO; } if (msgs->flags & I2C_M_RD) dprintk(DEBUG_INFO, "[i2c%d] msgs->buf[0]=0x%x\n", i2c->bus_num, (msgs->buf)[0]); out: pm_runtime_mark_last_busy(i2c->dev); pm_runtime_put_autosuspend(i2c->dev); do_gettimeofday(&end); diff_time = 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec; dprintk(DEBUG_INFO, "[i2c%d] diff_time = %ld us\n", i2c->bus_num, diff_time); 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, TWI_SRST_REG, TWI_SRST_SRST); 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) { dprintk(DEBUG_INFO, "[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) { I2C_ERR("[i2c%d] twi_regulator: %s\n", i2c->bus_num, ((struct sunxi_i2c_platform_data *)(i2c->adap.dev.parent->platform_data))->regulator_id); twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST); 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); */ /* 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; } /* enable twi engine or twi driver */ if (i2c->twi_drv_used) { twi_set_clock(i2c, TWI_DRIVER_BUSC, 24000000, i2c->bus_freq, TWI_DRV_CLK_M, TWI_DRV_CLK_N); dprintk(DEBUG_INFO, "[i2c%d] set twi driver clock\n", i2c->bus_num); twi_enable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN); dprintk(DEBUG_INFO, "[i2c%d] twi driver enable \n", i2c->bus_num); } else { #ifdef CONFIG_EVB_PLATFORM twi_set_clock(i2c, TWI_CLK_REG, apb_clk, i2c->bus_freq, TWI_CLK_DIV_M, TWI_CLK_DIV_N); #else twi_set_clock(i2c, TWI_CLK_REG, 24000000, i2c->bus_freq, TWI_CLK_DIV_M, TWI_CLK_DIV_N); #endif dprintk(DEBUG_INFO, "[i2c%d] set twi engine clock\n", i2c->bus_num); twi_enable(i2c->base_addr, TWI_CTL_REG, TWI_CTL_BUSEN); dprintk(DEBUG_INFO, "[i2c%d] twi engine enable \n", i2c->bus_num); } return 0; } static int sunxi_i2c_clk_exit(struct sunxi_i2c *i2c) { /* disable twi bus */ if (i2c->twi_drv_used) { twi_disable(i2c->base_addr, TWI_DRIVER_CTRL, TWI_DRV_EN); dprintk(DEBUG_INFO, "[i2c%d] disable twi driver\n", i2c->bus_num); } else twi_disable(i2c->base_addr, TWI_CTL_REG, TWI_CTL_BUSEN); /* 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; } if (!(i2c->twi_drv_used)) twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST); 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_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 = %u\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 = [%u] %s\n" "i2c->msg_num = %u, ->msg_idx = %u, ->msg_ptr = %u\n" "i2c->bus_freq = %u\n" "i2c->irq = %d\n" "i2c->debug_state = %u\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->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; dma_addr_t phy_addr; 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 { if (strlen(str_vcc_twi) >= sizeof(pdata->regulator_id)) I2C_ERR("[i2c%d] illegal regulator id\n", pdev->id); else { strcpy(pdata->regulator_id, str_vcc_twi); pr_info("[i2c%d] twi_regulator: %s\n", pdev->id, pdata->regulator_id); } } 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; 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 defined(CONFIG_ARCH_SUN8IW18) i2c->twi_drv_used = of_property_read_bool(np, "twi_drv_used"); dprintk(DEBUG_INIT, "[i2c%d] twi_drv_used = %d\n", i2c->bus_num, i2c->twi_drv_used); #endif 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; } phy_addr = (dma_addr_t)mem_res->start; /* Init DMA config if supported */ if (i2c->twi_drv_used) sunxi_i2c_dma_request(i2c, phy_addr); i2c->pdev = pdev; platform_set_drvdata(pdev, i2c); sunxi_i2c_sysfs(pdev); dprintk(DEBUG_INIT, "I2C: %s: sunxi I2C adapter\n", dev_name(&i2c->adap.dev)); dprintk(DEBUG_INFO, "TWI_CTL 0x%p: 0x%08x\n", i2c->base_addr + 0x0c, readl(i2c->base_addr + 0x0c)); dprintk(DEBUG_INFO, "TWI_STAT 0x%p: 0x%08x\n", i2c->base_addr + 0x10, readl(i2c->base_addr + 0x10)); dprintk(DEBUG_INFO, "TWI_CLK 0x%p: 0x%08x\n", i2c->base_addr + 0x14, readl(i2c->base_addr + 0x14)); dprintk(DEBUG_INFO, "TWI_SRST 0x%p: 0x%08x\n", i2c->base_addr + 0x18, readl(i2c->base_addr + 0x18)); dprintk(DEBUG_INFO, "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); dprintk(DEBUG_INIT, "[i2c.%d] remove ...\n", i2c->bus_num); if (i2c->dma_tx) sunxi_i2c_dma_free(i2c->dma_tx); if (i2c->dma_rx) sunxi_i2c_dma_free(i2c->dma_rx); 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 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; } 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); dprintk(DEBUG_SUSPEND, "[i2c%d] finish\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; } if (!(i2c->twi_drv_used)) twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST); dprintk(DEBUG_SUSPEND, "[i2c%d] finish\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); dprintk(DEBUG_SUSPEND, "[i2c%d] finish\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); dprintk(DEBUG_SUSPEND, "[i2c%d] finish\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) { dprintk(DEBUG_INIT, "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_param_named(debug, debug_mask, int, 0664); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:i2c-sunxi"); MODULE_DESCRIPTION("SUNXI I2C Bus Driver"); MODULE_AUTHOR("pannan");