i2c-omap24xx.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 631 行 · 第 1/2 页
C
631 行
/* * drivers/i2c/busses/i2c-omap24xx.c * * Unified algorithm/adapter I2C driver for OMAP24xx I2C controller. * * Author: Andy Lowe (source@mvista.com) * * Copyright (C) 2004 MontaVista Software, Inc. * Copyright (C) 2004 Texas Instruments. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/i2c.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/arch/mux.h>#include "i2c-omap24xx.h"/* ----- configuration macros ----------------------------------------- */#define NUM_I2C_ADAP 2 /* number of I2C adapters */#define I2C_NAME "i2c-omap24xx"/* The default timeout is for the time allowed for each word (2 bytes) of * an I2C transfer to complete. This value is converted to ticks and * used to initialize the timeout member of the i2c_adapter struct. */#define I2C_DEFAULT_TIMEOUT_MS 20/* The reset timeout is the time allowed for a reset of the I2C controller * to complete. */#define I2C_RESET_TIMEOUT_MS 100/* The bus busy timeout is the maximum time we will wait for the bus to * become idle before initiating a transfer. */#define I2C_BUS_BUSY_TIMEOUT_MS 200/* This driver doesn't support I2C slave mode, so we set our own address * to zero to make sure it won't conflict with any slave devices on the * bus. */#define I2C_OWN_ADDRESS 0/* ----- global defines ----------------------------------------------- */#undef I2C_OMAP24XX_DEBUG#ifdef I2C_OMAP24XX_DEBUG#define LOG_LEVEL KERN_DEBUG#define DEB0(format, arg...) \ printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n", ## arg )#define DEB1(format, arg...) \ if (i2c_debug>=1) { \ printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n", ## arg ); \ }#define DEB2(format, arg...) \ if (i2c_debug>=2) { \ printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n", ## arg ); \ }#define DEB3(format, arg...) \ if (i2c_debug>=3) { \ printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n", ## arg ); \ }#define DEB9(format, arg...) \ /* debug the protocol by showing transferred bits */ \ if (i2c_debug>=9) { \ printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n", ## arg ); \ }#else#define DEB0(fmt, args...)#define DEB1(fmt, args...)#define DEB2(fmt, args...)#define DEB3(fmt, args...)#define DEB9(fmt, args...)#endif/* ----- global variables --------------------------------------------- */struct omap24xx_i2c_adapter { struct i2c_adapter adap; unsigned long mmio_base_phys; unsigned long mmio_base; unsigned long mmio_size; int irq; wait_queue_head_t i2c_wait_queue;};static struct omap24xx_i2c_adapter *saved_oadap[NUM_I2C_ADAP];/* ----- module parameters -------------------------------------------- */static int i2c_clock; /* Fast Mode = 400 KHz, Standard Mode = 100 KHz */#ifdef I2C_OMAP24XX_DEBUGstatic int i2c_debug;#endif/* * I2C register I/O routines */static __inline__ u16 i2c_reg_in(const struct omap24xx_i2c_adapter *oadap, u32 offset){ return readw(oadap->mmio_base + offset);}static __inline__ u16 i2c_reg_out(const struct omap24xx_i2c_adapter *oadap, u32 offset, u16 val){ writew(val, oadap->mmio_base + offset); return val;}/* ----- Utility functions -------------------------------------------- *//* Initialize the I2C controller. Returns zero if successful, * non-zero otherwise. */static inti2c_hw_init(struct omap24xx_i2c_adapter *oadap){ unsigned long timeout; /* disable the I2C controller */ i2c_reg_out(oadap, I2C_CON, 0); /* reset the I2C controller */ i2c_reg_out(oadap, I2C_SYSC, I2C_SYSC_SRST); /* initialize clock prescaler for 12MHz ICLK */ i2c_reg_out(oadap, I2C_PSC, 0); /* Initialize the I2C clock timers to generate an I2C bus clock * frequency of i2c_clock kilohertz (default is 100 KHz). */ i2c_reg_out(oadap, I2C_SCLL, (12000/(i2c_clock*2)) - 7); i2c_reg_out(oadap, I2C_SCLH, (12000/(i2c_clock*2)) - 7); /* set our own slave address */ i2c_reg_out(oadap, I2C_OA, I2C_OWN_ADDRESS); /* enable the I2C controller */ i2c_reg_out(oadap, I2C_CON, I2C_CON_I2C_EN); /* wait for reset to complete */ timeout = jiffies + (I2C_RESET_TIMEOUT_MS*HZ)/1000; while (!(i2c_reg_in(oadap, I2C_SYSS) & I2C_SYSS_RDONE) && time_before(jiffies, timeout)) { if (!in_atomic()) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } else udelay(100); } if (!(i2c_reg_in(oadap, I2C_SYSS) & I2C_SYSS_RDONE)) { DEB0("timeout waiting for I2C controller to reset"); return -ETIMEDOUT; } return 0;}static voidi2c_hw_release(struct omap24xx_i2c_adapter *oadap){ /* disable the I2C controller */ i2c_reg_out(oadap, I2C_CON, 0);}/* * The I2C bus protocol supports multiple masters. This driver only supports * using the OMAP I2C bus controller as a master, but we want to try to * accommodate situations where there are other I2C masters sharing the bus. * In order to allow for other bus masters, we have to check if the bus is * busy before initiating a transfer. This functions returns 0 if the bus is * idle, or -ETIMEDOUT if a timeout occurs before the bus becomes idle. */static inti2c_wait_while_bb(const struct omap24xx_i2c_adapter *oadap){ unsigned long timeout = jiffies + (I2C_BUS_BUSY_TIMEOUT_MS*HZ)/1000; while ((i2c_reg_in(oadap, I2C_STAT) & I2C_STAT_BB) && time_before(jiffies, timeout)) { if (!in_atomic()) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); } else udelay(100); } if (i2c_reg_in(oadap, I2C_STAT) & I2C_STAT_BB) { DEB0("timeout waiting for bus to be idle"); return -ETIMEDOUT; } return 0;}/* * Wait for status register update. Returns a negative error code or * the value of the status register. */static inti2c_wait_for_status(struct omap24xx_i2c_adapter *oadap){ struct i2c_adapter *adap = &oadap->adap; wait_queue_t i2c_wait; u16 status; const u16 ie_mask = I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE | I2C_IE_AL_IE; const u16 stat_mask = I2C_STAT_XRDY | I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | I2C_STAT_AL; status = i2c_reg_in(oadap, I2C_STAT); if (!(status & stat_mask)) { init_wait((&i2c_wait)); prepare_to_wait(&oadap->i2c_wait_queue, &i2c_wait, TASK_INTERRUPTIBLE); /* enable I2C interrupts */ i2c_reg_out(oadap, I2C_IE, ie_mask); status = i2c_reg_in(oadap, I2C_STAT); if (!(status & stat_mask)) schedule_timeout(adap->timeout); finish_wait(&oadap->i2c_wait_queue, &i2c_wait); status = i2c_reg_in(oadap, I2C_STAT); if (!(status & stat_mask)) { /* still no status update */ if (signal_pending(current)) return -ERESTARTSYS; else return -ETIMEDOUT; } } return status;}static irqreturn_t omap24xx_i2c_handler(int this_irq, void *dev_id, struct pt_regs *regs){ struct omap24xx_i2c_adapter *oadap = (struct omap24xx_i2c_adapter *) dev_id; DEB3("in interrupt handler"); /* disable I2C interrupts */ i2c_reg_out(oadap, I2C_IE, 0); wake_up_interruptible(&oadap->i2c_wait_queue); return IRQ_HANDLED;}/* * Read or write a single message. Returns the number of bytes * successfully read or written. A start (or restart) condition is always * generated first, followed by msg->len data bytes. A stop condition is * generated if the stop parameter is non-zero. Otherwise, the bus is left * busy (clock line pulled low) so that the next transfer can begin with a * restart. */static intomap24xx_i2c_xfer_msg(struct omap24xx_i2c_adapter *oadap, struct i2c_msg *msg, int stop){ struct i2c_adapter *adap = &oadap->adap; u16 data; int status; int retries; int count; DEB0("bus: %d, addr: 0x%04x, len: %d, flags: 0x%x, stop: %d", adap->nr, msg->addr, msg->len, msg->flags, stop); /* The OMAP controller can't generate a message with zero data bytes, so * return without doing anything in that case. */ if (!msg->len) { DEB0("Skipping message with zero-length payload at addr" " 0x%04x.", msg->addr); return 0; } for (retries = 0; retries <= adap->retries; retries++) { i2c_reg_out(oadap, I2C_SA, msg->addr); i2c_reg_out(oadap, I2C_CNT, msg->len); i2c_reg_out(oadap, I2C_CON, (I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STT | ((msg->flags & I2C_M_TEN) ? I2C_CON_XA : 0) | ((msg->flags & I2C_M_RD) ? 0 : I2C_CON_TRX) | (stop ? I2C_CON_STP : 0))); count = 0; while (count <= msg->len) { status = i2c_wait_for_status(oadap); if (status < 0) { DEB0("I2C transfer aborted with error %d", status); return status;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?