/* linux/drivers/i2c/i2c-adm5120.c Copyright (c) 2006 Steve Tsai ADM5120 I2C bus on GPIO pins Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl 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. Dez/2006 : dl4huf Modified for correct open collector emulation on gpio Add support for all gpio0 to gpio7 Add support for switch-led as gpio8 to gpio22 */ #include #include #include #include #include #include #include #include #include #include static int scl = CONFIG_I2C_ADM5120_SCL; static int sda = CONFIG_I2C_ADM5120_SDA; #define GPIO_PORT0 (*(unsigned long *)0xb20000b8) #define REG_BASE (0xb2000000) typedef struct { u32 reg_offset; int bit_no; } led_config_struct; /* config for GPIO 8..22 */ static led_config_struct led_config[] = /* same counting as led-driver */ { /* Registeraddress, led-number */ {Port0_LED_REG + REG_BASE, 0}, {Port0_LED_REG + REG_BASE, 1}, {Port0_LED_REG + REG_BASE, 2}, {Port1_LED_REG + REG_BASE, 0}, {Port1_LED_REG + REG_BASE, 1}, {Port1_LED_REG + REG_BASE, 2}, {Port2_LED_REG + REG_BASE, 0}, {Port2_LED_REG + REG_BASE, 1}, {Port2_LED_REG + REG_BASE, 2}, {Port3_LED_REG + REG_BASE, 0}, {Port3_LED_REG + REG_BASE, 1}, {Port3_LED_REG + REG_BASE, 2}, {Port4_LED_REG + REG_BASE, 0}, {Port4_LED_REG + REG_BASE, 1}, {Port4_LED_REG + REG_BASE, 2}, }; static void adm5120_gpio_set(int id, int state) { unsigned long led_bit; if(id <= 7 && id >= 0) { if (state) { GPIO_PORT0 |= (1 << (id+24)); /* output high, obsolet ? */ GPIO_PORT0 &= ~(1 << (id+16)); /* input, see below */ } else { GPIO_PORT0 &= ~(1 << (id+24)); /* output low */ GPIO_PORT0 |= (1 << (id+16)); /* output enable */ } } else { id = id - 8; u32* p_reg = (u32*)(led_config[id].reg_offset); led_bit = *p_reg; /* read the current settings */ led_bit &= ~(0xf << (led_config[id].bit_no * 4)); /* clear the led bits */ if (state) { *p_reg = led_bit; /* i2c expect open collector with pullup resitor for high level, so set to input-mode */ } else { *p_reg = led_bit | (3 << (led_config[id].bit_no * 4)); /* set output to low level */ } } } static int adm5120_gpio_get(int id) { unsigned long val; if(id <= 7 && id >= 0) { GPIO_PORT0 &= ~(1 << (id+16)); /* set to input, obsolete ? */ val = GPIO_PORT0 & (1 << (id+8)); /* read the input */ } else { id = id - 8; u32* p_reg = (u32*)(led_config[id].reg_offset); *p_reg &= ~(0xf << (led_config[id].bit_no * 4)); /* set to input mode (mode 0x00h) */ val = *p_reg & (1 << (led_config[id].bit_no + 12)); /* read the input */ } val = (val == 0)? 0:1; return val; } static void bit_adm5120_setscl(void *data, int state) { adm5120_gpio_set(scl, state); } static void bit_adm5120_setsda(void *data, int state) { adm5120_gpio_set(sda, state); } static int bit_adm5120_getscl(void *data) { return adm5120_gpio_get(scl); } static int bit_adm5120_getsda(void *data) { return adm5120_gpio_get(sda); } static int bit_adm5120_reg(struct i2c_client *client) { return 0; } static int bit_adm5120_unreg(struct i2c_client *client) { return 0; } static void bit_adm5120_inc_use(struct i2c_adapter *adap) { MOD_INC_USE_COUNT; } static void bit_adm5120_dec_use(struct i2c_adapter *adap) { MOD_DEC_USE_COUNT; } /* ------------------------------------------------------------------------ * Encapsulate the above functions in the correct operations structure. * This is only done when more than one hardware adapter is supported. */ static struct i2c_algo_bit_data bit_adm5120_data = { NULL, bit_adm5120_setsda, bit_adm5120_setscl, bit_adm5120_getsda, bit_adm5120_getscl, 5, 5, 100, /* waits, timeout, around 62,5KHz SCL-Clock */ }; static struct i2c_adapter bit_adm5120_ops = { .name = "ADM5120 I2C", .id = I2C_HW_B_VELLE, .algo_data = &bit_adm5120_data, .inc_use = bit_adm5120_inc_use, .dec_use = bit_adm5120_dec_use, .client_register = bit_adm5120_reg, .client_unregister = bit_adm5120_unreg, }; int i2c_bitadm5120_init(void) { printk(KERN_INFO "i2c-adm5120.o: %s Driver V1.1\n",bit_adm5120_ops.name); printk(KERN_INFO "i2c-adm5120.o: SCL=GPIO-%02u, SDA=GPIO-%02u\n",scl,sda); if (scl < 0 || sda < 0 || scl == sda || scl > 22 || sda > 22) { printk(KERN_ERR "i2c-adm5120.o: scl and sda must be specified between 0..22\n"); return -EINVAL; } if (i2c_bit_add_bus(&bit_adm5120_ops) < 0) { printk(KERN_ERR "i2c-adm5120.o: adapter %s registration failed\n", bit_adm5120_ops.name); return -ENODEV; } /* Configure GPIOs as open collector outputs (input) */ adm5120_gpio_set(scl, 1); adm5120_gpio_set(sda, 1); return 0; } void i2c_bitadm5120_cleanup(void) { i2c_bit_del_bus(&bit_adm5120_ops); } EXPORT_NO_SYMBOLS; #ifdef MODULE MODULE_AUTHOR("Steve Tsai ,modified DL4HUF "); MODULE_DESCRIPTION("ADM5120 I2C Driver"); MODULE_LICENSE("GPL"); MODULE_PARM(scl, "i"); MODULE_PARM_DESC(scl, "GPIO line for SCL"); MODULE_PARM(sda, "i"); MODULE_PARM_DESC(sda, "GPIO line for SDA"); int init_module(void) { return i2c_bitadm5120_init(); } void cleanup_module(void) { i2c_bitadm5120_cleanup(); } #endif