From 9e2183e25592447f733803f06687cd6f86d835c7 Mon Sep 17 00:00:00 2001 From: "charles.park" Date: Fri, 20 Jan 2017 18:33:15 +0900 Subject: [PATCH] ODROID-XU4: add IO Expension Board dirver. Change-Id: Iea4b8c3a6eb7b99d2f94edd2a2822b9655fe1d7d --- drivers/hardkernel/Kconfig | 8 + drivers/hardkernel/Makefile | 3 +- drivers/hardkernel/ioboard-bh1780.c | 317 +++++++++++++++++++++ drivers/hardkernel/ioboard-bmp180.c | 421 ++++++++++++++++++++++++++++ 4 files changed, 748 insertions(+), 1 deletion(-) create mode 100755 drivers/hardkernel/ioboard-bh1780.c create mode 100755 drivers/hardkernel/ioboard-bmp180.c diff --git a/drivers/hardkernel/Kconfig b/drivers/hardkernel/Kconfig index 775ef0cb7ec8..2ed85d4bb6b6 100755 --- a/drivers/hardkernel/Kconfig +++ b/drivers/hardkernel/Kconfig @@ -6,5 +6,13 @@ config ODROID_EXYNOS5_SP default n ---help--- Enables the INA231 Sensor on ODROID-EXYNOS5 + +config ODROID_EXYNOS5_IOBOARD + tristate "Enable the ODROID EXYNOS5 IOBOARD Drivers" + depends on ARCH_EXYNOS5 + default n + ---help--- + Enables the IO-Expansion BOARD Drivers + endmenu diff --git a/drivers/hardkernel/Makefile b/drivers/hardkernel/Makefile index 7126abac41aa..4f1d26032b32 100755 --- a/drivers/hardkernel/Makefile +++ b/drivers/hardkernel/Makefile @@ -1,2 +1,3 @@ -obj-$(CONFIG_ODROID_EXYNOS5_SP) += ina231-sensor.o +obj-$(CONFIG_ODROID_EXYNOS5_SP) += ina231-sensor.o ina231-sensor-y := ina231-misc.o ina231-sysfs.o ina231-i2c.o +obj-$(CONFIG_ODROID_EXYNOS5_IOBOARD) += ioboard-bh1780.o ioboard-bmp180.o \ No newline at end of file diff --git a/drivers/hardkernel/ioboard-bh1780.c b/drivers/hardkernel/ioboard-bh1780.c new file mode 100755 index 000000000000..fa4bb6b89003 --- /dev/null +++ b/drivers/hardkernel/ioboard-bh1780.c @@ -0,0 +1,317 @@ +//[*]------------------------------------------------------------------------[*] +// +// +// ODROID IOBOARD Board : IOBOARD BH1780 Sensor driver (charles.park) +// 2013.08.28 +// +// +//[*]------------------------------------------------------------------------[*] +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//[*]------------------------------------------------------------------------[*] +#define BH1780_NAME "ioboard-bh1780" + +#define BH1780_WORK_PERIOD msecs_to_jiffies(100) + +//[*]------------------------------------------------------------------------[*] +// +// 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; + /* lx : 0 ~ 65535 */ + unsigned short light_data; +}; + +//[*]------------------------------------------------------------------------[*] +// +// 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_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 (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, 0660, bh1780_data_show, NULL); +static DEVICE_ATTR(enable, 0660, 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; + } + + i2c_set_clientdata(client, bh1780); + + dev_set_drvdata(&client->dev, bh1780); + + bh1780->client = client; + + /* detect and init hardware */ + if ((err = bh1780_detect(client, NULL)) != 0) + goto error; + + 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 (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__); + 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 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, + .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"); + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ioboard-bmp180.c b/drivers/hardkernel/ioboard-bmp180.c new file mode 100755 index 000000000000..be1824f5703d --- /dev/null +++ b/drivers/hardkernel/ioboard-bmp180.c @@ -0,0 +1,421 @@ +//[*]------------------------------------------------------------------------[*] +// +// +// ODROID IOBOARD Board : IOBOARD BMP180 Sensor driver (charles.park) +// 2013.08.28 +// +// +//[*]------------------------------------------------------------------------[*] +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//[*]------------------------------------------------------------------------[*] +#define BMP180_DRV_NAME "ioboard-bmp180" + +#define BMP180_WORK_PERIOD msecs_to_jiffies(100) + +#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 (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]); + bmp180->eeprom_vals.B2 = be16_to_cpu(buf[7]); + bmp180->eeprom_vals.MB = be16_to_cpu(buf[8]); + bmp180->eeprom_vals.MC = be16_to_cpu(buf[9]); + bmp180->eeprom_vals.MD = be16_to_cpu(buf[10]); + return 0; +} + +//[*]------------------------------------------------------------------------[*] +// +// sysfs device attributes +// +//[*]------------------------------------------------------------------------[*] +static ssize_t bmp180_pressure_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmp180_data *bmp180 = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", bmp180->pressure); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t bmp180_temperature_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmp180_data *bmp180 = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", bmp180->temperature); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t bmp180_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmp180_data *bmp180 = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", bmp180->enabled); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t bmp180_enable_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct bmp180_data *bmp180 = dev_get_drvdata(dev); + unsigned int val; + + if (!(sscanf(buf, "%d\n", &val))) + return -EINVAL; + + val = (val > 0) ? 1 : 0; + + if (bmp180->enabled != val) { + bmp180->enabled = val; + if (bmp180->enabled) + schedule_delayed_work(&bmp180->work, BMP180_WORK_PERIOD); + } + + return count; +} + +//[*]------------------------------------------------------------------------[*] +static DEVICE_ATTR(pressure, 0660, bmp180_pressure_show, NULL); +static DEVICE_ATTR(temperature, 0660, bmp180_temperature_show, NULL); +static DEVICE_ATTR(enable, 0660, bmp180_enable_show, bmp180_enable_set); + +static struct attribute *bmp180_attributes[] = { + &dev_attr_pressure.attr, + &dev_attr_temperature.attr, + &dev_attr_enable.attr, + NULL +}; + +static struct attribute_group bmp180_attribute_group = { + .attrs = bmp180_attributes +}; + +//[*]------------------------------------------------------------------------[*] +static int bmp180_detect(struct i2c_client *client) +{ + unsigned char id; + + if (bmp180_i2c_read(client, BMP180_ID_REG, &id, sizeof(id))) + return -1; + + if (id != BMP180_ID) + return -1; + + return 0; +} + +//[*]------------------------------------------------------------------------[*] +static int bmp180_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct bmp180_data *bmp180; + int err=0; + + bmp180 = kzalloc(sizeof(struct bmp180_data), GFP_KERNEL); + if (!bmp180) { + pr_err("%s: failed to allocate memory for module\n", __func__); + return -ENOMEM; + } + + i2c_set_clientdata(client, bmp180); + + dev_set_drvdata(&client->dev, bmp180); + + bmp180->client = client; + + if (bmp180_detect(client) < 0) + goto error; + + err = bmp180_read_store_eeprom_val(bmp180); + + if (err) { + pr_err("%s: Reading the EEPROM failed\n", __func__); + err = -ENODEV; + goto error; + } + + INIT_DELAYED_WORK(&bmp180->work, bmp180_work_func); + + if(bmp180->enabled) schedule_delayed_work(&bmp180->work, BMP180_WORK_PERIOD); + + if ((err = sysfs_create_group(&client->dev.kobj, &bmp180_attribute_group)) < 0) + goto error; + + printk("\n=================== ioboard_%s ===================\n\n", __func__); + + return 0; + +error: + printk("\n=================== ioboard_%s FAIL! ===================\n\n", __func__); + + return err; +} + +//[*]------------------------------------------------------------------------[*] +static int bmp180_remove(struct i2c_client *client) +{ + /* TO DO: revisit ordering here once _probe order is finalized */ + struct bmp180_data *bmp180 = i2c_get_clientdata(client); + + if (bmp180->enabled) + cancel_delayed_work_sync(&bmp180->work); + + sysfs_remove_group(&client->dev.kobj, &bmp180_attribute_group); + + kfree(bmp180); + return 0; +} + +//[*]------------------------------------------------------------------------[*] +static const struct i2c_device_id bmp180_id[] = { + {BMP180_DRV_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, bmp180_id); + +//[*]------------------------------------------------------------------------[*] +struct i2c_driver bmp180_driver = { + .driver = { + .name = BMP180_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = bmp180_probe, + .remove = bmp180_remove, + .id_table = bmp180_id, +}; + +//[*]------------------------------------------------------------------------[*] +static int __init bmp180_init(void) +{ + return i2c_add_driver(&bmp180_driver); +} +module_init(bmp180_init); + +//[*]------------------------------------------------------------------------[*] +static void __exit bmp180_exit(void) +{ + i2c_del_driver(&bmp180_driver); +} +module_exit(bmp180_exit); + +//[*]------------------------------------------------------------------------[*] +MODULE_DESCRIPTION("IOBOARD driver for ODROIDXU-Dev board"); +MODULE_AUTHOR("Hard-Kernel"); +MODULE_LICENSE("GPL"); + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*]