i2c - gpio

处理器只支持3个i2c通道,常常会不够用,最近写了一个gpio模拟i2c的driver,把模拟的i2c通道加入了i2c-core中,作为第 4 通道,调用接口与标准i2c一致,代码如下:

#define DELAY     2
#define SCL_GPIO  GPIO_I2C_SCL
#define SDA_GPIO  GPIO_I2C_SDA

static inline void i2c_delay(uint16_t delay)
{
	udelay(delay);
}

static inline void set_scl_low(void)
{
	gpio_direction_output(SCL_GPIO, 0);
}

static inline void set_scl_high(void)
{
	gpio_direction_output(SCL_GPIO, 1);
}

static inline void set_sda_low(void)
{
	gpio_direction_output(SDA_GPIO, 0);
}

static inline void set_sda_high(void)
{
	gpio_direction_output(SDA_GPIO, 1);
}

static inline void set_sda_in(void)
{
	gpio_direction_input(SDA_GPIO);
}

static inline uint8_t get_sda_bit(void)
{
	return __gpio_get_value(SDA_GPIO);
}

int i2c_gpio_init(void)
{
	int err;
	err = gpio_request(SCL_GPIO, NULL);
	if (err != 0)
		return err;
	err = gpio_request(SDA_GPIO, NULL);

	set_sda_high();
	set_scl_high();

	return err;
}

void i2c_gpio_free(void)
{
	gpio_free(SDA_GPIO);
	gpio_free(SCL_GPIO);
}

static inline void i2c_start(void)
{
	set_sda_high();
	i2c_delay(DELAY);
	set_scl_high();
	i2c_delay(DELAY);

	set_sda_low();
	i2c_delay(DELAY);
	set_scl_low();
	i2c_delay(DELAY);
}

static inline void i2c_stop(void)
{
	set_sda_low();
	i2c_delay(DELAY);
	set_scl_high();
	i2c_delay(4*DELAY);
	set_sda_high();
	i2c_delay(4*DELAY);
}

/*
 * return value:
 *      0 ---  收到ACK
 *      1 ---  没收到ACK
 */
uint8_t i2c_send_byte(uint8_t send_byte)
{
	uint8_t rc = 0;
	uint8_t out_mask = 0x80;
	uint8_t count = 8;
	uint8_t value;

	while(count > 0) {
		set_scl_low();
		i2c_delay(DELAY);
		value = ((send_byte & out_mask) ? 1 : 0);
		if(value == 1) {
			set_sda_high();
		} else {
			set_sda_low();
		}
		send_byte <<= 1;
		i2c_delay(DELAY);

		set_scl_high();
		i2c_delay(DELAY);

		count--;
	}
	set_scl_low();
	set_sda_in();
	i2c_delay(4*DELAY);
	set_scl_high();
	i2c_delay(DELAY);
	rc = get_sda_bit();
	i2c_delay(DELAY);
	set_scl_low();

	return rc;
}

/*
 * ack = 0 发送ACK
 * ack = 1 发送非ACK停止读取
 */
void i2c_read_byte(uint8_t *buffer, uint8_t ack)
{
	uint8_t count = 0x08;
	uint8_t data = 0x00;
	uint8_t temp = 0;

	while(count > 0) {
		set_scl_low();
		i2c_delay(2*DELAY);
		if(count == 8)
			set_sda_in();
		i2c_delay(DELAY);
		set_scl_high();
		i2c_delay(2*DELAY);
		temp = get_sda_bit();
		data <<= 1;
		if(temp)
			data |= 0x01;

		i2c_delay(DELAY);
		count--;
	}

	set_scl_low();
	i2c_delay(2*DELAY);
	if(ack) {
		set_sda_high();
	} else {
		set_sda_low();
	}
	i2c_delay(DELAY);
	set_scl_high();
	i2c_delay(2*DELAY);

	*buffer = data;
	set_scl_low();
}

struct atxx_i2c_gpio {
	struct i2c_adapter adap;
	struct device *dev;
	struct clk *clk;
	struct i2c_msg *msg;
	spinlock_t lock;
};

static int send_i2c(struct atxx_i2c_gpio *i2c)
{
	int i;
	uint8_t ack;

	spin_lock_irq(&i2c->lock);
	i2c_start();

	ack = i2c_send_byte((i2c->msg->addr << 1) | 0x00);
	if(ack){
		goto out;
	}

	for(i = 0; i < i2c->msg->len; i++) {
		ack = i2c_send_byte(i2c->msg->buf[i]);
		if(ack){
			goto out;
		}
	}

out:
	i2c_stop();
	spin_unlock_irq(&i2c->lock);
	return ack;
}

static int recv_i2c(struct atxx_i2c_gpio *i2c)
{
	int i;
	uint8_t ack;

	spin_lock_irq(&i2c->lock);
	i2c_start();

	ack = i2c_send_byte((i2c->msg->addr << 1) | 0x01);
	if(ack){
		goto out;
	}

	for(i = 0; i < i2c->msg->len - 1; i++) {
		i2c_read_byte(&i2c->msg->buf[i], 0);
	}

	i2c_read_byte(&i2c->msg->buf[i2c->msg->len - 1], 1);
out:
	i2c_stop();
	spin_unlock_irq(&i2c->lock);
	return ack;
}

static int i2c_atxx_gpio_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	int i, ret;
	struct atxx_i2c_gpio *i2c = i2c_get_adapdata(adap);

	for (i = 0; i < num; i++) {
		i2c->msg        = &msgs[i];
		i2c->msg->flags = msgs[i].flags;

		if (i2c->msg->flags & I2C_M_RD) {
			ret = recv_i2c(i2c);
			if (ret) {
				printk("recv_i2c failed. dev_name(%s).addr = 0x%02x\n",
					 dev_name(i2c->dev), i2c->msg[0].addr);
				return -EAGAIN;
			}
		} else {
			ret = send_i2c(i2c);
			if (ret) {
				printk("send_i2c failed. dev_name(%s).addr = 0x%02x\n",
					 dev_name(i2c->dev), i2c->msg[0].addr);
				return -EAGAIN;
			}
		}
	}

	return num;
}

static uint32_t i2c_atxx_gpio_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static struct i2c_algorithm i2c_atxx_gpio_algo = {
	.master_xfer   = i2c_atxx_gpio_xfer,
	.functionality = i2c_atxx_gpio_func,
};

static int i2c_atxx_gpio_probe(struct platform_device *pdev)
{
	int ret;
	struct atxx_i2c_gpio *i2c;

	ret = i2c_gpio_init();
	if(ret) {
		dev_err(&pdev->dev, "couldn't request gpio\n");
		return ret;
	}

	i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
	if (!i2c) {
		dev_err(&pdev->dev, "couldn't allocate memory\n");;
		ret = -ENOMEM;
		goto err_mem;
	}

	spin_lock_init(&i2c->lock);

	i2c->dev = &pdev->dev;
	i2c->adap.owner = THIS_MODULE;
	i2c->adap.algo  = &i2c_atxx_gpio_algo;
	i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	i2c->adap.timeout = I2C_ATXX_TIMEOUT;
	i2c->adap.retries = I2C_ATXX_RETRIES;
	i2c->adap.algo_data  = i2c;
	i2c->adap.dev.parent = &pdev->dev;
	i2c->adap.nr = pdev->id != -1 ? pdev->id : 0;

	sprintf(i2c->adap.name, "ATXX i2c gpio adapter");
	platform_set_drvdata(pdev, i2c);
	i2c_set_adapdata(&i2c->adap, i2c);

	ret = i2c_add_numbered_adapter(&i2c->adap);
	if (ret) {
		dev_err(i2c->dev, "failure adding adapter\n");
		goto err_adp;
	}

	return 0;

err_adp:
	kfree(i2c);
err_mem:
	i2c_gpio_free();
	return ret;
}

static int i2c_atxx_gpio_remove(struct platform_device *pdev)
{
	struct atxx_i2c_gpio *i2c = platform_get_drvdata(pdev);

	platform_set_drvdata(pdev, NULL);
	i2c_del_adapter(&i2c->adap);
	kfree(i2c);
	i2c_gpio_free();

	return 0;
}

static int i2c_atxx_gpio_suspend(struct device *dev)
{
	return 0;
}

static int i2c_atxx_gpio_resume(struct device *dev)
{
	return 0;
}

static void i2c_atxx_gpio_shutdown(struct platform_device *pdev)
{
	/* TODO: */
}

const struct dev_pm_ops i2c_atxx_gpio_pm_ops = {
	.suspend = i2c_atxx_gpio_suspend,
	.resume  = i2c_atxx_gpio_resume,
};

static struct platform_driver i2c_atxx_gpio_driver = {
	.driver	= {
		.name  = "i2c-gpio",
		.owner = THIS_MODULE,
		.pm    = &i2c_atxx_gpio_pm_ops,
	},
	.probe		= i2c_atxx_gpio_probe,
	.remove		= i2c_atxx_gpio_remove,
	.shutdown	= i2c_atxx_gpio_shutdown,
};

static int __init i2c_adap_atxx_gpio_init(void)
{
	return platform_driver_register(&i2c_atxx_gpio_driver);
}
static void __exit i2c_adap_atxx_gpio_exit(void)
{
	platform_driver_unregister(&i2c_atxx_gpio_driver);
}

arch_initcall(i2c_adap_atxx_gpio_init);

你可能感兴趣的:(i2c - gpio)