开发中经常与i2c打交道,芯片中自带的硬件i2c控制器使用起来并不是很灵活,而且要研究半天的寄存器。所以干脆搞一个软件模拟gpio的通用代码,移植起来也方便,使用灵活
具体代码如下:
#include <linux/module.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/rt2880/rt_mmap.h>
#include <asm/system.h>
#include <asm/io.h>
#include "gpio_i2c.h"
#include "../ralink_gpio.h"
#define DELAY(u) udelay(u*100)
#define RT2860REG(addr) (*(volatile u32 *)(addr))
static void gpio_i2c_set_bit(int id, int onoff)
{
int tmp;
if(id<=21&&id>=0)
{
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
tmp |= (1<<(id));
*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
tmp = (tmp&~(1<<(id)))|(onoff<<(id));
*(volatile u32 *)(RALINK_REG_PIODATA) = tmp;
}
else if(id>=22&&id<=27)
{
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
tmp |= (1<<(id-22));
*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));
tmp = (tmp&~(1<<(id-22)))|(onoff<<(id-22));
*(volatile u32 *)(RALINK_REG_PIO2722DATA) = tmp;
}
else
{
printk("####HU#### %s id %d invalid \r\n",__FUNCTION__,id);
}
}
static int gpio_i2c_get_bit(int id)
{
int tmp;
if(id<=21&&id>=0)
{
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
tmp &= ~(1<<(id));
*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
return (tmp>>id&0x01);
}
else if(id>=22&&id<=27)
{
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
tmp &= ~(1<<(id-22));
*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));
return (tmp>>(id-22)&0x01);
}
else
{
printk("####HU#### %s id %d invalid \r\n",__FUNCTION__,id);
}
}
static void i2c_clk(int onoff)
{
gpio_i2c_set_bit(2,onoff);
}
static void i2c_dat(int onoff)
{
gpio_i2c_set_bit(1,onoff);
}
static unsigned char i2c_data_read(void)
{
// unsigned char tmp;
// tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
// tmp &= ~(1<<(1));
// *(volatile u32 *)(RALINK_REG_PIODIR) = tmp;
//
// //DELAY(1);
//
// tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
// if((tmp&(1<<1)) != 0)
// return 1;
// else
// return 0;
return gpio_i2c_get_bit(1);
}
/*
* sends a start bit via I2C rountine.
*
*/
static void i2c_start_bit(void)
{
DELAY(1);
i2c_clk(1);
i2c_dat(1);
DELAY(1);
i2c_dat(0);
DELAY(1);
}
/*
* sends a stop bit via I2C rountine.
*
*/
static void i2c_stop_bit(void)
{
i2c_clk(0);
i2c_dat(1);
DELAY(1);
i2c_clk(1);
}
/*
* sends a character over I2C rountine.
*
* @param c: character to send
*
*/
static void i2c_send_byte(unsigned char c)
{
int i;
local_irq_disable();
//i2c_clk(1);
for (i=0; i<8; i++)
{
i2c_clk(0);
if (c & (1<<(7-i)))
i2c_dat(1);
else
i2c_dat(0);
DELAY(1);
i2c_clk(1);
DELAY(1);
}
// i2c_dat(1);
local_irq_enable();
}
/* receives a character from I2C rountine.
*
* @return value: character received
*
*/
static unsigned char i2c_receive_byte(void)
{
int j=0;
int i;
unsigned char regvalue;
local_irq_disable();
i2c_data_read();
for (i=0; i<8; i++)
{
i2c_clk(0);
DELAY(1);
i2c_clk(1);
i2c_data_read();
if (i2c_data_read())
j+=(1<<(7-i));
DELAY(1);
}
local_irq_enable();
// i2c_dat(0);
// DELAY(1);
return j;
}
/* receives an acknowledge from I2C rountine.
*
* @return value: 0--Ack received; 1--Nack received
*
*/
static int i2c_receive_ack(void)
{
int nack;
i2c_data_read();
i2c_clk(0);
DELAY(1);
i2c_clk(1);
i2c_data_read();
nack = i2c_data_read();
DELAY(1);
i2c_dat(1);
if (nack == 0)
return 1;
return 0;
}
/*
* sends an acknowledge over I2C rountine.
*
*/
static void i2c_send_ack(void)
{
i2c_clk(0);
i2c_dat(0);
DELAY(1);
i2c_clk(1);
DELAY(1);
}
static void si2c_init()
{
RT2860REG(RALINK_REG_GPIOMODE)=RT2860REG(RALINK_REG_GPIOMODE)|(1<<0);
}
static void si2c_exit()
{
}
/*
* read data from the I2C bus by GPIO simulated of a device rountine.
*
* @param devaddress: address of the device
* @param address: address of register within device
*
* @return value: data from the device readed
*
*/
unsigned char gpio_i2c_read(unsigned char devaddress, unsigned char address)
{
int rxdata;
si2c_init();
i2c_start_bit();
i2c_send_byte((unsigned char)((devaddress<<1)|0));
i2c_receive_ack();
i2c_send_byte(address);
i2c_receive_ack();
i2c_start_bit();
i2c_send_byte((unsigned char)((devaddress<<1)|1));
i2c_receive_ack();
rxdata = i2c_receive_byte();
i2c_send_ack();
i2c_stop_bit();
si2c_exit();
return rxdata;
}
EXPORT_SYMBOL(gpio_i2c_read);
/*
* writes data to a device on the I2C bus rountine.
*
* @param devaddress: address of the device
* @param address: address of register within device
* @param data: data for write to device
*
*/
void gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data)
{
si2c_init();
i2c_start_bit();
i2c_send_byte((unsigned char)((devaddress<<1)|0));
i2c_receive_ack();
i2c_send_byte(address);
i2c_receive_ack();
i2c_send_byte(data);
i2c_receive_ack();
i2c_stop_bit();
si2c_exit();
}
EXPORT_SYMBOL(gpio_i2c_write);
/*
* initializes I2C interface routine.
*
* @return value:0--success; 1--error.
*
*/
static int __init gpio_i2c_init(void)
{
}
static void __exit gpio_i2c_exit(void)
{
}
module_init(gpio_i2c_init);
module_exit(gpio_i2c_exit);
#ifdef MODULE
#include <linux/compile.h>
#endif
MODULE_INFO(build, UTS_VERSION);
MODULE_LICENSE("GPL");
MODULE_VERSION("HI_VERSION=" OSDRV_MODULE_VERSION_STRING);
gpio_i2c_set_bit与gpio_i2c_get_bit设置gpio高低的,DELAY宏为延时方法,理论只要实现这几个值就能够实现标准的gpio_i2c的功能了