Skip to content

Instantly share code, notes, and snippets.

@pdp7
Last active April 1, 2016 16:33
Show Gist options
  • Save pdp7/55d1b5be832b41d17ad1 to your computer and use it in GitHub Desktop.
Save pdp7/55d1b5be832b41d17ad1 to your computer and use it in GitHub Desktop.
Android has driver for the ADC in the SeeedStudio Grove ADC: android / kernel / bcm / drivers / power / adc121c021_driver.c
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1ddd13c..b9d7919 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -495,6 +495,13 @@ config CHARGER_RT9455
help
Say Y to enable support for Richtek RT9455 battery charger.
+config MONITOR_ADC121C021_I2C
+ tristate "ADC121C021 Battery Monitor"
+ depends on I2C
+ help
+ Say Y here if you want to support a ADC121C021 battery monitor.
+ If unsure, say N.
+
config AXP20X_POWER
tristate "AXP20x power supply driver"
depends on MFD_AXP20X
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 0e4eab5..d58af99 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
obj-$(CONFIG_POWER_RESET) += reset/
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
+obj-$(CONFIG_MONITOR_ADC121C021_I2C) += adc121c021_driver.o
diff --git a/drivers/power/adc121c021_driver.c b/drivers/power/adc121c021_driver.c
new file mode 100644
index 0000000..64f19d5
--- /dev/null
+++ b/drivers/power/adc121c021_driver.c
@@ -0,0 +1,603 @@
+opyright 2010 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+/*
+ * ADC121C021 I2C Battery Monitor Driver
+ *
+ * The ADC121C021 is a six pin IC that monitors the battery voltage. It is a
+ * I2C slave device found at 0x54.
+ */
+/* ---- Include Files ---------------------------------------------------- */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <asm/gpio.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/kthread.h>
+#include <linux/syscalls.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <asm/io.h>
+#include <linux/broadcom/adc121c021_driver.h>
+#if defined(CONFIG_BCM_CMP_BATTERY_MULTI) || defined(CONFIG_BCM_CMP_BATTERY_MULTI_MODULE)
+#include <linux/broadcom/cmp_battery_multi.h>
+#endif
+/* ---- Public Variables ------------------------------------------------- */
+static int mod_debug = 0;
+module_param(mod_debug, int, 0644);
+/* ---- Private Constants and Types -------------------------------------- */
+struct i2c_priv_data
+{
+ struct i2c_client *p_i2c_client;
+};
+/* Driver upgrade changes ... */
+struct i2c_state
+{
+ struct i2c_client *p_i2c_client;
+};
+static const char *reg_names[] =
+{ "output",
+ "status",
+ "config",
+ "under alert",
+ "over alert",
+ "hysteresis",
+ "lowest",
+ "highest",
+};
+#define GPIO_I2C_RESET_DELAY_MSECS 10
+#define GPIO_RESET_PIN 16
+#define MAX_NUMBER_READ_ERRORS 5
+#define MILLISECS_BETWEEN_READS 20000
+#define USE_ALERT_IRQ 0
+/* ---- Private Variables ------------------------------------------------ */
+static int g_num_read_errors = 0;
+static int g_num_driver_errors = 0;
+static int g_found_slave_addr = 0;
+static struct i2c_priv_data *gp_i2c_driver_priv = NULL;
+static char *gp_buffer = NULL;
+const struct I2C_ADC121C021_t *gp_i2c_adc121c021 = NULL;
+static int g_battery_millivolts = 0;
+static struct task_struct *gp_task_struct = NULL;
+static int g_adc121c021_registers[ADC121C021_NUM_REGISTERS];
+static struct ADC121C021_REGISTER adc121c021_registers[] =
+{ /* Reg (0-7) Length R/W Default */
+ { ADC121C021_ADC_REG, 2, 0, 0, },
+ { ADC121C021_STATUS_REG, 1, 1, 0, },
+ { ADC121C021_CONFIG_REG, 1, 1, 0, },
+ { ADC121C021_UNDER_ALERT_REG,2, 1, 0, },
+ { ADC121C021_OVER_ALERT_REG, 2, 1, 0xfff, },
+ { ADC121C021_HSYT_ALERT_REG, 2, 1, 0, },
+ { ADC121C021_LOWEST_REG, 2, 1, 0xfff, },
+ { ADC121C021_HIGHEST_REG, 2, 1, 0, },
+};
+static DECLARE_WAIT_QUEUE_HEAD(g_event_waitqueue);
+atomic_t g_atomic_irqs_rxd = ATOMIC_INIT(0);
+/* ---- Private Function Prototypes -------------------------------------- */
+int i2c_adc121_driver_read (int *millivolts);
+int i2c_adc121_driver_write (int length);
+void i2c_adc121_driver_handle_i2c_error(int rc);
+void i2c_adc121_read_slave (void);
+int i2c_adc121_get_battery_voltage (int *battery_millivolts);
+#if USE_ALERT_IRQ
+int i2c_adc121_driver_setup_gpio (void);
+#endif
+/* ---- Public Functions ------------------------------------------------- */
+int adc121_get_battery_voltage(void *p_data)
+{
+ int battery_millivolts;
+ i2c_adc121_get_battery_voltage(&battery_millivolts);
+ if (mod_debug)
+ printk("%s() retreiving battery voltage %d\n", __FUNCTION__, battery_millivolts);
+ return battery_millivolts;
+}
+/* ---- Functions -------------------------------------------------------- */
+/* Battery voltage in millivolts. */
+int i2c_adc121_get_battery_voltage(int *p_battery_millivolts)
+{
+ *p_battery_millivolts = g_battery_millivolts;
+
+ if (g_battery_millivolts > gp_i2c_adc121c021->battery_min_voltage &&
+ g_battery_millivolts < gp_i2c_adc121c021->battery_max_voltage)
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+int i2c_adc121c021_find_voltage(void)
+{
+ int rc = 0;
+ int adc_millivolts;
+ int fudged_millivolts;
+
+ rc = i2c_adc121_driver_read(&adc_millivolts);
+
+ if (rc == 0)
+ { /* Some adjustment needed to measurement to obtain accurate value. */
+ fudged_millivolts = adc_millivolts - 700;
+ g_battery_millivolts = (((gp_i2c_adc121c021->resistor_1 +
+ gp_i2c_adc121c021->resistor_2)*1000 /
+ gp_i2c_adc121c021->resistor_2) *
+ fudged_millivolts)/1000;
+
+ if (mod_debug)
+ {
+ printk("%s() raw(mV): %d fudged(mv): %d battery(mv): %d\n",
+ __FUNCTION__, adc_millivolts, fudged_millivolts, g_battery_millivolts);
+ }
+ }
+ else
+ {
+ printk("%s() error reading slave: %d\n", __FUNCTION__, rc);
+ }
+ return rc;
+}
+int i2c_adc121_driver_read(int *p_measured_millivoltage)
+{
+ int rc = 0;
+ int i;
+ int length;
+
+ if (gp_i2c_driver_priv == NULL ||
+ gp_i2c_driver_priv->p_i2c_client == NULL)
+ {
+ printk("%s() gp_i2c_driver_priv->p_i2c_client == NULL\n", __FUNCTION__);
+ return -1;
+ }
+
+ for (i = 0; i < ADC121C021_NUM_REGISTERS; i++)
+ { /* Have to set the address to read from each register. */
+ memset(gp_buffer, 0, gp_i2c_adc121c021->num_bytes_to_read);
+ /* Have to do a write to set the register index. */
+ length = ADC121C021_WRITE_REG_LENGTH;
+ gp_buffer[0] = i;
+ rc = i2c_master_send(gp_i2c_driver_priv->p_i2c_client,
+ gp_buffer,
+ length);
+ if (rc < length)
+ {
+ printk("%s %s() i2c_master_send() failed %d\n",
+ I2C_ADC121C021_DRIVER_NAME, __FUNCTION__, rc);
+ g_num_read_errors++;
+ return rc;
+ }
+
+ if (mod_debug > 1)
+ {
+ printk("%s() i2c_master_send() rc: %d\n", __FUNCTION__, rc);
+ }
+
+ memset(gp_buffer, 0, gp_i2c_adc121c021->num_bytes_to_read);
+ length = adc121c021_registers[i].num_bytes;
+
+ rc = i2c_master_recv(gp_i2c_driver_priv->p_i2c_client,
+ gp_buffer,
+ length);
+ if (mod_debug > 1)
+ {
+ printk("%s() i2c_master_recv() length %d rc: %d, "
+ "reg: %11s i: %d rcvd: 0x%x 0x%x\n",
+ __FUNCTION__, length, rc, reg_names[i],
+ i, gp_buffer[0], gp_buffer[1]);
+ }
+
+ if (rc < adc121c021_registers[i].num_bytes)
+ {
+ printk("%s %s() failed %d\n", I2C_ADC121C021_DRIVER_NAME, __FUNCTION__, rc);
+ g_num_read_errors++;
+ i2c_adc121_driver_handle_i2c_error(rc);
+ return rc;
+ }
+
+ g_adc121c021_registers[i] = ((0x0f & gp_buffer[0]) << 8) + gp_buffer[1];
+ }
+
+ *p_measured_millivoltage = g_adc121c021_registers[ADC121C021_ADC_REG];
+ g_num_read_errors = 0;
+ return 0;
+}
+/*
+ * Periodically wake up and read the battery voltage.
+ */
+static int i2c_adc121_driver_kthread(void *unused)
+{
+ int rc = 0;
+ long unsigned int my_jiffies, timeout_jiffies;
+ wait_queue_head_t wait_queue;
+ init_waitqueue_head (&wait_queue);
+ daemonize("i2c-adc121-driver");
+
+ /* Request delivery of SIGKILL */
+ allow_signal(SIGKILL);
+ timeout_jiffies = msecs_to_jiffies(MILLISECS_BETWEEN_READS);
+ for (;;)
+ {
+ /* Relinquish the processor until the event occurs */
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (atomic_read(&g_atomic_irqs_rxd) == 0)
+ { /* Nothing to read, wait a while ... */
+ my_jiffies = wait_event_timeout(g_event_waitqueue, /* the waitqueue to wait on */
+ atomic_read(&g_atomic_irqs_rxd), /* condition to check */
+ timeout_jiffies); /* timeout in jiffies */
+ if (my_jiffies < 0)
+ {
+ printk("i2c-driver kernel thread ended!\n");
+ break;
+ }
+ else
+ { /* Timed out, read voltage. */
+ i2c_adc121c021_find_voltage();
+ }
+ }
+ else
+ { /* Perform a read immediately. */
+ rc = i2c_adc121c021_find_voltage();
+
+ if (mod_debug)
+ {
+ printk("%s() i2c_adc121c021_find_voltage() returned: %d", __FUNCTION__, rc);
+ }
+ if (atomic_read(&g_atomic_irqs_rxd) > 0)
+ {
+ atomic_dec(&g_atomic_irqs_rxd);
+ }
+ }
+
+ }
+ return rc;
+}
+int i2c_adc121_driver_write(int length)
+{
+ int rc;
+
+ rc = i2c_master_send(gp_i2c_driver_priv->p_i2c_client,
+ gp_buffer,
+ length);
+ return rc;
+}
+static irqreturn_t i2c_adc121_driver_isr(int irq, void *dev_id)
+{
+ /* This is called if USE_ALERT_IRQ is set to 1 and either a under or over
+ * alert occurred.
+ * An under alert is raised if the battery voltage is detected to be less
+ * than the minimum HW_BATTERY_MIN_VOLTAGE.
+ * An over alert is raised if the battery voltage is detected to be greater
+ * than the maximum HW_BATTERY_MAX_VOLTAGE.
+ */
+ atomic_inc(&g_atomic_irqs_rxd);
+
+ if (atomic_read(&g_atomic_irqs_rxd) == 1)
+ {
+ wake_up(&g_event_waitqueue);
+ }
+ return IRQ_HANDLED;
+}
+void i2c_adc121_driver_handle_i2c_error(int rc)
+{
+ if (mod_debug > 0)
+ {
+ printk("%s I2C error, rc %d # read errors %d # known driver errors %d\n",
+ I2C_ADC121C021_DRIVER_NAME, rc,
+ g_num_read_errors,
+ g_num_driver_errors);
+ }
+
+ if (rc != 0)
+ { /* Was called by i2c_adc121_driver_read(). */
+ if (g_num_read_errors < MAX_NUMBER_READ_ERRORS)
+ {
+ printk("%s I2C read error %d, error %d\n",
+ I2C_ADC121C021_DRIVER_NAME, g_num_read_errors, rc);
+ }
+ else if (g_num_read_errors == MAX_NUMBER_READ_ERRORS)
+ {
+ printk("%s maximum # I2C read errors reached %d, error %d\n",
+ I2C_ADC121C021_DRIVER_NAME, g_num_read_errors, rc);
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ g_num_driver_errors++;
+ }
+
+ printk("%s I2C bus has problems but cannot reset slave at 0x%x\n",
+ I2C_ADC121C021_DRIVER_NAME, g_found_slave_addr);
+
+ if (rc == -EREMOTEIO)
+ { /* Indicates a problem with the bus. Reset the I2C master controller. */
+ printk("%s detected remote IO problem but cannot reset I2C bus master\n",
+ I2C_ADC121C021_DRIVER_NAME);
+ }
+}
+/*
+ * Setup the interrupt handling if it is going to be used.
+ */
+int i2c_adc121_driver_setup_gpio(void)
+{
+ int rc;
+ int ret = 0;
+
+ if ((rc = gpio_request(gp_i2c_adc121c021->gpio_irq_pin, "adc121c021 alert")) != 0)
+ {
+ printk("%s() gpio_request(%d) failed, rc = %d\n", __FUNCTION__,
+ gp_i2c_adc121c021->gpio_irq_pin, rc);
+ ret = rc;
+ }
+
+ if ((rc = request_irq(gpio_to_irq(gp_i2c_adc121c021->gpio_irq_pin),
+ i2c_adc121_driver_isr,
+ (IRQF_TRIGGER_FALLING),
+ "GPIO adc121c021 irq",
+ gp_i2c_driver_priv)) < 0)
+ {
+ printk("%s() request_irq(%d) failed, rc = %d\n", __FUNCTION__,
+ gp_i2c_adc121c021->gpio_irq_pin, rc);
+ ret = rc;
+ }
+ return ret;
+}
+#ifdef CONFIG_PM
+static int i2c_adc121_suspend_driver(struct i2c_client *p_client, pm_message_t mesg)
+{
+ /* Internal thread is stopped. */
+ return 0;
+}
+static int i2c_adc121_resume_driver(struct i2c_client *p_client)
+{
+ /* Internal thread is started. */
+ return 0;
+}
+#endif
+static int i2c_adc121_driver_probe(struct i2c_client *p_i2c_client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct i2c_state *p_state;
+ struct device *dev = &p_i2c_client->dev;
+ int battery_data;
+#if defined(CONFIG_BCM_CMP_BATTERY_MULTI) || defined(CONFIG_BCM_CMP_BATTERY_MULTI_MODULE)
+ struct battery_monitor *p_monitor;
+#endif
+
+ if (p_i2c_client == NULL)
+ {
+ printk(KERN_ERR "%s i2c_adc121_driver_probe() p_i2c_client == NULL\n",
+ I2C_ADC121C021_DRIVER_NAME);
+ return -1;
+ }
+
+ if (p_i2c_client->dev.platform_data == NULL)
+ {
+ printk(KERN_ERR "%s i2c_adc121_driver_probe() "
+ "p_i2c_client->dev.platform_data == NULL\n",
+ I2C_ADC121C021_DRIVER_NAME);
+ return -1;
+ }
+ if (g_found_slave_addr > 0)
+ { /* Needed when more than one I2C slave had the same address. */
+ printk(KERN_ERR "%s i2c_adc121_driver_probe() i2c slave already "
+ "found at 0x%x\n",
+ I2C_ADC121C021_DRIVER_NAME, g_found_slave_addr);
+ return -1;
+ }
+
+ /* get platform data */
+ gp_i2c_adc121c021 =
+ (struct I2C_ADC121C021_t *)p_i2c_client->dev.platform_data;
+
+ if (gp_i2c_adc121c021 == NULL)
+ { /* Cannot access platform data. */
+ printk("%s:%s Cannot access platform data for I2C slave address %d\n",
+ I2C_ADC121C021_DRIVER_NAME, __FUNCTION__, p_i2c_client->addr);
+ return -1;
+ }
+ /* todo: clean up memory allocation failure handlings */
+ p_state = kzalloc(sizeof(struct i2c_state), GFP_KERNEL);
+ if (p_state == NULL)
+ {
+ dev_err(dev, "failed to create our state\n");
+ return -ENOMEM;
+ }
+ p_state->p_i2c_client = p_i2c_client;
+ gp_i2c_driver_priv = kzalloc(sizeof(struct i2c_priv_data), GFP_KERNEL);
+ if (gp_i2c_driver_priv == NULL)
+ {
+ dev_err(dev, "failed to create gp_i2c_driver_priv\n");
+ return -ENOMEM;
+ }
+ gp_i2c_driver_priv->p_i2c_client = p_i2c_client;
+
+ i2c_set_clientdata(p_i2c_client, p_state);
+
+ /* Rest of the initialisation goes here. */
+
+ /* Create some space to store the I2C bytes read from the slave. */
+ gp_buffer = kzalloc(gp_i2c_adc121c021->num_bytes_to_read + 10, GFP_KERNEL);
+ if (!gp_buffer)
+ {
+ printk("i2c_adc121_driver_probe() kzalloc() returned NULL\n");
+ return -ENOMEM;
+ }
+ rc = i2c_adc121_driver_read(&battery_data);
+
+ if (rc < 0)
+ { /* Do not free anything otherwise I2C bus goes kaput and system will
+ * grind to a halt!
+ */
+ printk("%s() leaving, I2C slave not detected\n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+#if defined(CONFIG_BCM_CMP_BATTERY_MULTI) || defined(CONFIG_BCM_CMP_BATTERY_MULTI_MODULE)
+ p_monitor = kzalloc(sizeof(struct battery_monitor), GFP_KERNEL);
+
+ if (p_monitor == NULL)
+ {
+ return -ENOMEM;
+ }
+ p_monitor->name = I2C_ADC121C021_DRIVER_NAME;
+ p_monitor->get_voltage_fn = adc121_get_battery_voltage;
+ p_monitor->gpio_ac_power = gp_i2c_adc121c021->gpio_ac_power;
+ p_monitor->ac_power_on_level = gp_i2c_adc121c021->ac_power_on_level;
+ p_monitor->gpio_charger = gp_i2c_adc121c021->gpio_charger;
+ rc = register_battery_monitor(p_monitor, p_i2c_client);
+ if (rc < 0) {
+ kfree(p_monitor);
+ kfree(gp_buffer);
+ return rc;
+ }
+#endif
+
+ /*
+ * Setup the gpio for handling interrupt requests and the reset pin if used
+ * based on platform_data.
+ */
+#if USE_ALERT_IRQ
+ if (i2c_adc121_driver_setup_gpio() != 0)
+ {
+#if defined(CONFIG_BCM_CMP_BATTERY_MULTI) || defined(CONFIG_BCM_CMP_BATTERY_MULTI_MODULE)
+ kfree(p_monitor);
+#endif
+ kfree(gp_buffer);
+ return -1;
+ }
+#endif
+
+ /* This thread wakes periodically to read the battery voltage. */
+ gp_task_struct = kthread_run(i2c_adc121_driver_kthread, /* pointer to function */
+ NULL, /* data pointer argument */
+ "adc121c021 thread"); /* thread name string */
+
+ if (gp_task_struct == NULL)
+ {
+ printk("%s i2c_adc121_driver_probe() kernel thread not created\n",
+ I2C_ADC121C021_DRIVER_NAME);
+#if defined(CONFIG_BCM_CMP_BATTERY_MULTI) || defined(CONFIG_BCM_CMP_BATTERY_MULTI_MODULE)
+ kfree(p_monitor);
+#endif
+ kfree(gp_buffer);
+#if USE_ALERT_IRQ
+ free_irq(gp_i2c_adc121c021->gpio_irq_pin, gp_i2c_driver_priv);
+#endif
+ return -1;
+ }
+
+ g_found_slave_addr = p_i2c_client->addr;
+
+ printk("%s() found i2c slave at 0x%x\n", __FUNCTION__, p_i2c_client->addr);
+
+ if (mod_debug)
+ {
+ printk("%s() gp_i2c_adc121c021->i2c_slave_address : 0x%x\n",
+ __FUNCTION__, gp_i2c_adc121c021->i2c_slave_address);
+ printk("%s() gp_i2c_adc121c021->gpio_irq_pin : %d\n",
+ __FUNCTION__, gp_i2c_adc121c021->gpio_irq_pin);
+ printk("%s() gp_i2c_adc121c021->num_bytes_to_read : %d\n",
+ __FUNCTION__, gp_i2c_adc121c021->num_bytes_to_read);
+ }
+
+ /*
+ * The adc121c021 is being configured to run in manual conversion mode.
+ * Register 0 contains the output of the ADC of the voltage on pin 3, Vin.
+ * This is the simplest mode and no over voltage or under voltage alerts
+ * will be generated and detected on the GPIO.
+ */
+ rc = i2c_adc121c021_find_voltage();
+ return rc;
+}
+
+static int __devexit i2c_adc121_driver_remove(struct i2c_client *client)
+{
+ struct i2c_state *state = i2c_get_clientdata(client);
+ kfree(state);
+ if (gp_task_struct != NULL)
+ {
+ kthread_stop(gp_task_struct);
+ }
+
+#if USE_ALERT_IRQ
+ free_irq(gp_i2c_adc121c021->gpio_irq_pin, gp_i2c_driver_priv);
+#endif
+
+ /* Free all the memory that was allocated. */
+ if (gp_i2c_driver_priv->p_i2c_client != NULL)
+ {
+ kfree(gp_i2c_driver_priv->p_i2c_client);
+ }
+
+ if (gp_i2c_driver_priv != NULL)
+ {
+ kfree(gp_i2c_driver_priv);
+ }
+ if (gp_buffer != NULL)
+ {
+ kfree(gp_buffer);
+ }
+
+ return 0;
+}
+/* End of if using .probe in i2c_driver. */
+static struct i2c_device_id adc121c021_i2c_idtable[] = {
+ { I2C_ADC121C021_DRIVER_NAME, 0 },
+ { }
+};
+static struct i2c_driver adc121c021_i2c_driver = {
+ .driver = {
+ .name = I2C_ADC121C021_DRIVER_NAME,
+ },
+ .id_table = adc121c021_i2c_idtable,
+ .class = I2C_CLASS_HWMON,
+ .probe = i2c_adc121_driver_probe,
+ .remove = __devexit_p(i2c_adc121_driver_remove),
+#ifdef CONFIG_PM
+ .suspend = i2c_adc121_suspend_driver,
+ .resume = i2c_adc121_resume_driver,
+#endif
+};
+int __init i2c_adc121_driver_init(void)
+{
+ int rc;
+
+ rc = i2c_add_driver(&adc121c021_i2c_driver);
+ if (rc != 0)
+ {
+ printk("%s i2c_adc121_driver_init(): i2c_add_driver() failed, errno is %d\n",
+ I2C_ADC121C021_DRIVER_NAME, rc);
+ return rc;
+ }
+ return rc;
+}
+static void __exit i2c_adc121_driver_exit(void)
+{
+ i2c_del_driver(&adc121c021_i2c_driver);
+}
+MODULE_DESCRIPTION("I2C adc121c021 driver");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL");
+module_init(i2c_adc121_driver_init);
+module_exit(i2c_adc121_driver_exit);
@pdp7
Copy link
Author

pdp7 commented Feb 23, 2016

https://android.googlesource.com/kernel/bcm/+/0bee9da3949989d5142946a3fd5196ba890c4f9b%5E%21/#F14

https://android.googlesource.com/kernel/bcm/+/0bee9da3949989d5142946a3fd5196ba890c4f9b

android / kernel / bcm / 0bee9da3949989d5142946a3fd5196ba890c4f9b
commit  0bee9da3949989d5142946a3fd5196ba890c4f9b    [log] [tgz]
author  Danny Yip <dannyyip@broadcom.com>   Fri Aug 05 18:12:12 2011 -0700
committer   Danny Yip <dannyyip@broadcom.com>   Fri Aug 05 18:12:12 2011 -0700
tree    a396361dd6b51f1f26063225c3b3b0c62c692a4d
parent  53cde7dd0b9c57f3676a692693f5f3392924aa2c [diff]
SWMAPYVR-106 : Changed battery drivers to be statically linked.

Changed the ADC121c021, BQ27541, and CMP_BATTERY_MULIT driver to be statically linkable.
This involves consolidating battery and fuel gauge drivers into drivers/power.
Note that only the BCM11160 and BCM28160 related drivers are addressed.
arch/arm/configs/bcm11160_sv_defconfig[diff]
arch/arm/configs/bcm11160_sv_li_128mb_defconfig[diff]
arch/arm/configs/bcm11160_tablet_android_defconfig[diff]
arch/arm/configs/bcm11160_tablet_defconfig[diff]
arch/arm/configs/bcm11160_tablet_li_128mb_defconfig[diff]
arch/arm/configs/bcm28160_tablet_android_defconfig[diff]
arch/arm/configs/bcm28160_tablet_defconfig[diff]
arch/arm/configs/bcm28160_tablet_li_128mb_defconfig[diff]
arch/arm/mach-island/configs/board/tablet/linux.config[diff]
arch/arm/mach-island/configs/board/tablet_28160/linux.config[diff]
arch/arm/mach-island/configs/linux.config[diff]
drivers/misc/Kconfig[diff]
drivers/misc/Makefile[diff]
drivers/power/Kconfig[diff]
drivers/power/Makefile[diff]
drivers/power/adc121c021_driver.c[Renamed from drivers/misc/adc121c021_driver.c - diff]
drivers/power/bq27541.c[Renamed from drivers/misc/bq27541.c - diff]
17 files changed

@pdp7
Copy link
Author

pdp7 commented Feb 23, 2016

Attempt to compile:

pdp7@n4c:~/dev/beaglebone/linux-dev$ ./tools/rebuild.sh 
+ Detected build host [Debian GNU/Linux testing (stretch)]
+ host: [x86_64]
+ git HEAD commit: [545b4a9d6916eb3b2d5e88c3b55cd2d4a61f5e73]
-----------------------------
scripts/gcc: Using: arm-linux-gnueabihf-gcc (Linaro GCC 5.2-2015.11-2) 5.2.1 20151005
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------------
CROSS_COMPILE=/home/pdp7/dev/beaglebone/linux-dev/dl/gcc-linaro-5.2-2015.11-2-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
scripts/kconfig/mconf  Kconfig


*** End of the configuration.
*** Execute 'make' to start the build or try 'make help'.

‘.config’ -> ‘/home/pdp7/dev/beaglebone/linux-dev/patches/defconfig’
-----------------------------
make -j2 ARCH=arm LOCALVERSION=-armv7-devel-r47 CROSS_COMPILE="/home/pdp7/dev/beaglebone/linux-dev/dl/gcc-linaro-5.2-2015.11-2-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-"  zImage modules
-----------------------------
  HOSTCC  scripts/kconfig/conf.o
  HOSTLD  scripts/kconfig/conf
scripts/kconfig/conf  --silentoldconfig Kconfig
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
make[1]: 'include/generated/mach-types.h' is up to date.
  CHK     include/generated/timeconst.h
  CHK     include/generated/bounds.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  CHK     include/generated/compile.h
  GZIP    kernel/config_data.gz
  CHK     kernel/config_data.h
  UPD     kernel/config_data.h
  CC      kernel/configs.o
  LD      kernel/built-in.o
  DTC     drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb
  DTB     drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb.S
  AS      drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb.o
  LD      drivers/gpu/drm/tilcdc/built-in.o
rm drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb.S
  LD      drivers/gpu/drm/built-in.o
  LD      drivers/gpu/built-in.o
  CC [M]  drivers/power/adc121c021_driver.o
drivers/power/adc121c021_driver.c:1:10: error: expected '=', ',', ';', 'asm' or '__attribute__' before numeric constant
 opyright 2010 Broadcom Corporation.  All rights reserved.
          ^
drivers/power/adc121c021_driver.c:10:47: warning: missing terminating ' character
 * license other than the GPL, without Broadcom's express prior written
                                               ^
drivers/power/adc121c021_driver.c:10:1: error: missing terminating ' character
 * license other than the GPL, without Broadcom's express prior written
 ^
In file included from include/linux/list.h:4:0,
                 from include/linux/module.h:9,
                 from drivers/power/adc121c021_driver.c:21:
include/linux/types.h:98:10: error: unknown type name '__s8'
 typedef  __s8  int8_t;
          ^
In file included from include/linux/quota.h:42:0,
                 from include/linux/fs.h:268,
                 from include/linux/input.h:23,
                 from drivers/power/adc121c021_driver.c:23:
./include/uapi/linux/dqblk_xfs.h:52:2: error: unknown type name '__s8'
  __s8  d_version; /* version of this structure */
  ^
./include/uapi/linux/dqblk_xfs.h:53:2: error: unknown type name '__s8'
  __s8  d_flags; /* FS_{USER,PROJ,GROUP}_QUOTA */
  ^
./include/uapi/linux/dqblk_xfs.h:154:2: error: unknown type name '__s8'
  __s8  qs_version; /* version number for future changes */
  ^
./include/uapi/linux/dqblk_xfs.h:156:2: error: unknown type name '__s8'
  __s8  qs_pad;  /* unused */
  ^
./include/uapi/linux/dqblk_xfs.h:198:2: error: unknown type name '__s8'
  __s8   qs_version; /* version for future changes */
  ^
drivers/power/adc121c021_driver.c:37:46: fatal error: linux/broadcom/adc121c021_driver.h: No such file or directory
compilation terminated.
scripts/Makefile.build:264: recipe for target 'drivers/power/adc121c021_driver.o' failed
make[2]: *** [drivers/power/adc121c021_driver.o] Error 1
scripts/Makefile.build:407: recipe for target 'drivers/power' failed
make[1]: *** [drivers/power] Error 2
make[1]: *** Waiting for unfinished jobs....
Makefile:950: recipe for target 'drivers' failed
make: *** [drivers] Error 2

@jadonk
Copy link

jadonk commented Mar 3, 2016

Line 1 in the _driver.c file is missing the "/* C" at the beginning.
The .h file doesn't even seem to be part of the patch!

@pdp7
Copy link
Author

pdp7 commented Apr 1, 2016

@pdp7
Copy link
Author

pdp7 commented Apr 1, 2016

New patchset to linux-iio mailing list adds support for ADC121C021 to existing ti-adc081c driver:
https://lkml.org/lkml/2016/3/31/714

From    Crestez Dan Leonard <>
Subject [PATCH 0/2] Add support for adc101c* and adc121c*
Date    Thu, 31 Mar 2016 20:20:34 +0300
share 0
share 0
This adds support for adc101c* and adc121c* using the ti-adc081c driver.

I send just the first patch earlier but got no response. The first patch now no
longer sets .data in of_device_id because that was not actually used.

The second patch adds buffer support based on external trigger.

Crestez Dan Leonard (2):
  ti-adc081c: Add support for adc101c* and adc121c*
  ti-adc081c: Initial triggered buffer support

 drivers/iio/adc/Kconfig      |   6 +--
 drivers/iio/adc/ti-adc081c.c | 116 ++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 106 insertions(+), 16 deletions(-)
https://lkml.org/lkml/2016/4/1/112

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment