diff options
Diffstat (limited to 'arch/arm/mach-stmp378x/stmp378x_i2c.c')
-rw-r--r-- | arch/arm/mach-stmp378x/stmp378x_i2c.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/arch/arm/mach-stmp378x/stmp378x_i2c.c b/arch/arm/mach-stmp378x/stmp378x_i2c.c new file mode 100644 index 000000000000..f5e96fca5de8 --- /dev/null +++ b/arch/arm/mach-stmp378x/stmp378x_i2c.c @@ -0,0 +1,281 @@ +/* + * Freescale STMP378X I2C low-level/dma functions + * + * Author: Dmitrij Frasenyak <sed@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> + +#include <linux/dma-mapping.h> +#include <mach/hardware.h> +#include <mach/regs-i2c.h> +#include <mach/regs-apbx.h> +#include <mach/dma.h> +#include <mach/i2c.h> +#include <mach/platform.h> +#include <mach/pinmux.h> + +#define STMP378X_APBX_I2C 3 + +static unsigned int dma_channel = + STMP3XXX_DMA(STMP378X_APBX_I2C, STMP3XXX_BUS_APBX); + + +static struct stmp3xxx_dma_descriptor i2c_dma_read[2]; +static struct stmp3xxx_dma_descriptor i2c_dma_write; +static dma_addr_t i2c_buf_phys; +static u8 *i2c_buf_virt; + + +/* + * Select device to read from + */ + +u32 cmd_i2c_select[4] = { + 0, /* Chain to i2c_read */ + + (BF(1, APBX_CHn_CMD_XFER_COUNT) | + /* BM_APBX_CHn_CMD_SEMAPHORE | */ + BF(1, APBX_CHn_CMD_CMDWORDS) | + BM_APBX_CHn_CMD_WAIT4ENDCMD | + BM_APBX_CHn_CMD_CHAIN | + BM_APBX_CHn_CMD_IRQONCMPLT | /* For debug*/ + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND)), + + 0, /* dma handler */ + + BM_I2C_CTRL0_RETAIN_CLOCK | + BM_I2C_CTRL0_PRE_SEND_START | + BM_I2C_CTRL0_MASTER_MODE | + BM_I2C_CTRL0_DIRECTION | + BF(1, I2C_CTRL0_XFER_COUNT) + +}; + +u32 cmd_i2c_write[4] = { + 0, + + (BM_APBX_CHn_CMD_SEMAPHORE | + BF(1, APBX_CHn_CMD_CMDWORDS) | + BM_APBX_CHn_CMD_WAIT4ENDCMD | + BM_APBX_CHn_CMD_IRQONCMPLT | + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND)), + + 0, /* dma handler */ + + BM_I2C_CTRL0_PRE_SEND_START | + BM_I2C_CTRL0_MASTER_MODE | +/* BM_I2C_CTRL0_POST_SEND_STOP | */ + BM_I2C_CTRL0_DIRECTION + +}; + + +u32 cmd_i2c_read[4] = { + 0, + + (BM_APBX_CHn_CMD_SEMAPHORE | + BF(1, APBX_CHn_CMD_CMDWORDS) | + BM_APBX_CHn_CMD_WAIT4ENDCMD | + BM_APBX_CHn_CMD_IRQONCMPLT | + BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND)), + + 0, /* dma handler */ + + BM_I2C_CTRL0_SEND_NAK_ON_LAST | +/* BM_I2C_CTRL0_POST_SEND_STOP | */ + BM_I2C_CTRL0_MASTER_MODE | + 0 +}; + + +int hw_i2c_init_dma(struct device *dev) +{ + int ret; + + ret = stmp3xxx_dma_request(dma_channel, dev, "i2c"); + if (ret) { + dev_err(dev, "stmp3xxx_dma_request failed: error %d\n", ret); + return ret; + } + + i2c_buf_virt = + dma_alloc_coherent( + dev, + PAGE_SIZE, + &i2c_buf_phys, + GFP_KERNEL); + + if (i2c_buf_virt == NULL) + return -ENOMEM; + + + stmp3xxx_dma_allocate_command( + dma_channel, + &i2c_dma_read[0]); + + stmp3xxx_dma_allocate_command( + dma_channel, + &i2c_dma_read[1]); + + stmp3xxx_dma_allocate_command( + dma_channel, + &i2c_dma_write); + + stmp3xxx_dma_reset_channel(dma_channel); + stmp3xxx_dma_clear_interrupt(dma_channel); + stmp3xxx_dma_enable_interrupt(dma_channel); + return 0; +}; + +void hw_i2c_free_dma(struct device *dev) +{ + stmp3xxx_dma_free_command( + dma_channel, + &i2c_dma_write); + + stmp3xxx_dma_free_command( + dma_channel, + &i2c_dma_read[1]); + + stmp3xxx_dma_free_command( + dma_channel, + &i2c_dma_read[0]); + + dma_free_coherent( + dev, + PAGE_SIZE, + i2c_buf_virt, + i2c_buf_phys); + + stmp3xxx_dma_release(dma_channel); +} + +void hw_i2c_clear_dma_interrupt(void) +{ + stmp3xxx_dma_clear_interrupt(dma_channel); +} +EXPORT_SYMBOL(hw_i2c_clear_dma_interrupt); + +void hw_i2c_setup_write(u8 addr, void *buff, int len, int flags) +{ + + memcpy(i2c_dma_write.command, &cmd_i2c_write, sizeof(cmd_i2c_write)); + + i2c_dma_write.command->cmd |= + BF(len+1, APBX_CHn_CMD_XFER_COUNT); + + i2c_dma_write.command->pio_words[0] |= + BF(len+1, I2C_CTRL0_XFER_COUNT) | flags; + + i2c_dma_write.command->buf_ptr = i2c_buf_phys; + i2c_buf_virt[0] = addr | I2C_WRITE ; + memcpy(&i2c_buf_virt[1], buff, len); +} +EXPORT_SYMBOL(hw_i2c_setup_write); + +void hw_i2c_finish_read(void *buff, int len) +{ + memcpy(buff, &i2c_buf_virt[1], len); + +} +EXPORT_SYMBOL(hw_i2c_finish_read); + +void hw_i2c_setup_read(u8 addr, void *buff, int len, int flags) +{ + + if (len > (PAGE_SIZE - 4)) + BUG(); + + memcpy(i2c_dma_read[0].command, + &cmd_i2c_select, + sizeof(cmd_i2c_select)); + + memcpy(i2c_dma_read[1].command, + &cmd_i2c_read, + sizeof(cmd_i2c_read)); + + i2c_dma_read[0].command->next = i2c_dma_read[1].handle; + i2c_dma_read[0].command->buf_ptr = i2c_buf_phys ; + i2c_buf_virt[0] = addr | I2C_READ ; + + i2c_dma_read[1].command->cmd |= BF(len, APBX_CHn_CMD_XFER_COUNT); + + i2c_dma_read[1].command->pio_words[0] |= + BF(len, I2C_CTRL0_XFER_COUNT) | flags; + + i2c_dma_read[1].command->buf_ptr = (u32)i2c_buf_phys + 1 ; + memcpy(&i2c_buf_virt[1], buff, len); + +} +EXPORT_SYMBOL(hw_i2c_setup_read); + +void hw_i2c_run(int dir) +{ + if (dir == I2C_WRITE) + stmp3xxx_dma_go(dma_channel, &i2c_dma_write, 1); + else + stmp3xxx_dma_go(dma_channel, &i2c_dma_read[0], 1); +} +EXPORT_SYMBOL(hw_i2c_run); + +void hw_i2c_reset_dma(void) +{ + stmp3xxx_dma_reset_channel(dma_channel); + stmp3xxx_dma_clear_interrupt(dma_channel); +} +EXPORT_SYMBOL(hw_i2c_reset_dma); + + +int hw_i2c_init(struct device *dev) +{ + if (stmp3xxx_request_pin_group(dev->platform_data, "i2c")) + return -1; + + + /* Take controller out of reset */ + stmp3xxx_clearl(BM_I2C_CTRL0_SFTRST | + BM_I2C_CTRL0_CLKGATE, + REGS_I2C_BASE + HW_I2C_CTRL0); + udelay(10); + +/* * Set timing + * High time = 120 clks; read bit at 48 for 95Khz/24mhz + * Low time = 128 clks; write bit at 48 for 95khz/24mhz +*/ + +/* + Don't set 400khz by default; stfm1000 needs 100khz at the start. + __raw_writel(0x00780030, REGS_I2C_BASE + HW_I2C_TIMING0); + __raw_writel(0x001F000F, REGS_I2C_BASE + HW_I2C_TIMING1); + __raw_writel(0x0015000D, REGS_I2C_BASE + HW_I2C_TIMING2); +*/ + dev_dbg(dev, "I2C module version %x\n ", + __raw_readl(REGS_I2C_BASE + HW_I2C_VERSION)); + hw_i2c_init_dma(dev); + return 0; +} +EXPORT_SYMBOL(hw_i2c_init); + +void hw_i2c_stop(struct device *dev) +{ + stmp3xxx_setl(BM_I2C_CTRL0_SFTRST, + REGS_I2C_BASE + HW_I2C_CTRL0); + hw_i2c_reset_dma(); + hw_i2c_free_dma(dev); + stmp3xxx_release_pin_group(dev->platform_data, "i2c"); +} +EXPORT_SYMBOL(hw_i2c_stop); |