Commit 29d6a7a5 authored by charles's avatar charles

IO Expansion board test drivers added.

parent 3b62af1d
......@@ -7,5 +7,19 @@ config ODROID_EXYNOS5_SP
---help---
Enables the INA231 Sensor on ODROID-EXYNOS5
config ODROID_EXYNOS5_IOBOARD
bool "Enable the ODROID EXYNOS5 IOBOARD Drivers"
depends on MACH_ODROIDXU3
default n
---help---
Enables the IOBOARD Drivers
config ODROID_EXYNOS5_IOBOARD_DEBUG
bool "Enable the ODROIDXU IOBOARD DEBUG Enable"
depends on ODROID_EXYNOS5_IOBOARD
default n
---help---
Enables the IOBOARD Debug Message
endmenu
obj-$(CONFIG_ODROID_EXYNOS5_SP) += ina231-i2c.o ina231-misc.o ina231-sysfs.o
obj-$(CONFIG_ODROID_EXYNOS5_IOBOARD) += ioboard-bh1780.o ioboard-bmp180.o
obj-$(CONFIG_ODROID_EXYNOS5_IOBOARD) += ioboard-keyled.o ioboard-spi.o ioboard-spi-misc.o
//[*]--------------------------------------------------------------------------------------------------[*]
//
//
//
// ODROID IOBOARD Board : IOBOARD BH1780 Sensor driver (charles.park)
// 2013.08.28
//
//
//[*]--------------------------------------------------------------------------------------------------[*]
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
//[*]--------------------------------------------------------------------------------------------------[*]
#define BH1780_NAME "ioboard-bh1780"
#if defined(CONFIG_ODROID_EXYNOS5_IOBOARD_DEBUG)
#define BH1780_WORK_PERIOD msecs_to_jiffies(1000) // 1000 ms
#else
#define BH1780_WORK_PERIOD msecs_to_jiffies(100) // 100 ms
#endif
//[*]--------------------------------------------------------------------------------------------------[*]
//
// Registers Define
//
//[*]--------------------------------------------------------------------------------------------------[*]
#define BH1780_CONTROL_REG 0x00
#define BH1780_POWER_UP 0x03
#define BH1780_POWER_DOWN 0x00
#define BH1780_PART_REV_REG 0x0A
#define BH1780_CHIP_ID_REG 0x0B
#define BH1780_CHIP_ID 0x01
#define BH1780_DATA_LOW_REG 0x0C
#define BH1780_DATA_HIGH_REG 0x0D
#define BH1780_DATA_CAL(high, low) ((high & 0xFF) << 8 | (low & 0xff))
#define BH1780_COMMAND_REG 0x80
#define BH1780_DATA_MIN 0
#define BH1780_DATA_MAX 0xFFFF
//[*]--------------------------------------------------------------------------------------------------[*]
//
// Driver private data
//
//[*]--------------------------------------------------------------------------------------------------[*]
struct bh1780_data {
struct i2c_client *client;
struct delayed_work work;
bool enabled;
unsigned short light_data; /* lx : 0 ~ 65535 */
};
//[*]--------------------------------------------------------------------------------------------------[*]
//
// Device dependant operations
//
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_power_up(struct bh1780_data *bh1780)
{
i2c_smbus_write_byte_data(bh1780->client, (BH1780_COMMAND_REG + BH1780_CONTROL_REG), BH1780_POWER_UP);
/* wait 200ms for wake-up time from sleep to operational mode */
msleep(200);
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_power_down(struct bh1780_data *bh1780)
{
i2c_smbus_write_byte_data(bh1780->client, (BH1780_COMMAND_REG + BH1780_CONTROL_REG), BH1780_POWER_DOWN);
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_measure(struct bh1780_data *bh1780)
{
struct i2c_client *client = bh1780->client;
int low_data, high_data;
/* read light sensor data */
if(i2c_smbus_write_byte(bh1780->client, (BH1780_COMMAND_REG + BH1780_DATA_LOW_REG)) < 0) {
dev_err(&client->dev, "I2C write byte error: data=0x%02x\n", (BH1780_COMMAND_REG + BH1780_DATA_LOW_REG));
goto err;
}
if((low_data = i2c_smbus_read_byte(client)) < 0) {
dev_err(&client->dev, "I2C read byte error\n");
goto err;
}
if(i2c_smbus_write_byte(bh1780->client, (BH1780_COMMAND_REG + BH1780_DATA_HIGH_REG)) < 0) {
dev_err(&client->dev, "I2C write byte error: data=0x%02x\n", (BH1780_COMMAND_REG + BH1780_DATA_HIGH_REG));
goto err;
}
if((high_data = i2c_smbus_read_byte(client)) < 0) {
dev_err(&client->dev, "I2C read byte error\n");
goto err;
}
bh1780->light_data = BH1780_DATA_CAL(high_data, low_data);
err:
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static void bh1780_work_func(struct work_struct *work)
{
struct bh1780_data *bh1780 = container_of((struct delayed_work *)work,
struct bh1780_data, work);
bh1780_measure(bh1780);
#if defined(CONFIG_ODROID_EXYNOS5_IOBOARD_DEBUG)
printk("===> %s : %d \n", __func__, bh1780->light_data);
#endif
if(bh1780->enabled)
schedule_delayed_work(&bh1780->work, BH1780_WORK_PERIOD);
else
bh1780->light_data = 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
//
// sysfs device attributes
//
//[*]--------------------------------------------------------------------------------------------------[*]
static ssize_t bh1780_data_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct bh1780_data *bh1780 = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", bh1780->light_data);
}
//[*]--------------------------------------------------------------------------------------------------[*]
static ssize_t bh1780_enable_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct bh1780_data *bh1780 = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", bh1780->enabled);
}
//[*]--------------------------------------------------------------------------------------------------[*]
static ssize_t bh1780_enable_set (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
unsigned int val;
struct bh1780_data *bh1780 = dev_get_drvdata(dev);
if(!(sscanf(buf, "%d\n", &val))) return -EINVAL;
val = (val > 0) ? 1 : 0;
if(bh1780->enabled != val) {
bh1780->enabled = val;
if(bh1780->enabled) schedule_delayed_work(&bh1780->work, BH1780_WORK_PERIOD);
}
return count;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static DEVICE_ATTR(lux, S_IRWXUGO, bh1780_data_show, NULL);
static DEVICE_ATTR(enable, S_IRWXUGO, bh1780_enable_show, bh1780_enable_set);
static struct attribute *bh1780_attributes[] = {
&dev_attr_lux.attr,
&dev_attr_enable.attr,
NULL
};
static struct attribute_group bh1780_attribute_group = {
.attrs = bh1780_attributes
};
//[*]--------------------------------------------------------------------------------------------------[*]
//
// I2C client
//
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_detect(struct i2c_client *client, struct i2c_board_info *info)
{
int id;
if(i2c_smbus_write_byte(client, (BH1780_COMMAND_REG + BH1780_CHIP_ID_REG)) < 0) {
dev_err(&client->dev, "I2C write byte error: data=0x%02x\n", (BH1780_COMMAND_REG + BH1780_CHIP_ID_REG));
return -ENODEV;
}
if((id = i2c_smbus_read_byte(client)) < 0) {
dev_err(&client->dev, "I2C read byte error\n");
return -ENODEV;
}
if ((id & 0xFF) != BH1780_CHIP_ID) return -ENODEV;
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct bh1780_data *bh1780;
int err;
/* setup private data */
bh1780 = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL);
if (!bh1780) {
pr_err("%s: failed to allocate memory for module\n", __func__);
return -ENOMEM;
}
/* detect and init hardware */
if ((err = bh1780_detect(client, NULL)) != 0) goto error;
i2c_set_clientdata(client, bh1780);
dev_set_drvdata(&client->dev, bh1780);
bh1780->client = client;
if((err = i2c_smbus_write_byte(bh1780->client, (BH1780_COMMAND_REG + BH1780_PART_REV_REG))) < 0) {
dev_err(&client->dev, "I2C write byte error: data=0x%02x\n", (BH1780_COMMAND_REG + BH1780_PART_REV_REG));
goto error;
}
if((err = i2c_smbus_read_byte(client)) < 0) {
dev_err(&client->dev, "I2C read byte error\n");
goto error;
}
dev_info(&client->dev, "%s found\n", id->name);
dev_info(&client->dev, "part number=%d, rev=%d\n", ((err >> 4) & 0x0F), (err & 0x0F));
bh1780_power_up(bh1780);
INIT_DELAYED_WORK(&bh1780->work, bh1780_work_func);
#if defined(CONFIG_ODROID_EXYNOS5_IOBOARD_DEBUG)
bh1780->enabled = 1;
#endif
if(bh1780->enabled) schedule_delayed_work(&bh1780->work, BH1780_WORK_PERIOD);
if ((err = sysfs_create_group(&client->dev.kobj, &bh1780_attribute_group)) < 0) goto error;
printk("\n=================== ioboard_%s ===================\n\n", __func__);
return 0;
error:
printk("\n=================== ioboard_%s FAIL! ===================\n\n", __func__);
kfree(bh1780);
return err;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_remove(struct i2c_client *client)
{
struct bh1780_data *bh1780 = i2c_get_clientdata(client);
if(bh1780->enabled) cancel_delayed_work_sync(&bh1780->work);
sysfs_remove_group(&client->dev.kobj, &bh1780_attribute_group);
kfree(bh1780);
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct bh1780_data *bh1780 = i2c_get_clientdata(client);
if(bh1780->enabled) cancel_delayed_work_sync(&bh1780->work);
bh1780_power_down(bh1780);
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bh1780_resume(struct i2c_client *client)
{
struct bh1780_data *bh1780 = i2c_get_clientdata(client);
bh1780_power_up(bh1780);
if(bh1780->enabled) schedule_delayed_work(&bh1780->work, BH1780_WORK_PERIOD);
return 0;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static const struct i2c_device_id bh1780_id[] = {
{BH1780_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(i2c, bh1780_id);
//[*]--------------------------------------------------------------------------------------------------[*]
struct i2c_driver bh1780_driver ={
.driver = {
.name = BH1780_NAME,
.owner = THIS_MODULE,
},
.probe = bh1780_probe,
.remove = bh1780_remove,
.suspend = bh1780_suspend,
.resume = bh1780_resume,
.id_table = bh1780_id,
};
//[*]--------------------------------------------------------------------------------------------------[*]
/*
* Module init and exit
*/
//[*]--------------------------------------------------------------------------------------------------[*]
static int __init bh1780_init(void)
{
return i2c_add_driver(&bh1780_driver);
}
module_init(bh1780_init);
//[*]--------------------------------------------------------------------------------------------------[*]
static void __exit bh1780_exit(void)
{
i2c_del_driver(&bh1780_driver);
}
module_exit(bh1780_exit);
//[*]--------------------------------------------------------------------------------------------------[*]
MODULE_DESCRIPTION("IOBOARD driver for ODROIDXU-Dev board");
MODULE_AUTHOR("Hard-Kernel");
MODULE_LICENSE("GPL");
//[*]--------------------------------------------------------------------------------------------------[*]
//[*]--------------------------------------------------------------------------------------------------[*]
//[*]--------------------------------------------------------------------------------------------------[*]
//
//
//
// ODROID IOBOARD Board : IOBOARD BMP180 Sensor driver (charles.park)
// 2013.08.28
//
//
//[*]--------------------------------------------------------------------------------------------------[*]
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
//[*]--------------------------------------------------------------------------------------------------[*]
#define BMP180_DRV_NAME "ioboard-bmp180"
#if defined(CONFIG_ODROID_EXYNOS5_IOBOARD_DEBUG)
#define BMP180_WORK_PERIOD msecs_to_jiffies(1000) // 1000 ms
#else
#define BMP180_WORK_PERIOD msecs_to_jiffies(100) // 100 ms
#endif
#define BMP180_OVERSAMPLE 3
#define BMP180_ID 0x55
//[*]--------------------------------------------------------------------------------------------------[*]
// Register definitions
//[*]--------------------------------------------------------------------------------------------------[*]
#define BMP180_ID_REG 0xD0
#define BMP180_TAKE_MEAS_REG 0xf4
#define BMP180_READ_MEAS_REG_U 0xf6
#define BMP180_READ_MEAS_REG_L 0xf7
#define BMP180_READ_MEAS_REG_XL 0xf8
//[*]--------------------------------------------------------------------------------------------------[*]
/*
* Bytes defined by the spec to take measurements
* Temperature will take 4.5ms before EOC
*/
#define BMP180_MEAS_TEMP 0x2e
/* 4.5ms wait for measurement */
#define BMP180_MEAS_PRESS_OVERSAMP_0 0x34
/* 7.5ms wait for measurement */
#define BMP180_MEAS_PRESS_OVERSAMP_1 0x74
/* 13.5ms wait for measurement */
#define BMP180_MEAS_PRESS_OVERSAMP_2 0xb4
/* 25.5ms wait for measurement */
#define BMP180_MEAS_PRESS_OVERSAMP_3 0xf4
/*
* EEPROM registers each is a two byte value so there is
* an upper byte and a lower byte
*/
#define BMP180_EEPROM_AC1_U 0xaa
//[*]--------------------------------------------------------------------------------------------------[*]
struct bmp180_eeprom_data {
s16 AC1, AC2, AC3;
u16 AC4, AC5, AC6;
s16 B1, B2;
s16 MB, MC, MD;
};
//[*]--------------------------------------------------------------------------------------------------[*]
struct bmp180_data {
struct i2c_client *client;
struct delayed_work work;
bool enabled;
struct bmp180_eeprom_data eeprom_vals;
unsigned int pressure;
unsigned int temperature;
};
//[*]--------------------------------------------------------------------------------------------------[*]
static int bmp180_i2c_read(const struct i2c_client *client, u8 cmd, u8 *buf, int len)
{
int err;
err = i2c_smbus_read_i2c_block_data(client, cmd, len, buf);
if (err == len) return 0;
return err;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bmp180_i2c_write(const struct i2c_client *client, u8 cmd, u8 data)
{
int err;
err = i2c_smbus_write_byte_data(client, cmd, data);
if (!err) return 0;
return err;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bmp180_get_raw_temperature(struct bmp180_data *bmp180, u16 *raw_temperature)
{
int err;
u16 buf;
err = bmp180_i2c_write(bmp180->client, BMP180_TAKE_MEAS_REG, BMP180_MEAS_TEMP);
if (err) {
pr_err("%s: can't write BMP180_TAKE_MEAS_REG\n", __func__);
return err;
}
msleep(5);
err = bmp180_i2c_read(bmp180->client, BMP180_READ_MEAS_REG_U, (u8 *)&buf, 2);
if (err) {
pr_err("%s: Fail to read uncompensated temperature\n", __func__);
return err;
}
*raw_temperature = be16_to_cpu(buf);
return err;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bmp180_get_raw_pressure(struct bmp180_data *bmp180, u32 *raw_pressure)
{
int err;
u32 buf = 0;
err = bmp180_i2c_write(bmp180->client, BMP180_TAKE_MEAS_REG,
BMP180_MEAS_PRESS_OVERSAMP_0 | (BMP180_OVERSAMPLE << 6));
if (err) {
pr_err("%s: can't write BMP180_TAKE_MEAS_REG\n", __func__);
return err;
}
msleep(2+(3 << BMP180_OVERSAMPLE));
err = bmp180_i2c_read(bmp180->client, BMP180_READ_MEAS_REG_U, ((u8 *)&buf)+1, 3);
if (err) {
pr_err("%s: Fail to read uncompensated pressure\n", __func__);
return err;
}
*raw_pressure = be32_to_cpu(buf);
*raw_pressure >>= (8 - BMP180_OVERSAMPLE);
return err;
}
//[*]--------------------------------------------------------------------------------------------------[*]
static void bmp180_work_func(struct work_struct *work)
{
u16 raw_temperature;
u32 raw_pressure;
long x1, x2, x3, b3, b5, b6;
unsigned long b4, b7;
long p;
struct bmp180_data *bmp180 = container_of((struct delayed_work *)work,
struct bmp180_data, work);
if (bmp180_get_raw_temperature(bmp180, &raw_temperature)) {
pr_err("%s: can't read uncompensated temperature\n", __func__);
return;
}
if (bmp180_get_raw_pressure(bmp180, &raw_pressure)) {
pr_err("%s: Fail to read uncompensated pressure\n", __func__);
return;
}
x1 = ((raw_temperature - bmp180->eeprom_vals.AC6) *
bmp180->eeprom_vals.AC5) >> 15;
x2 = (bmp180->eeprom_vals.MC << 11) /
(x1 + bmp180->eeprom_vals.MD);
b5 = x1 + x2;
bmp180->temperature = (x1+x2+8) >> 4;
b6 = (b5 - 4000);
x1 = (bmp180->eeprom_vals.B2 * ((b6 * b6) >> 12)) >> 11;
x2 = (bmp180->eeprom_vals.AC2 * b6) >> 11;
x3 = x1 + x2;
b3 = (((((long)bmp180->eeprom_vals.AC1) * 4 +
x3) << BMP180_OVERSAMPLE) + 2) >> 2;
x1 = (bmp180->eeprom_vals.AC3 * b6) >> 13;
x2 = (bmp180->eeprom_vals.B1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (bmp180->eeprom_vals.AC4 *
(unsigned long)(x3 + 32768)) >> 15;
b7 = ((unsigned long)raw_pressure - b3) *
(50000 >> BMP180_OVERSAMPLE);
if (b7 < 0x80000000)
p = (b7 * 2) / b4;
else
p = (b7 / b4) * 2;
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
bmp180->pressure = p + ((x1 + x2 + 3791) >> 4);
#if defined(CONFIG_ODROID_EXYNOS5_IOBOARD_DEBUG)
printk("===> %s : %d %d\n", __func__, bmp180->pressure, bmp180->temperature);
#endif
if(bmp180->enabled)
schedule_delayed_work(&bmp180->work, BMP180_WORK_PERIOD);
else {
bmp180->pressure = 0;
bmp180->temperature = 0;
}
}
//[*]--------------------------------------------------------------------------------------------------[*]
static int bmp180_read_store_eeprom_val(struct bmp180_data *bmp180)
{
int err;
u16 buf[11];
err = bmp180_i2c_read(bmp180->client, BMP180_EEPROM_AC1_U, (u8 *)buf, 22);
if (err) {
pr_err("%s: Cannot read EEPROM values\n", __func__);
return err;
}
bmp180->eeprom_vals.AC1 = be16_to_cpu(buf[0]);
bmp180->eeprom_vals.AC2 = be16_to_cpu(buf[1]);
bmp180->eeprom_vals.AC3 = be16_to_cpu(buf[2]);
bmp180->eeprom_vals.AC4 = be16_to_cpu(buf[3]);
bmp180->eeprom_vals.AC5 = be16_to_cpu(buf[4]);
bmp180->eeprom_vals.AC6 = be16_to_cpu(buf[5]);
bmp180->eeprom_vals.B1 = be16_to_cpu(buf[6]);