转自 http://arm.cirrus.com/forum/viewtopic.php?t=51&highlight=i2c
The attached file should be placed in linux-2.4.21\drivers\i2c\ .
In Config.in I have added:
dep_tristate ' I2C bus glue for Cirrus EP93xx processors' CONFIG_I2C_EP93XX $CONFIG_I2C_ALGOBIT
And in Makefile:
obj-$(CONFIG_I2C_EP93XX) += i2c-ep93xx.o
/* ------------------------------------------------------------------------ * * i2c-ep933xx.c I2C bus glue for Cirrus EP93xx * * ------------------------------------------------------------------------ * Copyright (C) 2004 Michael Burian Based on i2c-parport-light.c Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ------------------------------------------------------------------------ */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> #include <asm/io.h> //1/(2*clockfrequency) #define EE_DELAY_USEC 50 #define GPIOG_EECLK 1 #define GPIOG_EEDAT 2 /* ----- I2C algorithm call-back functions and structures ----------------- */ // TODO: optimize static void ep93xx_setscl(void *data, int state) { unsigned int val, dir; val = inl(GPIO_PGDR); dir = inl(GPIO_PGDDR); /* Configure the clock line as output. */ dir |= GPIOG_EECLK; outl(dir, GPIO_PGDDR); /* Set clock line to state */ if(state) val |= GPIOG_EECLK; else val &= ~GPIOG_EECLK; outl(val, GPIO_PGDR); } static void ep93xx_setsda(void *data, int state) { unsigned int val, dir; val = inl(GPIO_PGDR); dir = inl(GPIO_PGDDR); /* Configure the data line as output. */ dir |= GPIOG_EEDAT; outl(dir, GPIO_PGDDR); /* Set data line to state */ if(state) val |= GPIOG_EEDAT; else val &= ~GPIOG_EEDAT; outl(val, GPIO_PGDR); } static int ep93xx_getscl(void *data) { unsigned int val, dir; val = inl(GPIO_PGDR); dir = inl(GPIO_PGDDR); /* Configure the clock line as input */ dir &= ~GPIOG_EECLK; outl(dir, GPIO_PGDDR); /* Return state of the clock line */ return (inl(GPIO_PGDR) & GPIOG_EECLK) ? 1 : 0; } static int ep93xx_getsda(void *data) { unsigned int val, dir; val = inl(GPIO_PGDR); dir = inl(GPIO_PGDDR); /* Configure the data line as input */ dir &= ~GPIOG_EEDAT; outl(dir, GPIO_PGDDR); /* Return state of the data line */ return (inl(GPIO_PGDR) & GPIOG_EEDAT) ? 1 : 0; } static int ep93xx_reg(struct i2c_client *client) { return 0; } static int ep93xx_unreg(struct i2c_client *client) { return 0; } static void ep93xx_inc_use(struct i2c_adapter *adap) { #ifdef MODULE MOD_INC_USE_COUNT; #endif } static void ep93xx_dec_use(struct i2c_adapter *adap) { #ifdef MODULE MOD_DEC_USE_COUNT; #endif } /* ------------------------------------------------------------------------ * Encapsulate the above functions in the correct operations structure. * This is only done when more than one hardware adapter is supported. */ /* last line (us, ms, timout) * us dominates the bit rate: 10us means: 100Kbit/sec(25 means 40kbps) * 10ms not known * 100ms timeout */ static struct i2c_algo_bit_data ep93xx_data = { NULL, ep93xx_setsda, ep93xx_setscl, ep93xx_getsda, ep93xx_getscl, // EE_DELAY_USEC, EE_DELAY_USEC, 100, /* orginal (non-guide) value 10, 10, 100 */ 10, 10, 100, /* orginal (non-guide) value 10, 10, 100 */ }; /* ----- I2c structure ---------------------------------------------------- */ static struct i2c_adapter ep93xx_adapter = { "EP93XX I2C interface", I2C_HW_B_LP, //I2C_HW_B_GUIDE NULL, &ep93xx_data, ep93xx_inc_use, ep93xx_dec_use, ep93xx_reg, ep93xx_unreg, }; /* ----- Module loading, unloading and information ------------------------ */ static int __init i2c_ep93xx_init(void) { unsigned long uiVal, uiDDR; /* Read the current value of the GPIO data and data direction registers. */ uiVal = inl(GPIO_PGDR); uiDDR = inl(GPIO_PGDDR); /* If the GPIO pins have not been configured since reset, the data * and clock lines will be set as inputs and with data value of 0. * External pullup resisters are pulling them high. * Set them both high before configuring them as outputs. */ uiVal |= (GPIOG_EEDAT | GPIOG_EECLK); outl(uiVal, GPIO_PGDR); /* Delay to meet the EE Interface timing specification. */ udelay(EE_DELAY_USEC); /* Configure the EE data and clock lines as outputs. */ uiDDR |= (GPIOG_EEDAT | GPIOG_EECLK); outl(uiDDR, GPIO_PGDDR); /* Delay to meet the EE Interface timing specification. */ udelay(EE_DELAY_USEC); /* Reset hardware to a sane state (SCL and SDA high) */ ep93xx_setsda(NULL, 1); ep93xx_setscl(NULL, 1); if (i2c_bit_add_bus(&ep93xx_adapter) < 0) { printk(KERN_ERR "i2c-ep93xx: Unable to register with I2C\n"); return -ENODEV; } return 0; } static void __exit i2c_ep93xx_exit(void) { i2c_bit_del_bus(&ep93xx_adapter); } EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Michael Burian"); MODULE_DESCRIPTION("I2C bus glue for Cirrus EP93xx processors"); MODULE_LICENSE("GPL"); module_init(i2c_ep93xx_init); module_exit(i2c_ep93xx_exit);