9
0
Fork 0

i2c: add i2c-gpio support

Based on linux 3.7-rc2

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Jean-Christophe PLAGNIOL-VILLARD 2012-11-02 10:36:51 +01:00 committed by Sascha Hauer
parent 334e8f3b62
commit 32a9da73c2
4 changed files with 224 additions and 0 deletions

View File

@ -4,6 +4,14 @@
menu "I2C Hardware Bus support"
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
depends on GENERIC_GPIO
select I2C_ALGOBIT
help
This is a very simple bitbanging I2C driver utilizing the
arch-neutral GPIO API to control the SCL and SDA lines.
config I2C_IMX
bool "MPC85xx/i.MX I2C Master driver"
depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX

View File

@ -1,2 +1,3 @@
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o

View File

@ -0,0 +1,177 @@
/*
* Bitbanging I2C bus driver using the GPIO API
*
* Copyright (C) 2007 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <common.h>
#include <driver.h>
#include <i2c/i2c.h>
#include <i2c/i2c-algo-bit.h>
#include <i2c/i2c-gpio.h>
#include <init.h>
#include <gpio.h>
struct i2c_gpio_private_data {
struct i2c_adapter adap;
struct i2c_algo_bit_data bit_data;
struct i2c_gpio_platform_data pdata;
};
/* Toggle SDA by changing the direction of the pin */
static void i2c_gpio_setsda_dir(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
if (state)
gpio_direction_input(pdata->sda_pin);
else
gpio_direction_output(pdata->sda_pin, 0);
}
/*
* Toggle SDA by changing the output value of the pin. This is only
* valid for pins configured as open drain (i.e. setting the value
* high effectively turns off the output driver.)
*/
static void i2c_gpio_setsda_val(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
gpio_set_value(pdata->sda_pin, state);
}
/* Toggle SCL by changing the direction of the pin. */
static void i2c_gpio_setscl_dir(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
if (state)
gpio_direction_input(pdata->scl_pin);
else
gpio_direction_output(pdata->scl_pin, 0);
}
/*
* Toggle SCL by changing the output value of the pin. This is used
* for pins that are configured as open drain and for output-only
* pins. The latter case will break the i2c protocol, but it will
* often work in practice.
*/
static void i2c_gpio_setscl_val(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
gpio_set_value(pdata->scl_pin, state);
}
static int i2c_gpio_getsda(void *data)
{
struct i2c_gpio_platform_data *pdata = data;
return gpio_get_value(pdata->sda_pin);
}
static int i2c_gpio_getscl(void *data)
{
struct i2c_gpio_platform_data *pdata = data;
return gpio_get_value(pdata->scl_pin);
}
static int i2c_gpio_probe(struct device_d *dev)
{
struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
int ret;
priv = xzalloc(sizeof(*priv));
adap = &priv->adap;
bit_data = &priv->bit_data;
pdata = &priv->pdata;
if (!dev->platform_data)
return -ENXIO;
memcpy(pdata, dev->platform_data, sizeof(*pdata));
ret = gpio_request(pdata->sda_pin, "sda");
if (ret)
goto err_request_sda;
ret = gpio_request(pdata->scl_pin, "scl");
if (ret)
goto err_request_scl;
if (pdata->sda_is_open_drain) {
gpio_direction_output(pdata->sda_pin, 1);
bit_data->setsda = i2c_gpio_setsda_val;
} else {
gpio_direction_input(pdata->sda_pin);
bit_data->setsda = i2c_gpio_setsda_dir;
}
if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
gpio_direction_output(pdata->scl_pin, 1);
bit_data->setscl = i2c_gpio_setscl_val;
} else {
gpio_direction_input(pdata->scl_pin);
bit_data->setscl = i2c_gpio_setscl_dir;
}
if (!pdata->scl_is_output_only)
bit_data->getscl = i2c_gpio_getscl;
bit_data->getsda = i2c_gpio_getsda;
if (pdata->udelay)
bit_data->udelay = pdata->udelay;
else if (pdata->scl_is_output_only)
bit_data->udelay = 50; /* 10 kHz */
else
bit_data->udelay = 5; /* 100 kHz */
if (pdata->timeout_ms)
bit_data->timeout_ms = pdata->timeout_ms;
else
bit_data->timeout_ms = 100; /* 100 ms */
bit_data->data = pdata;
adap->algo_data = bit_data;
adap->dev.parent = dev;
adap->nr = dev->id;
ret = i2c_bit_add_numbered_bus(adap);
if (ret)
goto err_add_bus;
dev_info(dev, "using pins %u (SDA) and %u (SCL%s)\n",
pdata->sda_pin, pdata->scl_pin,
pdata->scl_is_output_only
? ", no clock stretching" : "");
return 0;
err_add_bus:
gpio_free(pdata->scl_pin);
err_request_scl:
gpio_free(pdata->sda_pin);
err_request_sda:
return ret;
}
static struct driver_d i2c_gpio_driver = {
.name = "i2c-gpio",
.probe = i2c_gpio_probe,
};
static int __init i2c_gpio_init(void)
{
return platform_driver_register(&i2c_gpio_driver);
}
device_initcall(i2c_gpio_init);

38
include/i2c/i2c-gpio.h Normal file
View File

@ -0,0 +1,38 @@
/*
* i2c-gpio interface to platform code
*
* Copyright (C) 2007 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _LINUX_I2C_GPIO_H
#define _LINUX_I2C_GPIO_H
/**
* struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
* @sda_pin: GPIO pin ID to use for SDA
* @scl_pin: GPIO pin ID to use for SCL
* @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
* @timeout_ms: clock stretching timeout in ms. If the slave keeps
* SCL low for longer than this, the transfer will time out.
* @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
* isn't actively driven high when setting the output value high.
* gpio_get_value() must return the actual pin state even if the
* pin is configured as an output.
* @scl_is_open_drain: SCL is set up as open drain. Same requirements
* as for sda_is_open_drain apply.
* @scl_is_output_only: SCL output drivers cannot be turned off.
*/
struct i2c_gpio_platform_data {
unsigned int sda_pin;
unsigned int scl_pin;
int udelay;
int timeout_ms;
unsigned int sda_is_open_drain:1;
unsigned int scl_is_open_drain:1;
unsigned int scl_is_output_only:1;
};
#endif /* _LINUX_I2C_GPIO_H */