diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3.dts b/arch/arm/boot/dts/exynos5422-odroidxu3.dts old mode 100644 new mode 100755 index f138e562578e..6ac262a30511 --- a/arch/arm/boot/dts/exynos5422-odroidxu3.dts +++ b/arch/arm/boot/dts/exynos5422-odroidxu3.dts @@ -20,35 +20,60 @@ compatible = "hardkernel,odroid-xu3", "samsung,exynos5800", "samsung,exynos5"; }; +/* + i2c0 INA231 Sensors + + - drivers/hardkernel/ina231.h + + config = INA231_CONFIG(VSH_CT(eVSH_CT_8244uS) | \ + VBUS_CT(eVBUS_CT_8244uS) | \ + AVG_BIT(eAVG_16) | \ + eSHUNT_BUS_VOLT_CONTINUOUS), + update_period = CONVERSION_DELAY(eVSH_CON_8244uS, eVBUS_CON_8244uS, eAVG_CON_16), +*/ + &i2c_0 { status = "okay"; - - /* A15 cluster: VDD_ARM */ - ina231@40 { - compatible = "ti,ina231"; + clock-frequency = <400000>; + ina231@40 { + compatible = "hardkernel,INA231"; reg = <0x40>; - shunt-resistor = <10000>; + sensor-name = "sensor_arm"; + enable = <0>; + max_A = <9>; + shunt_R_mohm = <10>; + config = <0x45FF>; + update_period = <263808>; }; - - /* memory: VDD_MEM */ - ina231@41 { - compatible = "ti,ina231"; + ina231@41 { + compatible = "hardkernel,INA231"; reg = <0x41>; - shunt-resistor = <10000>; + sensor-name = "sensor_mem"; + enable = <0>; + max_A = <3>; + shunt_R_mohm = <10>; + config = <0x45FF>; + update_period = <263808>; }; - - /* GPU: VDD_G3D */ - ina231@44 { - compatible = "ti,ina231"; + ina231@44 { + compatible = "hardkernel,INA231"; reg = <0x44>; - shunt-resistor = <10000>; + sensor-name = "sensor_g3d"; + enable = <0>; + max_A = <5>; + shunt_R_mohm = <10>; + config = <0x45FF>; + update_period = <263808>; }; - - /* A7 cluster: VDD_KFC */ - ina231@45 { - compatible = "ti,ina231"; + ina231@45 { + compatible = "hardkernel,INA231"; reg = <0x45>; - shunt-resistor = <10000>; + sensor-name = "sensor_kfc"; + enable = <0>; + max_A = <2>; + shunt_R_mohm = <10>; + config = <0x45FF>; + update_period = <263808>; }; }; diff --git a/drivers/Kconfig b/drivers/Kconfig index e1e2066cecdb..2a27246be91f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig" source "drivers/fpga/Kconfig" +# Hardkernel Specific drivers +source "drivers/hardkernel/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 194d20bee7dc..9c1f7870ebd3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -173,3 +173,6 @@ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ + +# Hardkernel Specific drivers +obj-y += hardkernel/ diff --git a/drivers/hardkernel/Kconfig b/drivers/hardkernel/Kconfig new file mode 100755 index 000000000000..775ef0cb7ec8 --- /dev/null +++ b/drivers/hardkernel/Kconfig @@ -0,0 +1,10 @@ +menu "ODROID Specific Hardware" + +config ODROID_EXYNOS5_SP + tristate "Enable the INA231 Sensor on ODROID-EXYNOS5" + depends on I2C && ARCH_EXYNOS5 + default n + ---help--- + Enables the INA231 Sensor on ODROID-EXYNOS5 +endmenu + diff --git a/drivers/hardkernel/Makefile b/drivers/hardkernel/Makefile new file mode 100755 index 000000000000..7126abac41aa --- /dev/null +++ b/drivers/hardkernel/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ODROID_EXYNOS5_SP) += ina231-sensor.o +ina231-sensor-y := ina231-misc.o ina231-sysfs.o ina231-i2c.o diff --git a/drivers/hardkernel/ina231-i2c.c b/drivers/hardkernel/ina231-i2c.c new file mode 100755 index 000000000000..db79ea6e57ac --- /dev/null +++ b/drivers/hardkernel/ina231-i2c.c @@ -0,0 +1,341 @@ +//[*]------------------------------------------------------------------------[*] +// +// +// I2C INA231(Sensor) driver +// 2013.07.17 +// +// +//[*]------------------------------------------------------------------------[*] +#include +#include +#include + +//[*]------------------------------------------------------------------------[*] +#include "ina231.h" +#include "ina231-misc.h" +#include "ina231-sysfs.h" + +//#define DEBUG_INA231 +//[*]------------------------------------------------------------------------[*] +// +// function prototype +// +//[*]------------------------------------------------------------------------[*] +static void __exit ina231_i2c_exit (void); +static int __init ina231_i2c_init (void); +static int ina231_i2c_remove (struct i2c_client *client); +static int ina231_i2c_probe (struct i2c_client *client, const struct i2c_device_id *id); +static void ina231_work (struct work_struct *work); +int ina231_i2c_read (struct i2c_client *client, unsigned char cmd); +int ina231_i2c_write (struct i2c_client *client, unsigned char cmd, unsigned short data); +void ina231_i2c_enable (struct ina231_sensor *sensor); + +static enum hrtimer_restart ina231_timer (struct hrtimer *timer); + +//[*]------------------------------------------------------------------------[*] +int ina231_i2c_read(struct i2c_client *client, unsigned char cmd) +{ + struct i2c_msg msg[2]; + int ret; + + unsigned char buf[2]; + + memset(msg, 0x00, sizeof(msg)); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &cmd; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = &buf[0]; + + if ((ret = i2c_transfer(client->adapter, msg, 2)) != 2) { + dev_err(&client->dev, "I2C read error: (%d) reg: 0x%X \n", ret, cmd); + return -EIO; + } + + ret = ((buf[0] << 8) | buf[1]) & 0xFFFF; + return ret; +} + +//[*]------------------------------------------------------------------------[*] +int ina231_i2c_write(struct i2c_client *client, unsigned char cmd, unsigned short data) +{ + int ret; + unsigned char block_data[3]; + + memset(block_data, 0x00, sizeof(block_data)); + + block_data[0] = cmd; + block_data[1] = (data >> 8) & 0xFF; + block_data[2] = (data ) & 0xFF; + + if ((ret = i2c_master_send(client, block_data, 3)) < 0) { + dev_err(&client->dev, "I2C write error: (%d) reg: 0x%X \n", ret, cmd); + return ret; + } + + return ret; +} + +//[*]------------------------------------------------------------------------[*] +void ina231_i2c_enable(struct ina231_sensor *sensor) +{ + hrtimer_start(&sensor->timer, + ktime_set(sensor->timer_sec, sensor->timer_nsec), + HRTIMER_MODE_REL); +} + +//[*]------------------------------------------------------------------------[*] +static void ina231_work (struct work_struct *work) +{ + struct ina231_sensor *sensor = container_of(work, struct ina231_sensor, work); + + if (sensor->pd->enable) { + sensor->reg_bus_volt = ina231_i2c_read(sensor->client, REG_BUS_VOLT); + sensor->reg_current = ina231_i2c_read(sensor->client, REG_CURRENT ); + + mutex_lock(&sensor->mutex); + sensor->cur_uV = sensor->reg_bus_volt * FIX_uV_LSB; + sensor->cur_uA = sensor->reg_current * sensor->cur_lsb_uA; + sensor->cur_uW = (sensor->cur_uV / 1000 ) * (sensor->cur_uA / 1000); + + if ((sensor->cur_uV > sensor->max_uV) || (sensor->cur_uA > sensor->cur_uA)) { + sensor->max_uV = sensor->cur_uV; + sensor->max_uA = sensor->cur_uA; + sensor->max_uW = sensor->cur_uW; + } + mutex_unlock(&sensor->mutex); + } + else { + sensor->cur_uV = 0; + sensor->cur_uA = 0; + sensor->cur_uW = 0; + } + +#if defined(DEBUG_INA231) + printk("%s : BUS Voltage = %06d uV, %1d.%06d V\n", + sensor->pd->name, sensor->cur_uV, + sensor->cur_uV/1000000, + sensor->cur_uV%1000000); + printk("%s : Curent = %06d uA, %1d.%06d A\n", + sensor->pd->name, sensor->cur_uA, + sensor->cur_uA/1000000, + sensor->cur_uA%1000000); + printk("%s : Powert = %06d uW, %1d.%06d W\n", + sensor->pd->name, sensor->cur_uW, + sensor->cur_uW/1000000, + sensor->cur_uW%1000000); +#endif +} + +//[*]------------------------------------------------------------------------[*] +static enum hrtimer_restart ina231_timer(struct hrtimer *timer) +{ + struct ina231_sensor *sensor = container_of(timer, struct ina231_sensor, timer); + + queue_work(sensor->wq, &sensor->work); + + if (sensor->pd->enable) ina231_i2c_enable(sensor); + + return HRTIMER_NORESTART; +} + +//[*]------------------------------------------------------------------------[*] +#ifdef CONFIG_OF +static int ina231_i2c_dt_parse(struct i2c_client *client, struct ina231_sensor *sensor) +{ + struct device_node *sensor_np = client->dev.of_node; + const char *sensor_name; + unsigned int rdata; + + if (!(sensor->pd = devm_kzalloc(&client->dev, sizeof(struct ina231_pd), + GFP_KERNEL))) { + dev_err(&client->dev, "INA231 Sensor platform data struct malloc error!\n"); + return -ENOMEM; + } + + if (of_property_read_string(sensor_np, "sensor-name", &sensor_name)) + return -1; + sensor->pd->name = (unsigned char *)sensor_name; + + if (of_property_read_u32(sensor_np, "enable", &rdata)) + return -1; + sensor->pd->enable = rdata; + + if (of_property_read_u32(sensor_np, "max_A", &rdata)) + return -1; + sensor->pd->max_A = rdata; + + if (of_property_read_u32(sensor_np, "shunt_R_mohm", &rdata)) + return -1; + sensor->pd->shunt_R_mohm = rdata; + + if (of_property_read_u32(sensor_np, "config", &rdata)) + return -1; + sensor->pd->config = rdata; + + if (of_property_read_u32(sensor_np, "update_period", &rdata)) + return -1; + sensor->pd->update_period = rdata; + + return 0; +} + +#else // CONFIG_OF +static int ina231_i2c_dt_parse(struct i2c_client *client, struct ina231_sensor *sensor) +{ + return 0; +} +#endif + +//[*]------------------------------------------------------------------------[*] +static int ina231_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int rc = 0; + struct ina231_sensor *sensor; + + if (!(sensor = devm_kzalloc(&client->dev, sizeof(struct ina231_sensor), + GFP_KERNEL))) { + dev_err(&client->dev, "INA231 Sensor struct malloc error!\n"); + return -ENOMEM; + } + // mutex init + mutex_init(&sensor->mutex); + + sensor->client = client; + + if (client->dev.of_node) { + if(ina231_i2c_dt_parse(client, sensor) < 0) + goto out; + } + else { + sensor->pd = client->dev.platform_data; + } + + i2c_set_clientdata(client, sensor); + + // Calculate current lsb value + sensor->cur_lsb_uA = sensor->pd->max_A * 1000000 / 32768; + // Calculate register value + sensor->reg_calibration = 5120000 / (sensor->cur_lsb_uA * sensor->pd->shunt_R_mohm); + + if ((rc = ina231_i2c_write(sensor->client, REG_CONFIG, sensor->pd->config)) < 0) + goto out; + if ((rc = ina231_i2c_write(sensor->client, REG_CALIBRATION, sensor->reg_calibration)) < 0) + goto out; + if ((rc = ina231_i2c_write(sensor->client, REG_ALERT_EN, 0x0000)) < 0) + goto out; + if ((rc = ina231_i2c_write(sensor->client, REG_ALERT_LIMIT, 0x0000)) < 0) + goto out; + + if ((rc = ina231_i2c_read(sensor->client, REG_CONFIG)) != sensor->pd->config) + goto out; + if ((rc = ina231_i2c_read(sensor->client, REG_CALIBRATION)) != sensor->reg_calibration ) + goto out; + if ((rc = ina231_i2c_read(sensor->client, REG_ALERT_EN)) != 0x0000) + goto out; + if ((rc = ina231_i2c_read(sensor->client, REG_ALERT_LIMIT)) != 0x0000) + goto out; + + // misc driver probe + if (ina231_misc_probe(sensor) < 0) + goto out; + + // sysfs probe + if (ina231_sysfs_create(&client->dev) < 0) + goto out; + + // timer run for sensor data receive + INIT_WORK(&sensor->work, ina231_work); + if ((sensor->wq = create_singlethread_workqueue("ina231_wq")) == NULL) goto out; + + hrtimer_init(&sensor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + sensor->timer_sec = sensor->pd->update_period / 1000000; + sensor->timer_nsec = sensor->pd->update_period % 1000000; + sensor->timer_nsec = sensor->timer_nsec * 1000; + sensor->timer.function = ina231_timer; + + if (sensor->pd->enable) ina231_i2c_enable(sensor); + + // display register message + rc = 0; + dev_info(&client->dev, "============= Probe INA231 : %s ============= \n", + sensor->pd->name); + dev_info(&client->dev, "SENSOR ENABLE : %s\n" , + sensor->pd->enable ? "true" : "false"); + dev_info(&client->dev, "REG CONFIG : 0x%04X\n" , + sensor->pd->config ); + dev_info(&client->dev, "REG CALIBRATION : 0x%04X\n" , + sensor->reg_calibration ); + dev_info(&client->dev, "SHUNT Resister : %d mOhm\n", + sensor->pd->shunt_R_mohm ); + dev_info(&client->dev, "MAX Current : %d A\n" , + sensor->pd->max_A ); + dev_info(&client->dev, "Current LSB uA : %d uA\n" , + sensor->cur_lsb_uA ); + dev_info(&client->dev, "Conversion Time : %d us\n" , + sensor->pd->update_period ); + dev_info(&client->dev, "=====================================================\n"); + + return 0; +out: + dev_err(&client->dev, "============= Probe INA231 Fail! : %s (0x%04X) ============= \n", + sensor->pd->name, rc); + + return rc; +} + +//[*]------------------------------------------------------------------------[*] +static int ina231_i2c_remove(struct i2c_client *client) +{ + struct ina231_sensor *sensor = dev_get_drvdata(&client->dev); + + // removed sysfs entry + ina231_sysfs_remove(&client->dev); + // removed misc drv + ina231_misc_remove(&client->dev); + // timer + if (sensor->pd->enable) hrtimer_cancel(&sensor->timer); + + return 0; +} + +//[*]------------------------------------------------------------------------[*] +static const struct i2c_device_id ina231_id[] = { + { INA231_I2C_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ina231_id); + +//[*]------------------------------------------------------------------------[*] +static struct i2c_driver ina231_i2c_driver = { + .driver = { + .name = INA231_I2C_NAME, + .owner = THIS_MODULE, + }, + .probe = ina231_i2c_probe, + .remove = ina231_i2c_remove, + .id_table = ina231_id, +}; + +//[*]------------------------------------------------------------------------[*] +static int __init ina231_i2c_init(void) +{ + return i2c_add_driver(&ina231_i2c_driver); +} +module_init(ina231_i2c_init); + +//[*]------------------------------------------------------------------------[*] +static void __exit ina231_i2c_exit(void) +{ + i2c_del_driver(&ina231_i2c_driver); +} +module_exit(ina231_i2c_exit); + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ina231-i2c.h b/drivers/hardkernel/ina231-i2c.h new file mode 100755 index 000000000000..083a8560ed87 --- /dev/null +++ b/drivers/hardkernel/ina231-i2c.h @@ -0,0 +1,24 @@ +//[*]--------------------------------------------------------------------------------------------------[*] +// +// +// +// I2C INA231(Sensor) driver +// 2013.07.17 +// +// +//[*]--------------------------------------------------------------------------------------------------[*] +#ifndef _INA231_I2C_H_ +#define _INA231_I2C_H_ + +//[*]--------------------------------------------------------------------------------------------------[*] +// extern function define +//[*]--------------------------------------------------------------------------------------------------[*] +extern void ina231_i2c_enable (struct ina231_sensor *sensor); +extern int ina231_i2c_read (struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len); +extern int ina231_i2c_write (struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len); + +//[*]--------------------------------------------------------------------------------------------------[*] +#endif /* _INA231_I2C_H_ */ + +//[*]--------------------------------------------------------------------------------------------------[*] +//[*]--------------------------------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ina231-misc.c b/drivers/hardkernel/ina231-misc.c new file mode 100755 index 000000000000..4cb25ec0848e --- /dev/null +++ b/drivers/hardkernel/ina231-misc.c @@ -0,0 +1,175 @@ +//[*]------------------------------------------------------------------------[*] +// +// +// I2C INA231(Sensor) driver +// 2013.07.17 +// +// +//[*]------------------------------------------------------------------------[*] +#include +#include +#include +#include +#include + +#include + +//[*]------------------------------------------------------------------------[*] +#include "ina231.h" +#include "ina231-i2c.h" +#include "ina231-misc.h" + +//[*]------------------------------------------------------------------------[*] +// Global Sensor struct (sensor struct save for ioctl) +//[*]------------------------------------------------------------------------[*] +struct global_sensor { + struct ina231_sensor *p; + struct list_head list; +}; + +LIST_HEAD(SensorList); + +//[*]------------------------------------------------------------------------[*] +// +// function prototype +// +//[*]------------------------------------------------------------------------[*] +static int ina231_misc_open (struct inode *inode, struct file *file); +static long ina231_misc_ioctl (struct file *file, unsigned int cmd, unsigned long arg); +void ina231_misc_remove (struct device *dev); +int ina231_misc_probe (struct ina231_sensor *sensor); + +//[*]------------------------------------------------------------------------[*] +static const struct file_operations ina231_misc_fops = { + .owner = THIS_MODULE, + .open = ina231_misc_open, + .unlocked_ioctl = ina231_misc_ioctl, +}; + +//[*]------------------------------------------------------------------------[*] +static long ina231_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ina231_sensor *sensor = (struct ina231_sensor *)file->private_data; + struct ina231_iocreg *iocreg = (struct ina231_iocreg *)arg; + + switch (cmd) { + // Get regisger + case INA231_IOCGREG: + iocreg->enable = sensor->pd->enable; + if (sensor->pd->enable) { + mutex_lock(&sensor->mutex); + iocreg->cur_uV = sensor->cur_uV; + iocreg->cur_uA = sensor->cur_uA; + iocreg->cur_uW = sensor->cur_uW; + mutex_unlock(&sensor->mutex); + } + else { + iocreg->cur_uV = 0; + iocreg->cur_uA = 0; + iocreg->cur_uW = 0; + } + break; + // Set status + case INA231_IOCSSTATUS: + if (sensor->pd->enable != iocreg->enable) { + sensor->pd->enable = iocreg->enable; + if (sensor->pd->enable) ina231_i2c_enable(sensor); + } + break; + // Set status + case INA231_IOCGSTATUS: + iocreg->enable = sensor->pd->enable; + memset(iocreg->name, 0x00, sizeof(iocreg->name)); + memcpy(iocreg->name, sensor->pd->name, sizeof(iocreg->name)); + break; + default : + printk("%s : unknown message!!\n", __func__); + break; + } + return 0; +} + +//[*]------------------------------------------------------------------------[*] +static int ina231_misc_open(struct inode *inode, struct file *file) +{ + struct global_sensor *gsensor; + struct list_head *list_head; + + printk("%s : %d\n", __func__, iminor(inode)); + + list_for_each(list_head, &SensorList) + { + gsensor = list_entry(list_head, struct global_sensor, list); + + if (gsensor->p->misc->minor == iminor(inode)) { + printk("find match sensor struct : name = %s\n", + gsensor->p->pd->name); + file->private_data = gsensor->p; + } + } + + return 0; +} + +//[*]------------------------------------------------------------------------[*] +int ina231_misc_probe(struct ina231_sensor *sensor) +{ + int rc; + struct miscdevice *pmisc; + struct global_sensor *gsensor; + + if (!(pmisc = devm_kzalloc(&sensor->client->dev, + sizeof(struct miscdevice), + GFP_KERNEL))) { + printk("INA231 Sensor misc struct malloc error!\n"); + return -ENOMEM; + } + + pmisc->minor = MISC_DYNAMIC_MINOR; + pmisc->name = sensor->pd->name; + pmisc->fops = &ina231_misc_fops; + pmisc->mode = S_IWUGO | S_IRUGO; + + sensor->misc = pmisc; + + if ((rc = misc_register(sensor->misc)) < 0) { + printk("%s : INA231 misc register fail!\n", __func__); + return rc; + } + + if(!(gsensor = (struct global_sensor *)devm_kzalloc(&sensor->client->dev, + sizeof(struct global_sensor), + GFP_KERNEL))) { + printk("%s : INA231 global sensor malloc error!\n", __func__); + return -ENOMEM; + } + else { + gsensor->p = sensor; + list_add(&gsensor->list, &SensorList); + } + + return 0; +} + +//[*]------------------------------------------------------------------------[*] +void ina231_misc_remove(struct device *dev) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + struct list_head *list_head; + struct global_sensor *gsensor; + + misc_deregister(sensor->misc); + + list_for_each(list_head, &SensorList) + { + gsensor = list_entry(list_head, struct global_sensor, list); + } +} + +//[*]------------------------------------------------------------------------[*] +MODULE_AUTHOR("HardKernel Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("INA231 Current Sensor Driver"); + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ina231-misc.h b/drivers/hardkernel/ina231-misc.h new file mode 100755 index 000000000000..a01f258ca39f --- /dev/null +++ b/drivers/hardkernel/ina231-misc.h @@ -0,0 +1,33 @@ +//[*]--------------------------------------------------------------------------------------------------[*] +// +// +// +// I2C INA231(Sensor) driver +// 2013.07.17 +// +// +//[*]--------------------------------------------------------------------------------------------------[*] +#ifndef _INA231_MISC_H_ +#define _INA231_MISC_H_ + +//[*]--------------------------------------------------------------------------------------------------[*] +struct ina231_iocreg { + unsigned char name[20]; + unsigned int enable; + unsigned int cur_uV; + unsigned int cur_uA; + unsigned int cur_uW; +} __attribute__ ((packed)); + +#define INA231_IOCGREG _IOR('i', 1, struct ina231_iocreg *) +#define INA231_IOCSSTATUS _IOW('i', 2, struct ina231_iocreg *) +#define INA231_IOCGSTATUS _IOR('i', 3, struct ina231_iocreg *) + +//[*]--------------------------------------------------------------------------------------------------[*] +extern void ina231_misc_remove (struct device *dev); +extern int ina231_misc_probe (struct ina231_sensor *sensor); + +//[*]--------------------------------------------------------------------------------------------------[*] +#endif // _INA231_MISC_H_ +//[*]--------------------------------------------------------------------------------------------------[*] +//[*]--------------------------------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ina231-sysfs.c b/drivers/hardkernel/ina231-sysfs.c new file mode 100755 index 000000000000..4b1ed40b2cdc --- /dev/null +++ b/drivers/hardkernel/ina231-sysfs.c @@ -0,0 +1,236 @@ +//[*]------------------------------------------------------------------------[*] +// +// +// I2C INA231(Sensor) driver +// 2013.07.17 +// +// +//[*]------------------------------------------------------------------------[*] +#include +#include +#include +#include +#include + +//[*]------------------------------------------------------------------------[*] +#include "ina231.h" +#include "ina231-i2c.h" +#include "ina231-misc.h" + +//[*]------------------------------------------------------------------------[*] +// +// sysfs function prototype define +// +//[*]------------------------------------------------------------------------[*] +static ssize_t show_name + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_name, 0660, show_name, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_power + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_W, 0660, show_power, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_current + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_A, 0660, show_current, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_voltage + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_V, 0660, show_voltage, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_max_power + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_maxW, 0660, show_max_power, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_max_current + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_maxA, 0660, show_max_current, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_max_voltage + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(sensor_maxV, 0660, show_max_voltage, NULL); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_enable + (struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t set_enable + (struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static DEVICE_ATTR(enable, 0660, show_enable, set_enable); + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_period + (struct device *dev, struct device_attribute *attr, char *buf); +static DEVICE_ATTR(update_period, 0660, show_period, NULL); + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*] +static struct attribute *ina231_sysfs_entries[] = { + &dev_attr_sensor_name.attr, + &dev_attr_sensor_W.attr, + &dev_attr_sensor_A.attr, + &dev_attr_sensor_V.attr, + &dev_attr_sensor_maxW.attr, + &dev_attr_sensor_maxA.attr, + &dev_attr_sensor_maxV.attr, + &dev_attr_enable.attr, + &dev_attr_update_period.attr, + NULL +}; + +static struct attribute_group ina231_attr_group = { + .name = NULL, + .attrs = ina231_sysfs_entries, +}; + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*] +static ssize_t show_name + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", sensor->pd->name); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_power + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + unsigned int value; + + mutex_lock(&sensor->mutex); + value = sensor->cur_uW; + mutex_unlock(&sensor->mutex); + + return sprintf(buf, "%d.%06d\n", (value/1000000), (value%1000000)); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_max_power + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + unsigned int value; + + mutex_lock(&sensor->mutex); + value = sensor->max_uW; + mutex_unlock(&sensor->mutex); + + return sprintf(buf, "%d.%06d\n", (value/1000000), (value%1000000)); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_current + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + unsigned int value; + + mutex_lock(&sensor->mutex); + value = sensor->cur_uA; + mutex_unlock(&sensor->mutex); + + return sprintf(buf, "%d.%06d\n", (value/1000000), (value%1000000)); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_max_current + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + unsigned int value; + + mutex_lock(&sensor->mutex); + value = sensor->max_uA; + mutex_unlock(&sensor->mutex); + + return sprintf(buf, "%d.%06d\n", (value/1000000), (value%1000000)); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_voltage + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + unsigned int value; + + mutex_lock(&sensor->mutex); + value = sensor->cur_uV; + mutex_unlock(&sensor->mutex); + + return sprintf(buf, "%d.%06d\n", (value/1000000), (value%1000000)); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_max_voltage + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + unsigned int value; + + mutex_lock(&sensor->mutex); + value = sensor->max_uV; + mutex_unlock(&sensor->mutex); + + return sprintf(buf, "%d.%06d\n", (value/1000000), (value%1000000)); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_enable + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", sensor->pd->enable); +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t set_enable + (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + + if (simple_strtol(buf, NULL, 10) != 0) { + if (!sensor->pd->enable) { + sensor->pd->enable = 1; + ina231_i2c_enable(sensor); + } + } + else { + if (sensor->pd->enable) { + sensor->pd->enable = 0; + } + } + return count; +} + +//[*]------------------------------------------------------------------------[*] +static ssize_t show_period + (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ina231_sensor *sensor = dev_get_drvdata(dev); + + return sprintf(buf, "%d usec\n", sensor->pd->update_period); +} + +//[*]------------------------------------------------------------------------[*] +int ina231_sysfs_create (struct device *dev) +{ + return sysfs_create_group(&dev->kobj, &ina231_attr_group); +} + +//[*]------------------------------------------------------------------------[*] +void ina231_sysfs_remove (struct device *dev) +{ + sysfs_remove_group(&dev->kobj, &ina231_attr_group); +} + +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ina231-sysfs.h b/drivers/hardkernel/ina231-sysfs.h new file mode 100755 index 000000000000..885c08f74713 --- /dev/null +++ b/drivers/hardkernel/ina231-sysfs.h @@ -0,0 +1,23 @@ +//[*]--------------------------------------------------------------------------------------------------[*] +// +// +// +// I2C INA231(Sensor) driver +// 2013.07.17 +// +// +//[*]--------------------------------------------------------------------------------------------------[*] +#ifndef _INA231_SYSFS_H_ +#define _INA231_SYSFS_H_ + +//[*]--------------------------------------------------------------------------------------------------[*] +// extern function define +//[*]--------------------------------------------------------------------------------------------------[*] +extern int ina231_sysfs_create (struct device *dev); +extern void ina231_sysfs_remove (struct device *dev); + +//[*]--------------------------------------------------------------------------------------------------[*] +#endif /* _INA231_SYSFS_H_ */ + +//[*]--------------------------------------------------------------------------------------------------[*] +//[*]--------------------------------------------------------------------------------------------------[*] diff --git a/drivers/hardkernel/ina231.h b/drivers/hardkernel/ina231.h new file mode 100755 index 000000000000..4f3e168a0da5 --- /dev/null +++ b/drivers/hardkernel/ina231.h @@ -0,0 +1,170 @@ +//[*]------------------------------------------------------------------------[*] +// +// +// I2C INA231 Sensor driver (platform data struct) +// 2013.07.17 +// +// +//[*]------------------------------------------------------------------------[*] +#ifndef __INA231_H__ +#define __INA231_H__ + +//[*]------------------------------------------------------------------------[*] +#define INA231_I2C_NAME "INA231" + +struct i2c_client; +struct misc_dev; + +//[*]------------------------------------------------------------------------[*] +// INA231 Register define +//[*]------------------------------------------------------------------------[*] +#define REG_CONFIG 0x00 // R/W +#define REG_SHUNT_VOLT 0x01 // R +#define REG_BUS_VOLT 0x02 // R +#define REG_POWER 0x03 // R +#define REG_CURRENT 0x04 // R +#define REG_CALIBRATION 0x05 // R/W +#define REG_ALERT_EN 0x06 // R/W +#define REG_ALERT_LIMIT 0x07 // R/W + +#define FIX_uV_LSB 1250 // fix lsb value 0.00125 V + +// ex) CONVERSION_DELAY(eVBUS_CT_140uS, eVSH_CT_150uS, eAVG_1); return uSec +#define CONVERSION_DELAY(x, y, z) ((x + y) * z) + +#define INA231_CONFIG(x) ((0x4000 | x) & 0xFFFF) +//[*]------------------------------------------------------------------------[*] +enum { + eAVG_CON_1 = 1, + eAVG_CON_4 = 4, + eAVG_CON_16 = 16, + eAVG_CON_64 = 64, + eAVG_CON_128 = 128, + eAVG_CON_256 = 256, + eAVG_CON_512 = 512, + eAVG_CON_1024 = 1024, +}; + +enum { + eAVG_1 = 0, + eAVG_4, + eAVG_16, + eAVG_64, + eAVG_128, + eAVG_256, + eAVG_512, + eAVG_1024, +}; + +#define AVG_BIT(x) (x << 9) + +//[*]------------------------------------------------------------------------[*] +enum { + eVBUS_CON_140uS = 140, + eVBUS_CON_204uS = 204, + eVBUS_CON_332uS = 332, + eVBUS_CON_588uS = 588, + eVBUS_CON_1100uS = 1100, + eVBUS_CON_2116uS = 2116, + eVBUS_CON_4156uS = 4156, + eVBUS_CON_8244uS = 8244, +}; + +enum { + eVBUS_CT_140uS = 0, + eVBUS_CT_204uS, + eVBUS_CT_332uS, + eVBUS_CT_588uS, + eVBUS_CT_1100uS, + eVBUS_CT_2116uS, + eVBUS_CT_4156uS, + eVBUS_CT_8244uS, +}; + +#define VBUS_CT(x) (x << 6) + +//[*]------------------------------------------------------------------------[*] +enum { + eVSH_CON_140uS = 140, + eVSH_CON_204uS = 204, + eVSH_CON_332uS = 332, + eVSH_CON_588uS = 588, + eVSH_CON_1100uS = 1100, + eVSH_CON_2116uS = 2116, + eVSH_CON_4156uS = 4156, + eVSH_CON_8244uS = 8244, +}; + +enum { + eVSH_CT_140uS = 0, + eVSH_CT_204uS, + eVSH_CT_332uS, + eVSH_CT_588uS, + eVSH_CT_1100uS, + eVSH_CT_2116uS, + eVSH_CT_4156uS, + eVSH_CT_8244uS, +}; + +#define VSH_CT(x) (x << 3) + +//[*]------------------------------------------------------------------------[*] +enum { + ePOWER_DOWN1 = 0, + eSHUNT_VOLT_TRIGGER, + eBUS_VOLT_TRIGGER, + eSHUNT_BUS_VOLT_TRIGGER, + ePOWER_DOWN2, + eSHUNT_VOLT_CONTINUOUS, + eBUS_VOLT_CONTINUOUS, + eSHUNT_BUS_VOLT_CONTINUOUS, +}; + +#define MODE_SET(x) (x) + +//[*]------------------------------------------------------------------------[*] +struct ina231_pd { + unsigned char *name; + unsigned short config; + unsigned int max_A; + unsigned int shunt_R_mohm; // unit = m ohm + unsigned int update_period; // unit = usec + unsigned int enable; +}; + +//[*]------------------------------------------------------------------------[*] +struct ina231_sensor { + struct i2c_client *client; + struct ina231_pd *pd; + struct miscdevice *misc; + + unsigned short reg_calibration; + unsigned short reg_bus_volt; + unsigned short reg_current; + + unsigned int cur_lsb_uA; + + unsigned int cur_uA; + unsigned int cur_uV; + unsigned int cur_uW; + + unsigned int max_uA; + unsigned int max_uV; + unsigned int max_uW; + + struct hrtimer timer; + unsigned int timer_sec, timer_nsec; + struct work_struct work; + struct workqueue_struct *wq; + + struct mutex mutex; + +#if defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend power; +#endif +}; + +//[*]------------------------------------------------------------------------[*] +#endif // INA231 +//[*]------------------------------------------------------------------------[*] +//[*]------------------------------------------------------------------------[*]