summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorZhou Jingyu <b02241@freescale.com>2011-03-02 15:36:28 +0800
committerJason Liu <r64343@freescale.com>2012-01-09 19:53:51 +0800
commit3b91b5ac920ba4e716afb8618ee10d6fb7711852 (patch)
tree75f4dbe0c38918b549c2f048bfa4ea02733586a4 /drivers
parent28a8e184e019fa7b520521dd26e966d7f02302fc (diff)
ENGR00141391-1 PMIC: Add DA9053 source code from Dialog
Add DA9053 original source code Signed-off-by: Zhou Jingyu <Jingyu.Zhou@freescale.com> Acked-by: Lily Zhang <r58066@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/da9052-gpio.c731
-rw-r--r--drivers/hwmon/da9052-adc.c644
-rw-r--r--drivers/input/misc/da9052_onkey.c133
-rw-r--r--drivers/input/touchscreen/da9052_tsi.c1448
-rw-r--r--drivers/input/touchscreen/da9052_tsi_calibrate.c107
-rw-r--r--drivers/input/touchscreen/da9052_tsi_filter.c489
-rw-r--r--drivers/leds/leds-da9052.c307
-rw-r--r--drivers/mfd/da9052-core.c533
-rw-r--r--drivers/mfd/da9052-i2c.c376
-rw-r--r--drivers/mfd/da9052-spi.c399
-rw-r--r--drivers/power/da9052-battery.c844
-rw-r--r--drivers/regulator/da9052-regulator.c487
-rw-r--r--drivers/rtc/rtc-da9052.c694
-rw-r--r--drivers/spi/spi_sam.c1161
-rw-r--r--drivers/video/backlight/da9052_bl.c455
-rw-r--r--drivers/watchdog/da9052_wdt.c542
16 files changed, 9350 insertions, 0 deletions
diff --git a/drivers/gpio/da9052-gpio.c b/drivers/gpio/da9052-gpio.c
new file mode 100644
index 000000000000..a9c53f219c3e
--- /dev/null
+++ b/drivers/gpio/da9052-gpio.c
@@ -0,0 +1,731 @@
+/*
+ * da9052-gpio.c -- GPIO Driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/syscalls.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/gpio.h>
+
+#define DRIVER_NAME "da9052-gpio"
+static inline struct da9052_gpio_chip *to_da9052_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct da9052_gpio_chip, gp);
+}
+
+void da9052_gpio_notifier(struct da9052_eh_nb *eh_data, unsigned int event)
+{
+ struct da9052_gpio_chip *gpio =
+ container_of(eh_data, struct da9052_gpio_chip, eh_data);
+ kobject_uevent(&gpio->gp.dev->kobj, KOBJ_CHANGE);
+ printk(KERN_INFO "Event received from GPIO8\n");
+}
+
+static u8 create_gpio_config_value(u8 gpio_function, u8 gpio_type, u8 gpio_mode)
+{
+ /* The format is -
+ function - 2 bits
+ type - 1 bit
+ mode - 1 bit */
+ return gpio_function | (gpio_type << 2) | (gpio_mode << 3);
+}
+
+static s32 write_default_gpio_values(struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg;
+ u8 created_val = 0;
+
+#if (DA9052_GPIO_PIN_0 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0001_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO0_FUNCTION,
+ DEFAULT_GPIO0_TYPE, DEFAULT_GPIO0_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_1 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0001_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO1_FUNCTION,
+ DEFAULT_GPIO1_TYPE, DEFAULT_GPIO1_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 2-3*/
+#if (DA9052_GPIO_PIN_2 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0203_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO2_FUNCTION,
+ DEFAULT_GPIO2_TYPE, DEFAULT_GPIO2_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_3 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0203_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO3_FUNCTION,
+ DEFAULT_GPIO3_TYPE, DEFAULT_GPIO3_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 4-5*/
+#if (DA9052_GPIO_PIN_4 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0405_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO4_FUNCTION,
+ DEFAULT_GPIO4_TYPE, DEFAULT_GPIO4_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_5 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0405_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO5_FUNCTION,
+ DEFAULT_GPIO5_TYPE, DEFAULT_GPIO5_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 6-7*/
+#if (DA9052_GPIO_PIN_6 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0607_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO6_FUNCTION,
+ DEFAULT_GPIO6_TYPE, DEFAULT_GPIO6_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_7 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0607_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO7_FUNCTION,
+ DEFAULT_GPIO7_TYPE, DEFAULT_GPIO7_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 8-9*/
+#if (DA9052_GPIO_PIN_8 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0809_REG;
+ msg.data = 0;
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO8_FUNCTION,
+ DEFAULT_GPIO8_TYPE, DEFAULT_GPIO8_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_9 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO0809_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO9_FUNCTION,
+ DEFAULT_GPIO9_TYPE, DEFAULT_GPIO9_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 10-11*/
+#if (DA9052_GPIO_PIN_10 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO1011_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO10_FUNCTION,
+ DEFAULT_GPIO10_TYPE, DEFAULT_GPIO10_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_11 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO1011_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO11_FUNCTION,
+ DEFAULT_GPIO11_TYPE, DEFAULT_GPIO11_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 12-13*/
+#if (DA9052_GPIO_PIN_12 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO1213_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO12_FUNCTION,
+ DEFAULT_GPIO12_TYPE, DEFAULT_GPIO12_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_13 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO1213_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO13_FUNCTION,
+ DEFAULT_GPIO13_TYPE, DEFAULT_GPIO13_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+/* GPIO 14-15*/
+#if (DA9052_GPIO_PIN_14 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO1415_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO14_FUNCTION,
+ DEFAULT_GPIO14_TYPE, DEFAULT_GPIO14_MODE);
+ msg.data &= DA9052_GPIO_MASK_UPPER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+#if (DA9052_GPIO_PIN_15 == DA9052_GPIO_CONFIG)
+ da9052_lock(da9052);
+ msg.addr = DA9052_GPIO1415_REG;
+ msg.data = 0;
+
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+
+ created_val = create_gpio_config_value(DEFAULT_GPIO15_FUNCTION,
+ DEFAULT_GPIO15_TYPE, DEFAULT_GPIO15_MODE);
+ created_val = created_val << DA9052_GPIO_NIBBLE_SHIFT;
+ msg.data &= DA9052_GPIO_MASK_LOWER_NIBBLE;
+ msg.data |= created_val;
+
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+#endif
+ return 0;
+}
+
+s32 da9052_gpio_read_port(struct da9052_gpio_read_write *read_port,
+ struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg;
+ u8 shift_value = 0;
+ u8 port_functionality = 0;
+ msg.addr = (read_port->port_number / 2) + DA9052_GPIO0001_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+ port_functionality =
+ (read_port->port_number % 2) ?
+ ((msg.data & DA9052_GPIO_ODD_PORT_FUNCTIONALITY) >>
+ DA9052_GPIO_NIBBLE_SHIFT) :
+ (msg.data & DA9052_GPIO_EVEN_PORT_FUNCTIONALITY);
+
+ if (port_functionality != INPUT)
+ return DA9052_GPIO_INVALID_PORTNUMBER;
+
+ if (read_port->port_number >= (DA9052_GPIO_MAX_PORTNUMBER))
+ return DA9052_GPIO_INVALID_PORTNUMBER;
+
+ if (read_port->port_number < DA9052_GPIO_MAX_PORTS_PER_REGISTER)
+ msg.addr = DA9052_STATUSC_REG;
+ else
+ msg.addr = DA9052_STATUSD_REG;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+
+ shift_value = msg.data &
+ (1 << DA9052_GPIO_SHIFT_COUNT(read_port->port_number));
+ read_port->read_write_value = (shift_value >>
+ DA9052_GPIO_SHIFT_COUNT(read_port->port_number));
+
+ return 0;
+}
+
+s32 da9052_gpio_multiple_read(struct da9052_gpio_multiple_read *multiple_port,
+ struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg[2];
+ u8 port_number = 0;
+ u8 loop_index = 0;
+ msg[loop_index++].addr = DA9052_STATUSC_REG;
+ msg[loop_index++].addr = DA9052_STATUSD_REG;
+
+ da9052_lock(da9052);
+ if (da9052->read_many(da9052, msg, loop_index)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+ loop_index = 0;
+ for (port_number = 0; port_number < DA9052_GPIO_MAX_PORTS_PER_REGISTER;
+ port_number++) {
+ multiple_port->signal_value[port_number] =
+ msg[loop_index].data & 1;
+ msg[loop_index].data = msg[loop_index].data >> 1;
+ }
+ loop_index++;
+ for (port_number = DA9052_GPIO_MAX_PORTS_PER_REGISTER;
+ port_number < DA9052_GPIO_MAX_PORTNUMBER; port_number++) {
+ multiple_port->signal_value[port_number] =
+ msg[loop_index].data & 1;
+ msg[loop_index].data = msg[loop_index].data >> 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(da9052_gpio_multiple_read);
+
+s32 da9052_gpio_write_port(struct da9052_gpio_read_write *write_port,
+ struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg;
+ u8 port_functionality = 0;
+ u8 bit_pos = 0;
+ msg.addr = DA9052_GPIO0001_REG + (write_port->port_number / 2);
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+
+ port_functionality =
+ (write_port->port_number % 2) ?
+ ((msg.data & DA9052_GPIO_ODD_PORT_FUNCTIONALITY) >>
+ DA9052_GPIO_NIBBLE_SHIFT) :
+ (msg.data & DA9052_GPIO_EVEN_PORT_FUNCTIONALITY);
+
+ if (port_functionality < 2)
+ return DA9052_GPIO_INVALID_PORTNUMBER;
+
+ bit_pos = (write_port->port_number % 2) ?
+ DA9052_GPIO_ODD_PORT_WRITE_MODE :
+ DA9052_GPIO_EVEN_PORT_WRITE_MODE;
+
+ if (write_port->read_write_value)
+ msg.data = msg.data | bit_pos;
+ else
+ msg.data = (msg.data & ~(bit_pos));
+
+ da9052_lock(da9052);
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+ return 0;
+}
+
+s32 da9052_gpio_configure_port(struct da9052_gpio *gpio_data,
+ struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg;
+ u8 register_value = 0;
+ u8 function = 0;
+ u8 port_functionality = 0;
+ msg.addr = (gpio_data->port_number / 2) + DA9052_GPIO0001_REG;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+
+ port_functionality =
+ (gpio_data->port_number % 2) ?
+ ((msg.data & DA9052_GPIO_ODD_PORT_FUNCTIONALITY) >>
+ DA9052_GPIO_NIBBLE_SHIFT) :
+ (msg.data & DA9052_GPIO_EVEN_PORT_FUNCTIONALITY);
+ if (port_functionality < INPUT)
+ return DA9052_GPIO_INVALID_PORTNUMBER;
+ if (gpio_data->gpio_config.input.type > ACTIVE_HIGH)
+ return DA9052_GPIO_INVALID_TYPE;
+ if (gpio_data->gpio_config.input.mode > DEBOUNCING_ON)
+ return DA9052_GPIO_INVALID_MODE;
+ function = gpio_data->gpio_function;
+ switch (function) {
+ case INPUT:
+ register_value = create_gpio_config_value(function,
+ gpio_data->gpio_config.input.type,
+ gpio_data->gpio_config.input.mode);
+ break;
+ case OUTPUT_OPENDRAIN:
+ case OUTPUT_PUSHPULL:
+ register_value = create_gpio_config_value(function,
+ gpio_data->gpio_config.input.type,
+ gpio_data->gpio_config.input.mode);
+ break;
+ default:
+ return DA9052_GPIO_INVALID_FUNCTION;
+ break;
+ }
+
+ if (gpio_data->port_number % 2) {
+ msg.data = (msg.data & ~(DA9052_GPIO_MASK_UPPER_NIBBLE)) |
+ (register_value << DA9052_GPIO_NIBBLE_SHIFT);
+ } else {
+ msg.data = (msg.data & ~(DA9052_GPIO_MASK_LOWER_NIBBLE)) |
+ register_value;
+ }
+ da9052_lock(da9052);
+ if (da9052->write(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return -EIO;
+ }
+ da9052_unlock(da9052);
+ return 0;
+}
+
+static s32 da9052_gpio_read(struct gpio_chip *gc, u32 offset)
+{
+ struct da9052_gpio_chip *gpio;
+ gpio = to_da9052_gpio(gc);
+ gpio->read_write.port_number = offset;
+ da9052_gpio_read_port(&gpio->read_write, gpio->da9052);
+ return gpio->read_write.read_write_value;
+}
+
+static void da9052_gpio_write(struct gpio_chip *gc, u32 offset, s32 value)
+{
+ struct da9052_gpio_chip *gpio;
+ gpio = to_da9052_gpio(gc);
+ gpio->read_write.port_number = offset;
+ gpio->read_write.read_write_value = (u8)value;
+ da9052_gpio_write_port(&gpio->read_write, gpio->da9052);
+}
+
+static s32 da9052_gpio_ip(struct gpio_chip *gc, u32 offset)
+{
+ struct da9052_gpio_chip *gpio;
+ gpio = to_da9052_gpio(gc);
+ gpio->gpio.gpio_function = INPUT;
+ gpio->gpio.gpio_config.input.type = ACTIVE_LOW;
+ gpio->gpio.gpio_config.input.mode = DEBOUNCING_ON;
+ gpio->gpio.port_number = offset;
+ return da9052_gpio_configure_port(&gpio->gpio, gpio->da9052);
+}
+
+static s32 da9052_gpio_op(struct gpio_chip *gc, u32 offset, s32 value)
+{
+ struct da9052_gpio_chip *gpio;
+ gpio = to_da9052_gpio(gc);
+ gpio->gpio.gpio_function = OUTPUT_PUSHPULL;
+ gpio->gpio.gpio_config.output.type = SUPPLY_VDD_IO1;
+ gpio->gpio.gpio_config.output.mode = value;
+ gpio->gpio.port_number = offset;
+ return da9052_gpio_configure_port(&gpio->gpio, gpio->da9052);
+}
+
+static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset)
+{
+ struct da9052_gpio_chip *gpio;
+ gpio = to_da9052_gpio(gc);
+ kobject_uevent(&gpio->gp.dev->kobj, KOBJ_CHANGE);
+ printk(KERN_INFO"gpio->gp.base +offset = %d\n", gpio->gp.base + offset);
+ printk(KERN_INFO"Test1\n\n");
+ return gpio->gp.base + offset;
+}
+
+static int __devinit da9052_gpio_probe(struct platform_device *pdev)
+{
+ struct da9052_gpio_chip *gpio;
+ struct da9052_platform_data *pdata = (pdev->dev.platform_data);
+ s32 ret;
+ gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+ if (gpio == NULL)
+ return -ENOMEM;
+ gpio->da9052 = dev_get_drvdata(pdev->dev.parent);
+ gpio->gp.get = da9052_gpio_read;
+ gpio->gp.direction_input = da9052_gpio_ip;
+ gpio->gp.direction_output = da9052_gpio_op;
+ gpio->gp.set = da9052_gpio_write;
+
+ gpio->gp.base = pdata->gpio_base;
+ gpio->gp.ngpio = DA9052_GPIO_MAX_PORTNUMBER;
+ gpio->gp.can_sleep = 1;
+ gpio->gp.dev = &pdev->dev;
+ gpio->gp.owner = THIS_MODULE;
+ gpio->gp.label = "da9052-gpio";
+ gpio->gp.to_irq = da9052_gpio_to_irq;
+
+ gpio->eh_data.eve_type = GPI8_EVE;
+ gpio->eh_data.call_back = &da9052_gpio_notifier;
+ ret = gpio->da9052->register_event_notifier(gpio->da9052,
+ &gpio->eh_data);
+
+ ret = write_default_gpio_values(gpio->da9052);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "GPIO initial config failed, %d\n",
+ ret);
+ goto ret;
+ }
+
+ ret = gpiochip_add(&gpio->gp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
+ ret);
+ goto ret;
+ }
+ platform_set_drvdata(pdev, gpio);
+
+ return ret;
+
+ret:
+ kfree(gpio);
+ return ret;
+
+}
+
+static int __devexit da9052_gpio_remove(struct platform_device *pdev)
+{
+ struct da9052_gpio_chip *gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ gpio->da9052->unregister_event_notifier
+ (gpio->da9052, &gpio->eh_data);
+ ret = gpiochip_remove(&gpio->gp);
+ if (ret == 0)
+ kfree(gpio);
+ return 0;
+}
+
+static struct platform_driver da9052_gpio_driver = {
+ .probe = da9052_gpio_probe,
+ .remove = __devexit_p(da9052_gpio_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_gpio_init(void)
+{
+ return platform_driver_register(&da9052_gpio_driver);
+}
+
+static void __exit da9052_gpio_exit(void)
+{
+ return platform_driver_unregister(&da9052_gpio_driver);
+}
+
+module_init(da9052_gpio_init);
+module_exit(da9052_gpio_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 GPIO Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/hwmon/da9052-adc.c b/drivers/hwmon/da9052-adc.c
new file mode 100644
index 000000000000..647e580e2131
--- /dev/null
+++ b/drivers/hwmon/da9052-adc.c
@@ -0,0 +1,644 @@
+/*
+ * da9052-adc.c -- ADC Driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define DRIVER_NAME "da9052-adc"
+
+static const char *input_names[] = {
+ [DA9052_ADC_VDDOUT] = "VDDOUT",
+ [DA9052_ADC_ICH] = "CHARGING CURRENT",
+ [DA9052_ADC_TBAT] = "BATTERY TEMP",
+ [DA9052_ADC_VBAT] = "BATTERY VOLTAGE",
+ [DA9052_ADC_ADCIN4] = "ADC INPUT 4",
+ [DA9052_ADC_ADCIN5] = "ADC INPUT 5",
+ [DA9052_ADC_ADCIN6] = "ADC INPUT 6",
+ [DA9052_ADC_TSI] = "TSI",
+ [DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP",
+ [DA9052_ADC_VBBAT] = "BACK-UP BATTERY TEMP",
+};
+
+
+int da9052_manual_read(struct da9052 *da9052,
+ unsigned char channel)
+{
+ unsigned char man_timeout_cnt = DA9052_ADC_MAX_MANCONV_RETRY_COUNT;
+ struct da9052_ssc_msg msg;
+ unsigned short calc_data;
+ unsigned int ret;
+ u16 data = 0;
+
+ msg.addr = DA9052_ADCMAN_REG;
+ msg.data = channel;
+ msg.data = (msg.data | DA9052_ADCMAN_MANCONV);
+
+ mutex_lock(&da9052->manconv_lock);
+ da9052_lock(da9052);
+
+ ret = da9052->write(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+
+ /* Wait for the event */
+ do {
+ msg.addr = DA9052_ADCCONT_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+
+ if (DA9052_ADCCONT_ADCMODE & msg.data)
+ msleep(1);
+ else
+ msleep(10);
+
+ msg.addr = DA9052_ADCMAN_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+
+ /* Counter to avoid endless while loop */
+ man_timeout_cnt--;
+ if (man_timeout_cnt == 1) {
+ if (!(msg.data & DA9052_ADCMAN_MANCONV))
+ break;
+ else
+ goto err_ssc_comm;
+ }
+ /* Wait until the MAN_CONV bit is cleared to zero */
+ } while (msg.data & DA9052_ADCMAN_MANCONV);
+
+ msg.addr = DA9052_ADCRESH_REG;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+
+ calc_data = (unsigned short)msg.data;
+ data = (calc_data << 2);
+
+ msg.addr = DA9052_ADCRESL_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+
+ /* Clear first 14 bits before ORing */
+ calc_data = (unsigned short)msg.data & 0x0003;
+ data |= calc_data;
+
+ mutex_unlock(&da9052->manconv_lock);
+
+ return data;
+err_ssc_comm:
+ mutex_unlock(&da9052->manconv_lock);
+ da9052_unlock(da9052);
+ return -EIO;
+}
+EXPORT_SYMBOL(da9052_manual_read);
+
+int da9052_read_tjunc(struct da9052 *da9052, char *buf)
+{
+ struct da9052_ssc_msg msg;
+ unsigned char temp;
+ int ret;
+
+ msg.addr = DA9052_TJUNCRES_REG;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+
+ temp = msg.data;
+
+ msg.addr = DA9052_TOFFSET_REG;
+ msg.data = 0;
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+ /* Calculate Junction temperature */
+ temp = (temp - msg.data);
+ *buf = temp;
+ return 0;
+err_ssc_comm:
+ da9052_unlock(da9052);
+ return -EIO;
+}
+EXPORT_SYMBOL(da9052_read_tjunc);
+
+int da9052_read_tbat_ich(struct da9052 *da9052, char *data, int channel_no)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ /* Read TBAT conversion result */
+ switch (channel_no) {
+ case DA9052_ADC_TBAT:
+ msg.addr = DA9052_TBATRES_REG;
+ break;
+ case DA9052_ADC_ICH:
+ msg.addr = DA9052_ICHGAV_REG;
+ break;
+ default:
+ return -EINVAL;
+ }
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+ *data = msg.data;
+ printk(KERN_INFO"msg.data 1= %d\n", msg.data);
+ msg.data = 28;
+ da9052_lock(da9052);
+ ret = da9052->write(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+ printk(KERN_INFO"msg.data2 = %d\n", msg.data);
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+ printk(KERN_INFO"msg.data3 = %d\n", msg.data);
+ return 0;
+
+err_ssc_comm:
+ da9052_unlock(da9052);
+ return ret;
+}
+EXPORT_SYMBOL(da9052_read_tbat_ich);
+
+static int da9052_start_adc(struct da9052 *da9052, unsigned channel)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ msg.addr = DA9052_ADCCONT_REG;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+
+ if (channel == DA9052_ADC_VDDOUT)
+ msg.data = (msg.data | DA9052_ADCCONT_AUTOVDDEN);
+ else if (channel == DA9052_ADC_ADCIN4)
+ msg.data = (msg.data | DA9052_ADCCONT_AUTOAD4EN);
+ else if (channel == DA9052_ADC_ADCIN5)
+ msg.data = (msg.data | DA9052_ADCCONT_AUTOAD5EN);
+ else if (channel == DA9052_ADC_ADCIN6)
+ msg.data = (msg.data | DA9052_ADCCONT_AUTOAD6EN);
+ else
+ return -EINVAL;
+
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+ return 0;
+
+err_ssc_comm:
+ da9052_unlock(da9052);
+ return -EIO;
+}
+
+static int da9052_stop_adc(struct da9052 *da9052, unsigned channel)
+{
+ int ret;
+ struct da9052_ssc_msg msg;
+
+ msg.addr = DA9052_ADCCONT_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+
+ if (channel == DA9052_ADC_VDDOUT)
+ msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOVDDEN));
+ else if (channel == DA9052_ADC_ADCIN4)
+ msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD4EN));
+ else if (channel == DA9052_ADC_ADCIN5)
+ msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD5EN));
+ else if (channel == DA9052_ADC_ADCIN6)
+ msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD6EN));
+ else
+ return -EINVAL;
+
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+
+ return 0;
+err_ssc_comm:
+ da9052_unlock(da9052);
+ return -EIO;
+}
+
+static ssize_t da9052_adc_read_start_stop(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+ struct da9052_ssc_msg msg;
+ int channel = to_sensor_dev_attr(devattr)->index;
+ int ret;
+
+ ret = da9052_start_adc(priv->da9052, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Read the ADC converted value */
+ switch (channel) {
+ case DA9052_ADC_VDDOUT:
+ msg.addr = DA9052_VDDRES_REG;
+ break;
+#if (DA9052_ADC_CONF_ADC4 == 1)
+ case DA9052_ADC_ADCIN4:
+ msg.addr = DA9052_ADCIN4RES_REG;
+ break;
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+ case DA9052_ADC_ADCIN5:
+ msg.addr = DA9052_ADCIN5RES_REG;
+ break;
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+ case DA9052_ADC_ADCIN6:
+ msg.addr = DA9052_ADCIN6RES_REG;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+ msg.data = 0;
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(priv->da9052);
+
+ ret = da9052_stop_adc(priv->da9052, channel);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", msg.data);
+
+err_ssc_comm:
+ da9052_unlock(priv->da9052);
+ return ret;
+}
+
+static ssize_t da9052_adc_read_ich(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_ICH);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%u\n", *buf);
+}
+
+static ssize_t da9052_adc_read_tbat(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_TBAT);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%u\n", *buf);
+}
+
+static ssize_t da9052_adc_read_vbat(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+ s32 ret;
+
+ ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBAT);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%u\n", ret);
+}
+
+static ssize_t da9052_adc_read_tjunc(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+ int ret;
+ ret = da9052_read_tjunc(priv->da9052, buf);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%u\n", *buf);
+}
+
+static ssize_t da9052_adc_read_vbbat(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+ s32 ret;
+
+ ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBBAT);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%u\n", ret);
+}
+
+static int da9052_adc_hw_init(struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ /* ADC channel 4 and 5 are by default enabled */
+#if (DA9052_ADC_CONF_ADC4 == 1)
+ msg.addr = DA9052_GPIO0001_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+
+ msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN));
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+#endif
+
+#if (DA9052_ADC_CONF_ADC5 == 1)
+ msg.addr = DA9052_GPIO0001_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+
+ msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN));
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+#endif
+
+#if (DA9052_ADC_CONF_ADC6 == 1)
+ msg.addr = DA9052_GPIO0203_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto err_ssc_comm;
+
+ msg.data = (msg.data & ~(DA9052_GPIO0203_GPIO2PIN));
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+#endif
+#if 0
+ /* By default configure the Measurement sequence interval to 10ms */
+ msg.addr = DA9052_ADCCONT_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+
+ /* Set the ADC MODE bit for 10msec sampling timer */
+ msg.data = (msg.data & ~(DA9052_ADCCONT_ADCMODE));
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0)
+ goto err_ssc_comm;
+ da9052_unlock(da9052);
+#endif
+ return 0;
+err_ssc_comm:
+ da9052_unlock(da9052);
+ return -EIO;
+}
+
+static ssize_t da9052_adc_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "da9052-adc\n");
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int channel = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%s\n", input_names[channel]);
+}
+#define DA9052_ADC_CHANNELS(id, name) \
+ static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
+ NULL, name)
+
+DA9052_ADC_CHANNELS(0, DA9052_ADC_VDDOUT);
+DA9052_ADC_CHANNELS(1, DA9052_ADC_ICH);
+DA9052_ADC_CHANNELS(2, DA9052_ADC_TBAT);
+DA9052_ADC_CHANNELS(3, DA9052_ADC_VBAT);
+#if (DA9052_ADC_CONF_ADC4 == 1)
+DA9052_ADC_CHANNELS(4, DA9052_ADC_ADCIN4);
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+DA9052_ADC_CHANNELS(5, DA9052_ADC_ADCIN5);
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+DA9052_ADC_CHANNELS(6, DA9052_ADC_ADCIN6);
+#endif
+DA9052_ADC_CHANNELS(7, DA9052_ADC_TSI);
+DA9052_ADC_CHANNELS(8, DA9052_ADC_TJUNC);
+DA9052_ADC_CHANNELS(9, DA9052_ADC_VBBAT);
+
+
+static DEVICE_ATTR(name, S_IRUGO, da9052_adc_show_name, NULL);
+static SENSOR_DEVICE_ATTR(read_vddout, S_IRUGO,
+ da9052_adc_read_start_stop, NULL,
+ DA9052_ADC_VDDOUT);
+static SENSOR_DEVICE_ATTR(read_ich, S_IRUGO, da9052_adc_read_ich, NULL,
+ DA9052_ADC_ICH);
+static SENSOR_DEVICE_ATTR(read_tbat, S_IRUGO, da9052_adc_read_tbat, NULL,
+ DA9052_ADC_TBAT);
+static SENSOR_DEVICE_ATTR(read_vbat, S_IRUGO, da9052_adc_read_vbat, NULL,
+ DA9052_ADC_VBAT);
+#if (DA9052_ADC_CONF_ADC4 == 1)
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
+ DA9052_ADC_ADCIN4);
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
+ DA9052_ADC_ADCIN5);
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
+ DA9052_ADC_ADCIN6);
+#endif
+static SENSOR_DEVICE_ATTR(read_tjunc, S_IRUGO, da9052_adc_read_tjunc, NULL,
+ DA9052_ADC_TJUNC);
+static SENSOR_DEVICE_ATTR(read_vbbat, S_IRUGO, da9052_adc_read_vbbat, NULL,
+ DA9052_ADC_VBBAT);
+
+static struct attribute *da9052_attr[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_read_vddout.dev_attr.attr,
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_read_ich.dev_attr.attr,
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_read_tbat.dev_attr.attr,
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+ &sensor_dev_attr_read_vbat.dev_attr.attr,
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+#if (DA9052_ADC_CONF_ADC4 == 1)
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_label.dev_attr.attr,
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+#endif
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_read_tjunc.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ &sensor_dev_attr_read_vbbat.dev_attr.attr,
+ &sensor_dev_attr_in9_label.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group da9052_group = {
+ .attrs = da9052_attr,
+};
+
+static int __init da9052_adc_probe(struct platform_device *pdev)
+{
+ struct da9052_adc_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->da9052 = dev_get_drvdata(pdev->dev.parent);
+
+ platform_set_drvdata(pdev, priv);
+
+ /* Register sysfs hooks */
+ ret = sysfs_create_group(&pdev->dev.kobj, &da9052_group);
+ if (ret)
+ goto out_err_create1;
+
+ priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(priv->hwmon_dev)) {
+ ret = PTR_ERR(priv->hwmon_dev);
+ goto out_err_create2;
+ }
+ /* Initializes the hardware for ADC module */
+ da9052_adc_hw_init(priv->da9052);
+
+ /* Initialize mutex required for ADC Manual read */
+ mutex_init(&priv->da9052->manconv_lock);
+
+ return 0;
+
+out_err_create2:
+ sysfs_remove_group(&pdev->dev.kobj, &da9052_group);
+out_err_create1:
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit da9052_adc_remove(struct platform_device *pdev)
+{
+ struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+
+ mutex_destroy(&priv->da9052->manconv_lock);
+
+ hwmon_device_unregister(priv->hwmon_dev);
+
+ sysfs_remove_group(&pdev->dev.kobj, &da9052_group);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver da9052_adc_driver = {
+ .remove = __devexit_p(da9052_adc_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init da9052_adc_init(void)
+{
+ return platform_driver_probe(&da9052_adc_driver, da9052_adc_probe);
+}
+module_init(da9052_adc_init);
+
+static void __exit da9052_adc_exit(void)
+{
+ platform_driver_unregister(&da9052_adc_driver);
+}
+module_exit(da9052_adc_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>")
+MODULE_DESCRIPTION("DA9052 ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c
new file mode 100644
index 000000000000..2271b59261fa
--- /dev/null
+++ b/drivers/input/misc/da9052_onkey.c
@@ -0,0 +1,133 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+#define DRIVER_NAME "da9052-onkey"
+
+struct da9052_onkey_data {
+ struct da9052 *da9052;
+ struct da9052_eh_nb eh_data;
+ struct input_dev *input;
+};
+
+static void da9052_onkey_report_event(struct da9052_eh_nb *eh_data,
+ unsigned int event)
+{
+ struct da9052_onkey_data *da9052_onkey =
+ container_of(eh_data, struct da9052_onkey_data, eh_data);
+ struct da9052_ssc_msg msg;
+ unsigned int ret;
+
+ /* Read the Evnet Register */
+ msg.addr = DA9052_EVENTB_REG;
+ da9052_lock(da9052_onkey->da9052);
+ ret = da9052_onkey->da9052->read(da9052_onkey->da9052, &msg);
+ if (ret) {
+ da9052_unlock(da9052_onkey->da9052);
+ return;
+ }
+ da9052_unlock(da9052_onkey->da9052);
+ msg.data = msg.data & DA9052_EVENTB_ENONKEY;
+
+ input_report_key(da9052_onkey->input, KEY_POWER, msg.data);
+ input_sync(da9052_onkey->input);
+ printk(KERN_INFO "DA9052 ONKEY EVENT REPORTED \n");
+}
+
+static int __devinit da9052_onkey_probe(struct platform_device *pdev)
+{
+ struct da9052_onkey_data *da9052_onkey;
+ int error;
+
+ da9052_onkey = kzalloc(sizeof(*da9052_onkey), GFP_KERNEL);
+ da9052_onkey->input = input_allocate_device();
+ if (!da9052_onkey->input) {
+ dev_err(&pdev->dev, "failed to allocate data device\n");
+ error = -ENOMEM;
+ goto fail1;
+ }
+ da9052_onkey->da9052 = dev_get_drvdata(pdev->dev.parent);
+
+ if (!da9052_onkey->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ error = -ENOMEM;
+ goto fail2;
+ }
+
+ da9052_onkey->input->evbit[0] = BIT_MASK(EV_KEY);
+ da9052_onkey->input->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+ da9052_onkey->input->name = "da9052-onkey";
+ da9052_onkey->input->phys = "da9052-onkey/input0";
+ da9052_onkey->input->dev.parent = &pdev->dev;
+
+ /* Set the EH structure */
+ da9052_onkey->eh_data.eve_type = ONKEY_EVE;
+ da9052_onkey->eh_data.call_back = &da9052_onkey_report_event;
+ error = da9052_onkey->da9052->register_event_notifier(
+ da9052_onkey->da9052,
+ &da9052_onkey->eh_data);
+ if (error)
+ goto fail2;
+
+ error = input_register_device(da9052_onkey->input);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to register input\
+ device,error: %d\n", error);
+ goto fail3;
+ }
+
+ platform_set_drvdata(pdev, da9052_onkey);
+
+ return 0;
+
+fail3:
+ da9052_onkey->da9052->unregister_event_notifier(da9052_onkey->da9052,
+ &da9052_onkey->eh_data);
+fail2:
+ input_free_device(da9052_onkey->input);
+fail1:
+ kfree(da9052_onkey);
+ return error;
+}
+
+static int __devexit da9052_onkey_remove(struct platform_device *pdev)
+{
+ struct da9052_onkey_data *da9052_onkey = pdev->dev.platform_data;
+ da9052_onkey->da9052->unregister_event_notifier(da9052_onkey->da9052,
+ &da9052_onkey->eh_data);
+ input_unregister_device(da9052_onkey->input);
+ kfree(da9052_onkey);
+
+ return 0;
+}
+
+static struct platform_driver da9052_onkey_driver = {
+ .probe = da9052_onkey_probe,
+ .remove = __devexit_p(da9052_onkey_remove),
+ .driver = {
+ .name = "da9052-onkey",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init da9052_onkey_init(void)
+{
+ return platform_driver_register(&da9052_onkey_driver);
+}
+
+static void __exit da9052_onkey_exit(void)
+{
+ platform_driver_unregister(&da9052_onkey_driver);
+}
+
+module_init(da9052_onkey_init);
+module_exit(da9052_onkey_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Onkey driver for DA9052");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c
new file mode 100644
index 000000000000..3b79cf3debfa
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi.c
@@ -0,0 +1,1448 @@
+/*
+ * da9052_tsi.c -- TSI driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/tsi_cfg.h>
+#include <linux/mfd/da9052/tsi.h>
+#include <linux/mfd/da9052/gpio.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define WAIT_FOR_PEN_DOWN 0
+#define WAIT_FOR_SAMPLING 1
+#define SAMPLING_ACTIVE 2
+
+static ssize_t __init da9052_tsi_create_input_dev(struct input_dev **ip_dev,
+ u8 n);
+static ssize_t read_da9052_reg(struct da9052 *da9052, u8 reg_addr);
+static ssize_t write_da9052_reg(struct da9052 *da9052, u8 reg_addr, u8 data);
+
+static void da9052_tsi_reg_pendwn_event(struct da9052_ts_priv *priv);
+static void da9052_tsi_reg_datardy_event(struct da9052_ts_priv *priv);
+static ssize_t da9052_tsi_config_delay(struct da9052_ts_priv *priv,
+ enum TSI_DELAY delay);
+static ssize_t da9052_tsi_config_measure_seq(struct da9052_ts_priv *priv,
+ enum TSI_MEASURE_SEQ seq);
+static ssize_t da9052_tsi_config_state(struct da9052_ts_priv *ts,
+ enum TSI_STATE state);
+static ssize_t da9052_tsi_set_sampling_mode(struct da9052_ts_priv *priv,
+ u8 interval);
+static ssize_t da9052_tsi_config_skip_slots(struct da9052_ts_priv *priv,
+ enum TSI_SLOT_SKIP skip);
+static ssize_t da9052_tsi_config_pen_detect(struct da9052_ts_priv *priv,
+ u8 flag);
+static ssize_t da9052_tsi_disable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq);
+static ssize_t da9052_tsi_enable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq);
+static ssize_t da9052_tsi_config_manual_mode(struct da9052_ts_priv *priv,
+ u8 coordinate);
+static ssize_t da9052_tsi_config_auto_mode(struct da9052_ts_priv *priv,
+ u8 state);
+static ssize_t da9052_tsi_config_gpio(struct da9052_ts_priv *priv);
+static ssize_t da9052_tsi_config_power_supply(struct da9052_ts_priv *priv,
+ u8 state);
+static struct da9052_tsi_info *get_tsi_drvdata(void);
+static void da9052_tsi_penup_event(struct da9052_ts_priv *priv);
+static s32 da9052_tsi_get_rawdata(struct da9052_tsi_reg *buf, u8 cnt);
+static ssize_t da9052_tsi_reg_proc_thread(void *ptr);
+static ssize_t da9052_tsi_resume(struct platform_device *dev);
+static ssize_t da9052_tsi_suspend(struct platform_device *dev,
+ pm_message_t state);
+struct da9052_tsi tsi_reg;
+struct da9052_tsi_info gda9052_tsi_info;
+
+static ssize_t write_da9052_reg(struct da9052 *da9052, u8 reg_addr, u8 data)
+{
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = reg_addr;
+ ssc_msg.data = data;
+ ret = da9052->write(da9052, &ssc_msg);
+ if (ret) {
+ DA9052_DEBUG("%s: ",__FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_write Failed %d\n",ret );
+ }
+
+ return ret;
+}
+
+static ssize_t read_da9052_reg(struct da9052 *da9052, u8 reg_addr)
+{
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = reg_addr;
+ ssc_msg.data = 0;
+ ret = da9052->read(da9052, &ssc_msg);
+ if (ret) {
+ DA9052_DEBUG("%s: ",__FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_read Failed => %d\n" ,ret);
+ return -ret;
+ }
+ return ssc_msg.data;
+}
+
+static struct da9052_tsi_info *get_tsi_drvdata(void)
+{
+ return &gda9052_tsi_info;
+}
+
+static ssize_t da9052_tsi_config_measure_seq(struct da9052_ts_priv *priv,
+ enum TSI_MEASURE_SEQ seq)
+{
+ ssize_t ret = 0;
+ u8 data = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (seq > 1)
+ return -EINVAL;
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = (u8)ret;
+
+ if (seq == XYZP_MODE)
+ data = enable_xyzp_mode(data);
+ else if (seq == XP_MODE)
+ data = enable_xp_mode(data);
+ else {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid Value passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_set_sampling_mode(struct da9052_ts_priv *priv,
+ u8 mode)
+{
+ u8 data = 0;
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_ADCCONT_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ data = (u8)ret;
+
+ if (mode == ECONOMY_MODE)
+ data = adc_mode_economy_mode(data);
+ else if (mode == FAST_MODE)
+ data = adc_mode_fast_mode(data);
+ else {
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid interval passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_ADCCONT_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ switch (mode) {
+ case ECONOMY_MODE:
+ priv->tsi_reg_data_poll_interval =
+ TSI_ECO_MODE_REG_DATA_PROCESSING_INTERVAL;
+ priv->tsi_raw_data_poll_interval =
+ TSI_ECO_MODE_RAW_DATA_PROCESSING_INTERVAL;
+ break;
+ case FAST_MODE:
+ priv->tsi_reg_data_poll_interval =
+ TSI_FAST_MODE_REG_DATA_PROCESSING_INTERVAL;
+ priv->tsi_raw_data_poll_interval =
+ TSI_FAST_MODE_RAW_DATA_PROCESSING_INTERVAL;
+ break;
+ default:
+ DA9052_DEBUG("DA9052_TSI:%s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid interval passed \n" );
+ return -EINVAL;
+ }
+
+ ts->tsi_penup_count =
+ (u32)priv->tsi_pdata->pen_up_interval /
+ priv->tsi_reg_data_poll_interval;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_delay(struct da9052_ts_priv *priv,
+ enum TSI_DELAY delay)
+{
+ ssize_t ret = 0;
+ u8 data = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (delay > priv->tsi_pdata->max_tsi_delay) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" invalid value for tsi delay!!!\n" );
+ return -EINVAL;
+ }
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if(ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = clear_bits((u8)ret, DA9052_TSICONTA_TSIDELAY);
+
+ data = set_bits(data, (delay << priv->tsi_pdata->tsi_delay_bit_shift));
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+
+ return 0;
+}
+
+ssize_t da9052_tsi_config_skip_slots(struct da9052_ts_priv *priv,
+ enum TSI_SLOT_SKIP skip)
+{
+ ssize_t ret = 0;
+ u8 data = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (skip > priv->tsi_pdata->max_tsi_skip_slot) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" invalid value for tsi skip slots!!!\n" );
+ return -EINVAL;
+ }
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = clear_bits((u8)ret, DA9052_TSICONTA_TSISKIP);
+
+ data = set_bits(data, (skip << priv->tsi_pdata->tsi_skip_bit_shift));
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_config_skip_slots:");
+ DA9052_DEBUG(" write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_state(struct da9052_ts_priv *priv,
+ enum TSI_STATE state)
+{
+ s32 ret;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (ts->tsi_conf.state == state)
+ return 0;
+
+ switch (state) {
+ case TSI_AUTO_MODE:
+ ts->tsi_zero_data_cnt = 0;
+ priv->early_data_flag = TRUE;
+ priv->debounce_over = FALSE;
+ priv->win_reference_valid = FALSE;
+
+ clean_tsi_fifos(priv);
+
+ ret = da9052_tsi_config_auto_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_manual_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_power_supply(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_enable_irq(priv, TSI_PEN_DWN);
+ if (ret)
+ return ret;
+ ts->tsi_conf.tsi_pendown_irq_mask = RESET;
+
+ ret = da9052_tsi_disable_irq(priv, TSI_DATA_RDY);
+ if (ret)
+ return ret;
+ ts->tsi_conf.tsi_ready_irq_mask = SET;
+
+ da9052_tsi_reg_pendwn_event(priv);
+ da9052_tsi_reg_datardy_event(priv);
+
+ ret = da9052_tsi_config_pen_detect(priv, ENABLE);
+ if (ret)
+ return ret;
+ break;
+
+ case TSI_IDLE:
+ ts->pen_dwn_event = RESET;
+
+ ret = da9052_tsi_config_pen_detect(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_auto_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_manual_mode(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ ret = da9052_tsi_config_power_supply(priv, DISABLE);
+ if (ret)
+ return ret;
+
+ if (ts->pd_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->pd_nb);
+ ts->pd_reg_status = RESET;
+ }
+ break;
+
+ default:
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Invalid state passed");
+ return -EINVAL;
+ }
+
+ ts->tsi_conf.state = state;
+
+ return 0;
+}
+
+static void da9052_tsi_reg_pendwn_event(struct da9052_ts_priv *priv)
+{
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (ts->pd_reg_status) {
+ DA9052_DEBUG("%s: Pen down ",__FUNCTION__);
+ DA9052_DEBUG("Registeration is already done \n");
+ return;
+ }
+
+ priv->pd_nb.eve_type = PEN_DOWN_EVE;
+ priv->pd_nb.call_back = &da9052_tsi_pen_down_handler;
+
+ ret = priv->da9052->register_event_notifier(priv->da9052, &priv->pd_nb);
+ if (ret) {
+ DA9052_DEBUG("%s: EH Registeration",__FUNCTION__);
+ DA9052_DEBUG(" Failed: ret = %d\n",ret );
+ ts->pd_reg_status = RESET;
+ } else
+ ts->pd_reg_status = SET;
+
+ priv->os_data_cnt = 0;
+ priv->raw_data_cnt = 0;
+
+ return;
+}
+
+static void da9052_tsi_reg_datardy_event(struct da9052_ts_priv *priv)
+{
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if(ts->datardy_reg_status)
+ {
+ DA9052_DEBUG("%s: Data Ready ",__FUNCTION__);
+ DA9052_DEBUG("Registeration is already done \n");
+ return;
+ }
+
+ priv->datardy_nb.eve_type = TSI_READY_EVE;
+ priv->datardy_nb.call_back = &da9052_tsi_data_ready_handler;
+
+ ret = priv->da9052->register_event_notifier(priv->da9052,
+ &priv->datardy_nb);
+
+ if(ret)
+ {
+ DA9052_DEBUG("%s: EH Registeration",__FUNCTION__);
+ DA9052_DEBUG(" Failed: ret = %d\n",ret );
+ ts->datardy_reg_status = RESET;
+ } else
+ ts->datardy_reg_status = SET;
+
+ return;
+}
+
+static ssize_t __init da9052_tsi_create_input_dev(struct input_dev **ip_dev,
+ u8 n)
+{
+ u8 i;
+ s32 ret;
+ struct input_dev *dev = NULL;
+
+ if (!n)
+ return -EINVAL;
+
+ for (i = 0; i < n; i++) {
+ dev = input_allocate_device();
+ if (!dev) {
+ DA9052_DEBUG(KERN_ERR "%s:%s():memory allocation for \
+ inputdevice failed\n", __FILE__,
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ ip_dev[i] = dev;
+ switch (i) {
+ case TSI_INPUT_DEVICE_OFF:
+ dev->name = DA9052_TSI_INPUT_DEV;
+ dev->phys = "input(tsi)";
+ break;
+ default:
+ break;
+ }
+ }
+ dev->id.vendor = DA9052_VENDOR_ID;
+ dev->id.product = DA9052_PRODUCT_ID;
+ dev->id.bustype = BUS_RS232;
+ dev->id.version = TSI_VERSION;
+ dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ dev->evbit[0] = (BIT_MASK(EV_SYN) |
+ BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_ABS));
+
+ input_set_abs_params(dev, ABS_X, 0, DA9052_DISPLAY_X_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, DA9052_DISPLAY_Y_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, DA9052_TOUCH_PRESSURE_MAX,
+ 0, 0);
+
+ ret = input_register_device(dev);
+ if (ret) {
+ DA9052_DEBUG(KERN_ERR "%s: Could ", __FUNCTION__);
+ DA9052_DEBUG("not register input device(touchscreen)!\n");
+ ret = -EIO;
+ goto fail;
+ }
+ return 0;
+
+fail:
+ for (; i-- != 0; )
+ input_free_device(ip_dev[i]);
+ return -EINVAL;
+}
+
+static ssize_t __init da9052_tsi_init_drv(struct da9052_ts_priv *priv)
+{
+ u8 cnt = 0;
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if ((DA9052_GPIO_PIN_3 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_4 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_5 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_6 != DA9052_GPIO_CONFIG_TSI) ||
+ (DA9052_GPIO_PIN_7 != DA9052_GPIO_CONFIG_TSI)) {
+ printk(KERN_ERR"DA9052_TSI: Configure DA9052 GPIO ");
+ printk(KERN_ERR"pins for TSI\n");
+ return -EINVAL;
+ }
+
+ ret = da9052_tsi_config_gpio(priv);
+
+ ret = da9052_tsi_config_state(priv, TSI_IDLE);
+ ts->tsi_conf.state = TSI_IDLE;
+
+ da9052_tsi_config_measure_seq(priv, TSI_MODE_VALUE);
+
+ da9052_tsi_config_skip_slots(priv, TSI_SLOT_SKIP_VALUE);
+
+ da9052_tsi_config_delay(priv, TSI_DELAY_VALUE);
+
+ da9052_tsi_set_sampling_mode(priv, DEFAULT_TSI_SAMPLING_MODE);
+
+ ts->tsi_calib = get_calib_config();
+
+ ret = da9052_tsi_create_input_dev(ts->input_devs, NUM_INPUT_DEVS);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s: ", __FUNCTION__);
+ DA9052_DEBUG("da9052_tsi_create_input_dev Failed \n" );
+ return ret;
+ }
+
+ da9052_init_tsi_fifos(priv);
+
+ init_completion(&priv->tsi_reg_proc_thread.notifier);
+ priv->tsi_reg_proc_thread.state = ACTIVE;
+ priv->tsi_reg_proc_thread.pid =
+ kernel_thread(da9052_tsi_reg_proc_thread,
+ priv, CLONE_KERNEL | SIGCHLD);
+
+ init_completion(&priv->tsi_raw_proc_thread.notifier);
+ priv->tsi_raw_proc_thread.state = ACTIVE;
+ priv->tsi_raw_proc_thread.pid =
+ kernel_thread(da9052_tsi_raw_proc_thread,
+ priv, CLONE_KERNEL | SIGCHLD);
+
+ ret = da9052_tsi_config_state(priv, DEFAULT_TSI_STATE);
+ if (ret) {
+ for (cnt = 0; cnt < NUM_INPUT_DEVS; cnt++) {
+ if (ts->input_devs[cnt] != NULL)
+ input_free_device(ts->input_devs[cnt]);
+ }
+ }
+
+ return 0;
+}
+
+u32 da9052_tsi_get_input_dev(u8 off)
+{
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (off > NUM_INPUT_DEVS-1)
+ return -EINVAL;
+
+ return (u32)ts->input_devs[off];
+}
+
+static ssize_t da9052_tsi_config_pen_detect(struct da9052_ts_priv *priv,
+ u8 flag)
+{
+ u8 data;
+ u32 ret;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("%s:", __FUNCTION__);
+ DA9052_DEBUG(" read_da9052_reg Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ if (flag == ENABLE)
+ data = set_bits((u8)ret, DA9052_TSICONTA_PENDETEN);
+ else if (flag == DISABLE)
+ data = clear_bits((u8)ret, DA9052_TSICONTA_PENDETEN);
+ else {
+ DA9052_DEBUG("%s:", __FUNCTION__);
+ DA9052_DEBUG(" Invalid flag passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret < 0) {
+ DA9052_DEBUG("%s:", __FUNCTION__);
+ DA9052_DEBUG(" write_da9052_reg Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+ return 0;
+}
+
+static ssize_t da9052_tsi_disable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq)
+{
+ u8 data = 0;
+ ssize_t ret =0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_disable_irq:");
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ data = ret;
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ data = mask_pendwn_irq(data);
+ break;
+ case TSI_DATA_RDY:
+ data = mask_tsi_rdy_irq(data);
+ break;
+ default:
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_disable_irq:");
+ DA9052_DEBUG("Invalid IRQ passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+ ret = write_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_disable_irq:");
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ ts->tsi_conf.tsi_pendown_irq_mask = SET;
+ break;
+ case TSI_DATA_RDY:
+ ts->tsi_conf.tsi_ready_irq_mask = SET;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+static ssize_t da9052_tsi_enable_irq(struct da9052_ts_priv *priv,
+ enum TSI_IRQ tsi_irq)
+{
+ u8 data =0;
+ ssize_t ret =0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ da9052_lock(priv->da9052);
+ ret = read_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_enable_irq:");
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = ret;
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ data = unmask_pendwn_irq(data);
+ break;
+ case TSI_DATA_RDY:
+ data = unmask_tsi_rdy_irq(data);
+ break;
+ default:
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_enable_irq:");
+ DA9052_DEBUG("Invalid IRQ passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+ ret = write_da9052_reg(priv->da9052, DA9052_IRQMASKB_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI:da9052_tsi_enable_irq:");
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+ switch (tsi_irq) {
+ case TSI_PEN_DWN:
+ ts->tsi_conf.tsi_pendown_irq_mask = RESET;
+ break;
+ case TSI_DATA_RDY:
+ ts->tsi_conf.tsi_ready_irq_mask = RESET;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+static ssize_t da9052_tsi_config_gpio(struct da9052_ts_priv *priv)
+{
+ u8 idx = 0;
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg[priv->tsi_pdata->num_gpio_tsi_register];
+
+ ssc_msg[idx++].addr = DA9052_GPIO0203_REG;
+ ssc_msg[idx++].addr = DA9052_GPIO0405_REG;
+ ssc_msg[idx++].addr = DA9052_GPIO0607_REG;
+
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read_many(priv->da9052, ssc_msg,idx);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_read_many Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ idx = 0;
+ ssc_msg[idx].data = clear_bits(ssc_msg[idx].data,
+ DA9052_GPIO0203_GPIO3PIN);
+ idx++;
+ ssc_msg[idx].data = clear_bits(ssc_msg[idx].data,
+ (DA9052_GPIO0405_GPIO4PIN | DA9052_GPIO0405_GPIO5PIN));
+ idx++;
+ ssc_msg[idx].data = clear_bits(ssc_msg[idx].data,
+ (DA9052_GPIO0607_GPIO6PIN | DA9052_GPIO0607_GPIO7PIN));
+ idx++;
+
+ ret = priv->da9052->write_many(priv->da9052, ssc_msg,idx);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("da9052_ssc_read_many Failed\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+s32 da9052_pm_configure_ldo(struct da9052_ts_priv *priv,
+ struct da9052_ldo_config ldo_config)
+{
+ struct da9052_ssc_msg msg;
+ u8 reg_num;
+ u8 ldo_volt;
+ u8 ldo_volt_bit = 0;
+ u8 ldo_conf_bit = 0;
+ u8 ldo_en_bit = 0;
+ s8 ldo_pd_bit = -1;
+ s32 ret = 0;
+
+ if (validate_ldo9_mV(ldo_config.ldo_volt))
+ return INVALID_LDO9_VOLT_VALUE;
+
+ ldo_volt = ldo9_mV_to_reg(ldo_config.ldo_volt);
+
+ reg_num = DA9052_LDO9_REG;
+ ldo_volt_bit = DA9052_LDO9_VLDO9;
+ ldo_conf_bit = DA9052_LDO9_LDO9CONF;
+ ldo_en_bit = DA9052_LDO9_LDO9EN;
+
+ da9052_lock(priv->da9052);
+
+ msg.addr = reg_num;
+
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+ msg.data = ldo_volt |
+ (ldo_config.ldo_conf ? ldo_conf_bit : 0) |
+ (msg.data & ldo_en_bit);
+
+ ret = priv->da9052->write(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ if (-1 != ldo_pd_bit) {
+ msg.addr = DA9052_PULLDOWN_REG;
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ msg.data = (ldo_config.ldo_pd ?
+ set_bits(msg.data, ldo_pd_bit) :
+ clear_bits(msg.data, ldo_pd_bit));
+
+ ret = priv->da9052->write(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+
+s32 da9052_pm_set_ldo(struct da9052_ts_priv *priv, u8 ldo_num, u8 flag)
+{
+ struct da9052_ssc_msg msg;
+ u8 reg_num = 0;
+ u8 value = 0;
+ s32 ret = 0;
+
+ DA9052_DEBUG("I am in function: %s\n", __FUNCTION__);
+
+ reg_num = DA9052_LDO9_REG;
+ value = DA9052_LDO9_LDO9EN;
+ da9052_lock(priv->da9052);
+
+ msg.addr = reg_num;
+
+ ret = priv->da9052->read(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ msg.data = flag ?
+ set_bits(msg.data, value) :
+ clear_bits(msg.data, value);
+
+ ret = priv->da9052->write(priv->da9052, &msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_power_supply(struct da9052_ts_priv *priv,
+ u8 state)
+{
+ struct da9052_ldo_config ldo_config;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (state != ENABLE && state != DISABLE) {
+ DA9052_DEBUG("DA9052_TSI: %s: ", __FUNCTION__);
+ DA9052_DEBUG("Invalid state Passed\n" );
+ return -EINVAL;
+ }
+
+ ldo_config.ldo_volt = priv->tsi_pdata->tsi_supply_voltage;
+ ldo_config.ldo_num = priv->tsi_pdata->tsi_ref_source;
+ ldo_config.ldo_conf = RESET;
+
+ if (da9052_pm_configure_ldo(priv, ldo_config))
+ return -EINVAL;
+
+ if (da9052_pm_set_ldo(priv, priv->tsi_pdata->tsi_ref_source, state))
+ return -EINVAL;
+
+ if (state == ENABLE)
+ ts->tsi_conf.ldo9_en = SET;
+ else
+ ts->tsi_conf.ldo9_en = RESET;
+
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_auto_mode(struct da9052_ts_priv *priv,
+ u8 state)
+{
+ u8 data;
+ s32 ret = 0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (state != ENABLE && state != DISABLE)
+ return -EINVAL;
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTA_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = (u8)ret;
+
+ if (state == ENABLE)
+ data = set_auto_tsi_en(data);
+ else if (state == DISABLE)
+ data = reset_auto_tsi_en(data);
+ else {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("Invalid Parameter Passed \n" );
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTA_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Failed to configure Auto TSI mode\n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+ ts->tsi_conf.auto_cont.da9052_tsi_cont_a = data;
+ return 0;
+}
+
+static ssize_t da9052_tsi_config_manual_mode(struct da9052_ts_priv *priv,
+ u8 state)
+{
+ u8 data = 0;
+ ssize_t ret=0;
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+
+ if (state != ENABLE && state != DISABLE) {
+ DA9052_DEBUG("DA9052_TSI: %s: ", __FUNCTION__);
+ DA9052_DEBUG("Invalid state Passed\n" );
+ return -EINVAL;
+ }
+
+ da9052_lock(priv->da9052);
+
+ ret = read_da9052_reg(priv->da9052, DA9052_TSICONTB_REG);
+ if (ret < 0) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("read_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ data = (u8)ret;
+ if (state == DISABLE)
+ data = disable_tsi_manual_mode(data);
+ else
+ data = enable_tsi_manual_mode(data);
+
+ ret = write_da9052_reg(priv->da9052, DA9052_TSICONTB_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("write_da9052_reg Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+
+ if (state == DISABLE)
+ ts->tsi_conf.man_cont.tsi_cont_b.tsi_man = RESET;
+ else
+ ts->tsi_conf.man_cont.tsi_cont_b.tsi_man = SET;
+
+ data = 0;
+ data = set_bits(data, DA9052_ADC_TSI);
+
+ ret = write_da9052_reg(priv->da9052, DA9052_ADCMAN_REG, data);
+ if (ret) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG("ADC write Failed \n" );
+ da9052_unlock(priv->da9052);
+ return ret;
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+
+static u32 da9052_tsi_get_reg_data(struct da9052_ts_priv *priv)
+{
+ u32 free_cnt, copy_cnt, cnt;
+
+ if (down_interruptible(&priv->tsi_reg_fifo.lock))
+ return 0;
+
+ copy_cnt = 0;
+
+ if ((priv->tsi_reg_fifo.head - priv->tsi_reg_fifo.tail) > 1) {
+ free_cnt = get_reg_free_space_cnt(priv);
+ if (free_cnt > TSI_POLL_SAMPLE_CNT)
+ free_cnt = TSI_POLL_SAMPLE_CNT;
+
+ cnt = da9052_tsi_get_rawdata(
+ &priv->tsi_reg_fifo.data[priv->tsi_reg_fifo.tail],
+ free_cnt);
+
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+
+ copy_cnt = cnt;
+
+ while (cnt--)
+ incr_with_wrap_reg_fifo(priv->tsi_reg_fifo.tail);
+
+ } else if ((priv->tsi_reg_fifo.head - priv->tsi_reg_fifo.tail) <= 0) {
+
+ free_cnt = (TSI_REG_DATA_BUF_SIZE - priv->tsi_reg_fifo.tail);
+ if (free_cnt > TSI_POLL_SAMPLE_CNT) {
+ free_cnt = TSI_POLL_SAMPLE_CNT;
+
+ cnt = da9052_tsi_get_rawdata(
+ &priv->tsi_reg_fifo.data[priv->tsi_reg_fifo.tail],
+ free_cnt);
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+ copy_cnt = cnt;
+
+ while (cnt--)
+ incr_with_wrap_reg_fifo(
+ priv->tsi_reg_fifo.tail);
+ } else {
+ if (free_cnt) {
+ cnt = da9052_tsi_get_rawdata(
+ &priv->
+ tsi_reg_fifo.data[priv->
+ tsi_reg_fifo.tail],
+ free_cnt
+ );
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+ copy_cnt = cnt;
+ while (cnt--)
+ incr_with_wrap_reg_fifo(
+ priv->tsi_reg_fifo.tail);
+ }
+ free_cnt = priv->tsi_reg_fifo.head;
+ if (free_cnt > TSI_POLL_SAMPLE_CNT - copy_cnt)
+ free_cnt = TSI_POLL_SAMPLE_CNT - copy_cnt;
+ if (free_cnt) {
+ cnt = da9052_tsi_get_rawdata(
+ &priv->tsi_reg_fifo.data[priv->
+ tsi_reg_fifo.tail], free_cnt
+ );
+ if (cnt > free_cnt) {
+ DA9052_DEBUG("EH copied more data");
+ return -EINVAL;
+ }
+
+ copy_cnt += cnt;
+
+ while (cnt--)
+ incr_with_wrap_reg_fifo(
+ priv->tsi_reg_fifo.tail);
+ }
+ }
+ } else
+ copy_cnt = 0;
+
+ up(&priv->tsi_reg_fifo.lock);
+
+ return copy_cnt;
+}
+
+
+static ssize_t da9052_tsi_reg_proc_thread(void *ptr)
+{
+ u32 data_cnt;
+ ssize_t ret = 0;
+ struct da9052_tsi_info *ts;
+ struct da9052_ts_priv *priv = (struct da9052_ts_priv *)ptr;
+
+ set_freezable();
+
+ while (priv->tsi_reg_proc_thread.state == ACTIVE) {
+
+ try_to_freeze();
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(priv->
+ tsi_reg_data_poll_interval));
+
+ ts = get_tsi_drvdata();
+
+ if (!ts->pen_dwn_event)
+ continue;
+
+ data_cnt = da9052_tsi_get_reg_data(priv);
+
+ da9052_tsi_process_reg_data(priv);
+
+ if (data_cnt)
+ ts->tsi_zero_data_cnt = 0;
+ else {
+ if ((++(ts->tsi_zero_data_cnt)) >
+ ts->tsi_penup_count) {
+ ts->pen_dwn_event = RESET;
+ da9052_tsi_penup_event(priv);
+ }
+ }
+ }
+
+ complete_and_exit(&priv->tsi_reg_proc_thread.notifier, 0);
+ return 0;
+}
+
+
+static void da9052_tsi_penup_event(struct da9052_ts_priv *priv)
+{
+
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+ struct input_dev *ip_dev =
+ (struct input_dev *)da9052_tsi_get_input_dev(
+ (u8)TSI_INPUT_DEVICE_OFF);
+
+ if (da9052_tsi_config_auto_mode(priv, DISABLE))
+ goto exit;
+ ts->tsi_conf.auto_cont.tsi_cont_a.auto_tsi_en = RESET;
+
+ if (da9052_tsi_config_power_supply(priv, ENABLE))
+ goto exit;
+
+ ts->tsi_conf.ldo9_en = RESET;
+
+ if (da9052_tsi_enable_irq(priv, TSI_PEN_DWN))
+ goto exit;
+ ts->tsi_conf.tsi_pendown_irq_mask = RESET;
+ tsi_reg.tsi_state = WAIT_FOR_PEN_DOWN;
+
+ if (da9052_tsi_disable_irq(priv, TSI_DATA_RDY))
+ goto exit;
+
+ ts->tsi_zero_data_cnt = 0;
+ priv->early_data_flag = TRUE;
+ priv->debounce_over = FALSE;
+ priv->win_reference_valid = FALSE;
+
+ printk(KERN_INFO "The raw data count is %d \n", priv->raw_data_cnt);
+ printk(KERN_INFO "The OS data count is %d \n", priv->os_data_cnt);
+ printk(KERN_INFO "PEN UP DECLARED \n");
+ input_report_abs(ip_dev, BTN_TOUCH, 0);
+ input_sync(ip_dev);
+ priv->os_data_cnt = 0;
+ priv->raw_data_cnt = 0;
+
+exit:
+ clean_tsi_fifos(priv);
+ return;
+}
+
+void da9052_tsi_pen_down_handler(struct da9052_eh_nb *eh_data, u32 event)
+{
+ ssize_t ret = 0;
+ struct da9052_ts_priv *priv =
+ container_of(eh_data, struct da9052_ts_priv, pd_nb);
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+ struct input_dev *ip_dev =
+ (struct input_dev*)da9052_tsi_get_input_dev(
+ (u8)TSI_INPUT_DEVICE_OFF);
+
+ if (tsi_reg.tsi_state != WAIT_FOR_PEN_DOWN)
+ return;
+
+ tsi_reg.tsi_state = WAIT_FOR_SAMPLING;
+
+ if (ts->tsi_conf.state != TSI_AUTO_MODE) {
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Configure TSI to auto mode.\n" );
+ DA9052_DEBUG("DA9052_TSI: %s:", __FUNCTION__);
+ DA9052_DEBUG(" Then call this API.\n" );
+ goto fail;
+ }
+
+ if (da9052_tsi_config_power_supply(priv, ENABLE))
+ goto fail;
+
+ if (da9052_tsi_disable_irq(priv, TSI_PEN_DWN))
+ goto fail;
+
+ if (da9052_tsi_config_auto_mode(priv, ENABLE))
+ goto fail;
+ ts->tsi_conf.auto_cont.tsi_cont_a.auto_tsi_en = SET;
+
+ if (da9052_tsi_enable_irq(priv, TSI_DATA_RDY))
+ goto fail;
+
+ input_sync(ip_dev);
+
+ ts->tsi_rdy_event = (DA9052_EVENTB_ETSIREADY & (event>>8));
+ ts->pen_dwn_event = (DA9052_EVENTB_EPENDOWN & (event>>8));
+
+ tsi_reg.tsi_state = SAMPLING_ACTIVE;
+
+ goto success;
+
+fail:
+ if (ts->pd_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->pd_nb);
+ ts->pd_reg_status = RESET;
+
+ priv->da9052->register_event_notifier(priv->da9052,
+ &priv->datardy_nb);
+ da9052_tsi_reg_pendwn_event(priv);
+ }
+
+success:
+ ret = 0;
+ printk(KERN_INFO "Exiting PEN DOWN HANDLER \n");
+}
+
+void da9052_tsi_data_ready_handler(struct da9052_eh_nb *eh_data, u32 event)
+{
+ struct da9052_ssc_msg tsi_data[4];
+ s32 ret;
+ struct da9052_ts_priv *priv =
+ container_of(eh_data, struct da9052_ts_priv, datardy_nb);
+
+ if (tsi_reg.tsi_state != SAMPLING_ACTIVE)
+ return;
+
+ tsi_data[0].addr = DA9052_TSIXMSB_REG;
+ tsi_data[1].addr = DA9052_TSIYMSB_REG;
+ tsi_data[2].addr = DA9052_TSILSB_REG;
+ tsi_data[3].addr = DA9052_TSIZMSB_REG;
+
+ tsi_data[0].data = 0;
+ tsi_data[1].data = 0;
+ tsi_data[2].data = 0;
+ tsi_data[3].data = 0;
+
+ da9052_lock(priv->da9052);
+
+ ret = priv->da9052->read_many(priv->da9052, tsi_data, 4);
+ if (ret) {
+ DA9052_DEBUG("Error in reading TSI data \n" );
+ da9052_unlock(priv->da9052);
+ return;
+ }
+ da9052_unlock(priv->da9052);
+
+#if 1
+ mutex_lock(&tsi_reg.tsi_fifo_lock);
+
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].x_msb = tsi_data[0].data;
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].y_msb = tsi_data[1].data;
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].lsb = tsi_data[2].data;
+ tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_end].z_msb = tsi_data[3].data;
+ incr_with_wrap(tsi_reg.tsi_fifo_end);
+
+ if (tsi_reg.tsi_fifo_end == tsi_reg.tsi_fifo_start)
+ tsi_reg.tsi_fifo_start++;
+
+ mutex_unlock(&tsi_reg.tsi_fifo_lock);
+#endif
+// printk(KERN_INFO "Exiting Data ready handler \n");
+}
+
+static s32 da9052_tsi_get_rawdata(struct da9052_tsi_reg *buf, u8 cnt) {
+ u32 data_cnt = 0;
+ u32 rem_data_cnt = 0;
+
+ mutex_lock(&tsi_reg.tsi_fifo_lock);
+
+ if (tsi_reg.tsi_fifo_start < tsi_reg.tsi_fifo_end) {
+ data_cnt = (tsi_reg.tsi_fifo_end - tsi_reg.tsi_fifo_start);
+
+ if (cnt < data_cnt)
+ data_cnt = cnt;
+
+ memcpy(buf, &tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_start],
+ sizeof(struct da9052_tsi_reg) * data_cnt);
+
+ tsi_reg.tsi_fifo_start += data_cnt;
+
+ if (tsi_reg.tsi_fifo_start == tsi_reg.tsi_fifo_end) {
+ tsi_reg.tsi_fifo_start = 0;
+ tsi_reg.tsi_fifo_end = 0;
+ }
+ } else if (tsi_reg.tsi_fifo_start > tsi_reg.tsi_fifo_end) {
+ data_cnt = ((TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start)
+ + tsi_reg.tsi_fifo_end);
+
+ if (cnt < data_cnt)
+ data_cnt = cnt;
+
+ if (data_cnt <= (TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start)) {
+ memcpy(buf, &tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_start],
+ sizeof(struct da9052_tsi_reg) * data_cnt);
+
+ tsi_reg.tsi_fifo_start += data_cnt;
+ if (tsi_reg.tsi_fifo_start >= TSI_FIFO_SIZE)
+ tsi_reg.tsi_fifo_start = 0;
+ } else {
+ memcpy(buf, &tsi_reg.tsi_fifo[tsi_reg.tsi_fifo_start],
+ sizeof(struct da9052_tsi_reg)
+ * (TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start));
+
+ rem_data_cnt = (data_cnt -
+ (TSI_FIFO_SIZE - tsi_reg.tsi_fifo_start));
+
+ memcpy(buf, &tsi_reg.tsi_fifo[0],
+ sizeof(struct da9052_tsi_reg) * rem_data_cnt);
+
+ tsi_reg.tsi_fifo_start = rem_data_cnt;
+ }
+
+ if (tsi_reg.tsi_fifo_start == tsi_reg.tsi_fifo_end) {
+ tsi_reg.tsi_fifo_start = 0;
+ tsi_reg.tsi_fifo_end = 0;
+ }
+ } else
+ data_cnt = 0;
+
+ mutex_unlock(&tsi_reg.tsi_fifo_lock);
+
+ return data_cnt;
+}
+
+static ssize_t da9052_tsi_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ printk(KERN_INFO "%s: called\n", __FUNCTION__);
+ return 0;
+}
+
+static ssize_t da9052_tsi_resume(struct platform_device *dev)
+{
+ printk(KERN_INFO "%s: called\n", __FUNCTION__);
+ return 0;
+}
+
+static s32 __devinit da9052_tsi_probe(struct platform_device *pdev)
+{
+
+ struct da9052_ts_priv *priv;
+ struct da9052_tsi_platform_data *pdata = pdev->dev.platform_data;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, priv);
+
+ priv->tsi_pdata = pdata;
+
+ if (da9052_tsi_init_drv(priv))
+ return -EFAULT;
+
+ mutex_init(&tsi_reg.tsi_fifo_lock);
+ tsi_reg.tsi_state = WAIT_FOR_PEN_DOWN;
+
+ printk(KERN_INFO "TSI Drv Successfully Inserted %s\n",
+ DA9052_TSI_DEVICE_NAME);
+ return 0;
+}
+
+static int __devexit da9052_tsi_remove(struct platform_device *pdev)
+{
+ struct da9052_ts_priv *priv = platform_get_drvdata(pdev);
+ struct da9052_tsi_info *ts = get_tsi_drvdata();
+ s32 ret = 0, i = 0;
+
+ ret = da9052_tsi_config_state(priv, TSI_IDLE);
+ if (!ret)
+ return -EINVAL;
+
+ if (ts->pd_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->pd_nb);
+ ts->pd_reg_status = RESET;
+ }
+
+ if (ts->datardy_reg_status) {
+ priv->da9052->unregister_event_notifier(priv->da9052,
+ &priv->datardy_nb);
+ ts->datardy_reg_status = RESET;
+ }
+
+ mutex_destroy(&tsi_reg.tsi_fifo_lock);
+
+ priv->tsi_reg_proc_thread.state = INACTIVE;
+ wait_for_completion(&priv->tsi_reg_proc_thread.notifier);
+
+ priv->tsi_raw_proc_thread.state = INACTIVE;
+ wait_for_completion(&priv->tsi_raw_proc_thread.notifier);
+
+ for (i = 0; i < NUM_INPUT_DEVS; i++) {
+ input_unregister_device(ts->input_devs[i]);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ DA9052_DEBUG(KERN_DEBUG "Removing %s \n", DA9052_TSI_DEVICE_NAME);
+
+ return 0;
+}
+
+static struct platform_driver da9052_tsi_driver = {
+ .probe = da9052_tsi_probe,
+ .remove = __devexit_p(da9052_tsi_remove),
+ .suspend = da9052_tsi_suspend,
+ .resume = da9052_tsi_resume,
+ .driver = {
+ .name = DA9052_TSI_DEVICE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_tsi_init(void)
+{
+ printk("DA9052 TSI Device Driver, v1.0\n");
+ return platform_driver_register(&da9052_tsi_driver);
+}
+module_init(da9052_tsi_init);
+
+static void __exit da9052_tsi_exit(void)
+{
+ printk(KERN_ERR "TSI Driver %s Successfully Removed \n",
+ DA9052_TSI_DEVICE_NAME);
+ return;
+
+}
+module_exit(da9052_tsi_exit);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/input/touchscreen/da9052_tsi_calibrate.c b/drivers/input/touchscreen/da9052_tsi_calibrate.c
new file mode 100644
index 000000000000..4ffd0bbe7483
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi_calibrate.c
@@ -0,0 +1,107 @@
+/*
+ * da9052_tsi_calibrate.c -- TSI Calibration driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/mfd/da9052/tsi.h>
+
+static struct Calib_xform_matrix_t xform = {
+ .An = DA9052_TSI_CALIB_AN,
+ .Bn = DA9052_TSI_CALIB_BN,
+ .Cn = DA9052_TSI_CALIB_CN,
+ .Dn = DA9052_TSI_CALIB_DN,
+ .En = DA9052_TSI_CALIB_EN,
+ .Fn = DA9052_TSI_CALIB_FN,
+ .Divider = DA9052_TSI_CALIB_DIVIDER
+};
+
+static struct calib_cfg_t calib = {
+ .calibrate_flag = TSI_USE_CALIBRATION,
+};
+
+struct calib_cfg_t *get_calib_config(void)
+{
+ return &calib;
+}
+
+ssize_t da9052_tsi_set_calib_matrix(struct da9052_tsi_data *displayPtr,
+ struct da9052_tsi_data *screenPtr)
+{
+
+ int retValue = SUCCESS ;
+
+ xform.Divider = ((screenPtr[0].x - screenPtr[1].x)
+ * (screenPtr[1].y - screenPtr[2].y))
+ - ((screenPtr[1].x - screenPtr[2].x)
+ * (screenPtr[0].y - screenPtr[1].y));
+
+ if (xform.Divider == 0)
+ retValue = -FAILURE;
+ else {
+ xform.An = ((displayPtr[0].x - displayPtr[1].x)
+ * (screenPtr[1].y - screenPtr[2].y))
+ - ((displayPtr[1].x - displayPtr[2].x)
+ * (screenPtr[0].y - screenPtr[1].y));
+
+ xform.Bn = ((displayPtr[1].x - displayPtr[2].x)
+ * (screenPtr[0].x - screenPtr[1].x))
+ - ((screenPtr[1].x - screenPtr[2].x)
+ * (displayPtr[0].x - displayPtr[1].x));
+
+ xform.Cn = (displayPtr[0].x * xform.Divider)
+ - (screenPtr[0].x * xform.An)
+ - (screenPtr[0].y * xform.Bn);
+
+ xform.Dn = ((displayPtr[0].y - displayPtr[1].y)
+ * (screenPtr[1].y - screenPtr[2].y))
+ - ((displayPtr[1].y - displayPtr[2].y)
+ * (screenPtr[0].y - screenPtr[1].y));
+
+ xform.En = ((displayPtr[1].y - displayPtr[2].y)
+ * (screenPtr[0].x - screenPtr[1].x))
+ - ((screenPtr[1].x - screenPtr[2].x)
+ * (displayPtr[0].y - displayPtr[1].y));
+
+ xform.Fn = (displayPtr[0].y * xform.Divider)
+ - (screenPtr[0].x * xform.Dn)
+ - (screenPtr[0].y * xform.En);
+ }
+
+ return retValue;
+}
+
+ssize_t da9052_tsi_get_calib_display_point(struct da9052_tsi_data *displayPtr)
+{
+ int retValue = TRUE;
+ struct da9052_tsi_data screen_coord;
+
+ screen_coord = *displayPtr;
+ if (xform.Divider != 0) {
+ displayPtr->x = ((xform.An * screen_coord.x) +
+ (xform.Bn * screen_coord.y) +
+ xform.Cn
+ ) / xform.Divider;
+
+ displayPtr->y = ((xform.Dn * screen_coord.x) +
+ (xform.En * screen_coord.y) +
+ xform.Fn
+ ) / xform.Divider;
+ } else
+ retValue = FALSE;
+
+#if DA9052_TSI_CALIB_DATA_PROFILING
+ printk("C\tX\t%4d\tY\t%4d\n",
+ displayPtr->x,
+ displayPtr->y);
+#endif
+ return retValue;
+}
diff --git a/drivers/input/touchscreen/da9052_tsi_filter.c b/drivers/input/touchscreen/da9052_tsi_filter.c
new file mode 100644
index 000000000000..16467edf386f
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi_filter.c
@@ -0,0 +1,489 @@
+/*
+ * da9052_tsi_filter.c -- TSI filter driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/freezer.h>
+#include <linux/mfd/da9052/tsi.h>
+
+#define get_abs(x) (x < 0 ? ((-1) * x) : (x))
+
+#define WITHIN_WINDOW(x, y) ((get_abs(x) <= TSI_X_WINDOW_SIZE) && \
+ (get_abs(y) <= TSI_Y_WINDOW_SIZE))
+
+#define FIRST_SAMPLE 0
+
+#define incr_with_wrap_raw_fifo(x) \
+ if (++x >= TSI_RAW_DATA_BUF_SIZE) \
+ x = 0
+
+#define decr_with_wrap_raw_fifo(x) \
+ if (--x < 0) \
+ x = (TSI_RAW_DATA_BUF_SIZE-1)
+
+static u32 get_raw_data_cnt(struct da9052_ts_priv *priv);
+static void da9052_tsi_convert_reg_to_coord(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data);
+static inline void clean_tsi_reg_fifo(struct da9052_ts_priv *priv);
+static inline void clean_tsi_raw_fifo(struct da9052_ts_priv *priv);
+
+#if (ENABLE_AVERAGE_FILTER)
+static void da9052_tsi_avrg_filter(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *tsi_avg_data);
+
+#endif
+#if (ENABLE_TSI_DEBOUNCE)
+static s32 da9052_tsi_calc_debounce_data(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data);
+
+#endif
+# if (ENABLE_WINDOW_FILTER)
+static s32 diff_within_window(struct da9052_tsi_data *prev_raw_data,
+ struct da9052_tsi_data *cur_raw_data);
+#endif
+static s32 da9052_tsi_window_filter(struct da9052_ts_priv *ts,
+ struct da9052_tsi_data *raw_data);
+
+void clean_tsi_fifos(struct da9052_ts_priv *priv)
+{
+ clean_tsi_raw_fifo(priv);
+ clean_tsi_reg_fifo(priv);
+}
+
+void __init da9052_init_tsi_fifos(struct da9052_ts_priv *priv)
+{
+ init_MUTEX(&priv->tsi_raw_fifo.lock);
+ init_MUTEX(&priv->tsi_reg_fifo.lock);
+
+ clean_tsi_raw_fifo(priv);
+ clean_tsi_reg_fifo(priv);
+}
+
+u32 get_reg_data_cnt(struct da9052_ts_priv *priv)
+{
+ u8 reg_data_cnt;
+
+ if (priv->tsi_reg_fifo.head <= priv->tsi_reg_fifo.tail) {
+ reg_data_cnt = (priv->tsi_reg_fifo.tail -
+ priv->tsi_reg_fifo.head);
+ } else {
+ reg_data_cnt = (priv->tsi_reg_fifo.tail +
+ (TSI_REG_DATA_BUF_SIZE -
+ priv->tsi_reg_fifo.head));
+ }
+
+ return reg_data_cnt;
+}
+
+u32 get_reg_free_space_cnt(struct da9052_ts_priv *priv)
+{
+ u32 free_cnt;
+
+ if (priv->tsi_reg_fifo.head <= priv->tsi_reg_fifo.tail) {
+ free_cnt = ((TSI_REG_DATA_BUF_SIZE - 1) -
+ (priv->tsi_reg_fifo.tail - priv->tsi_reg_fifo.head));
+ } else
+ free_cnt = ((priv->tsi_reg_fifo.head - priv->tsi_reg_fifo.tail)
+ - 1);
+
+ return free_cnt;
+}
+
+void da9052_tsi_process_reg_data(struct da9052_ts_priv *priv)
+{
+ s32 ret;
+ struct da9052_tsi_data tmp_raw_data;
+ u32 reg_data_cnt;
+
+ if (down_interruptible(&priv->tsi_reg_fifo.lock))
+ return;
+
+ reg_data_cnt = get_reg_data_cnt(priv);
+
+ while (reg_data_cnt-- > 0) {
+
+ ret = 0;
+
+ if (get_raw_data_cnt(priv) >= (TSI_RAW_DATA_BUF_SIZE - 1)) {
+ DA9052_DEBUG("%s: RAW data FIFO is full\n",
+ __FUNCTION__);
+ break;
+ }
+
+ da9052_tsi_convert_reg_to_coord(priv, &tmp_raw_data);
+
+ if ((tmp_raw_data.x < TS_X_MIN) ||
+ (tmp_raw_data.x > TS_X_MAX) ||
+ (tmp_raw_data.y < TS_Y_MIN) ||
+ (tmp_raw_data.y > TS_Y_MAX)) {
+ DA9052_DEBUG("%s: ", __FUNCTION__);
+ DA9052_DEBUG("sample beyond touchscreen panel ");
+ DA9052_DEBUG("dimensions\n");
+ continue;
+ }
+
+#if (ENABLE_TSI_DEBOUNCE)
+ if (debounce_over == FALSE) {
+ ret =
+ da9052_tsi_calc_debounce_data(priv, &tmp_raw_data);
+ if (ret != SUCCESS)
+ continue;
+ }
+#endif
+
+# if (ENABLE_WINDOW_FILTER)
+ ret = da9052_tsi_window_filter(priv, &tmp_raw_data);
+ if (ret != SUCCESS)
+ continue;
+#endif
+ priv->early_data_flag = FALSE;
+
+ if (down_interruptible(&priv->tsi_raw_fifo.lock)) {
+ DA9052_DEBUG("%s: Failed to ", __FUNCTION__);
+ DA9052_DEBUG("acquire RAW FIFO Lock!\n");
+
+ up(&priv->tsi_reg_fifo.lock);
+ return;
+ }
+
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = tmp_raw_data;
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+ up(&priv->tsi_raw_fifo.lock);
+ }
+
+
+ up(&priv->tsi_reg_fifo.lock);
+
+ return;
+}
+
+static u32 get_raw_data_cnt(struct da9052_ts_priv *priv)
+{
+ u32 raw_data_cnt;
+
+ if (priv->tsi_raw_fifo.head <= priv->tsi_raw_fifo.tail)
+ raw_data_cnt =
+ (priv->tsi_raw_fifo.tail - priv->tsi_raw_fifo.head);
+ else
+ raw_data_cnt =
+ (priv->tsi_raw_fifo.tail + (TSI_RAW_DATA_BUF_SIZE -
+ priv->tsi_raw_fifo.head));
+
+ return raw_data_cnt;
+}
+
+static void da9052_tsi_convert_reg_to_coord(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data)
+{
+
+ struct da9052_tsi_reg *src;
+ struct da9052_tsi_data *dst = raw_data;
+
+ src = &priv->tsi_reg_fifo.data[priv->tsi_reg_fifo.head];
+
+ dst->x = (src->x_msb << X_MSB_SHIFT);
+ dst->x |= (src->lsb & X_LSB_MASK) >> X_LSB_SHIFT;
+
+ dst->y = (src->y_msb << Y_MSB_SHIFT);
+ dst->y |= (src->lsb & Y_LSB_MASK) >> Y_LSB_SHIFT;
+
+ dst->z = (src->z_msb << Z_MSB_SHIFT);
+ dst->z |= (src->lsb & Z_LSB_MASK) >> Z_LSB_SHIFT;
+
+#if DA9052_TSI_RAW_DATA_PROFILING
+ printk("R\tX\t%4d\tY\t%4d\tZ\t%4d\n",
+ (u16)dst->x,
+ (u16)dst->y,
+ (u16)dst->z);
+#endif
+ priv->raw_data_cnt++;
+ incr_with_wrap_reg_fifo(priv->tsi_reg_fifo.head);
+}
+
+#if (ENABLE_AVERAGE_FILTER)
+static void da9052_tsi_avrg_filter(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *tsi_avg_data)
+{
+ u8 cnt;
+
+ if (down_interruptible(&priv->tsi_raw_fifo.lock)) {
+ printk("%s: No RAW Lock !\n", __FUNCTION__);
+ return;
+ }
+
+ (*tsi_avg_data) = priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head];
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+
+ for (cnt = 2; cnt <= TSI_AVERAGE_FILTER_SIZE; cnt++) {
+
+ tsi_avg_data->x +=
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].x;
+ tsi_avg_data->y +=
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].y;
+ tsi_avg_data->z +=
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].z;
+
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+ }
+
+ up(&priv->tsi_raw_fifo.lock);
+
+ tsi_avg_data->x /= TSI_AVERAGE_FILTER_SIZE;
+ tsi_avg_data->y /= TSI_AVERAGE_FILTER_SIZE;
+ tsi_avg_data->z /= TSI_AVERAGE_FILTER_SIZE;
+
+#if DA9052_TSI_AVG_FLT_DATA_PROFILING
+ printk("A\tX\t%4d\tY\t%4d\tZ\t%4d\n",
+ (u16)tsi_avg_data->x,
+ (u16)tsi_avg_data->y,
+ (u16)tsi_avg_data->z);
+#endif
+
+ return;
+}
+#endif
+
+s32 da9052_tsi_raw_proc_thread(void *ptr)
+{
+ struct da9052_tsi_data coord;
+ u8 calib_ok, range_ok;
+ struct calib_cfg_t *tsi_calib = get_calib_config();
+ struct input_dev *ip_dev = (struct input_dev *)
+ da9052_tsi_get_input_dev(
+ (u8)TSI_INPUT_DEVICE_OFF);
+ struct da9052_ts_priv *priv = (struct da9052_ts_priv *)ptr;
+
+ set_freezable();
+
+ while (priv->tsi_raw_proc_thread.state == ACTIVE) {
+
+ try_to_freeze();
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(
+ msecs_to_jiffies(priv->tsi_raw_data_poll_interval));
+
+ if (priv->early_data_flag || (get_raw_data_cnt(priv) == 0))
+ continue;
+
+ calib_ok = TRUE;
+ range_ok = TRUE;
+
+#if (ENABLE_AVERAGE_FILTER)
+
+ if (get_raw_data_cnt(priv) < TSI_AVERAGE_FILTER_SIZE)
+ continue;
+
+ da9052_tsi_avrg_filter(priv, &coord);
+
+#else
+
+ if (down_interruptible(&priv->tsi_raw_fifo.lock))
+ continue;
+
+ coord = priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head];
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+
+ up(&priv->tsi_raw_fifo.lock);
+
+#endif
+
+ if (tsi_calib->calibrate_flag) {
+ calib_ok = da9052_tsi_get_calib_display_point(&coord);
+
+ if ((coord.x < DISPLAY_X_MIN) ||
+ (coord.x > DISPLAY_X_MAX) ||
+ (coord.y < DISPLAY_Y_MIN) ||
+ (coord.y > DISPLAY_Y_MAX))
+ range_ok = FALSE;
+ }
+
+ if (calib_ok && range_ok) {
+ input_report_abs(ip_dev, BTN_TOUCH, 1);
+ input_report_abs(ip_dev, ABS_X, coord.x);
+ input_report_abs(ip_dev, ABS_Y, coord.y);
+ input_sync(ip_dev);
+
+ priv->os_data_cnt++;
+
+#if DA9052_TSI_OS_DATA_PROFILING
+ printk("O\tX\t%4d\tY\t%4d\tZ\t%4d\n", (u16)coord.x,
+ (u16)coord.y, (u16)coord.z);
+#endif
+ } else {
+ if (!calib_ok) {
+ DA9052_DEBUG("%s: ", __FUNCTION__);
+ DA9052_DEBUG("calibration Failed\n");
+ }
+ if (!range_ok) {
+ DA9052_DEBUG("%s: ", __FUNCTION__);
+ DA9052_DEBUG("sample beyond display ");
+ DA9052_DEBUG("panel dimension\n");
+ }
+ }
+ }
+ priv->tsi_raw_proc_thread.thread_task = NULL;
+ complete_and_exit(&priv->tsi_raw_proc_thread.notifier, 0);
+ return 0;
+
+}
+
+#if (ENABLE_TSI_DEBOUNCE)
+static s32 da9052_tsi_calc_debounce_data(struct da9052_tsi_data *raw_data)
+{
+#if (TSI_DEBOUNCE_DATA_CNT)
+ u8 cnt;
+ struct da9052_tsi_data temp = {.x = 0, .y = 0, .z = 0};
+
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = (*raw_data);
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+ if (get_raw_data_cnt(priv) <= TSI_DEBOUNCE_DATA_CNT)
+ return -FAILURE;
+
+ for (cnt = 1; cnt <= TSI_DEBOUNCE_DATA_CNT; cnt++) {
+ temp.x += priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].x;
+ temp.y += priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].y;
+ temp.z += priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.head].z;
+
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.head);
+ }
+
+ temp.x /= TSI_DEBOUNCE_DATA_CNT;
+ temp.y /= TSI_DEBOUNCE_DATA_CNT;
+ temp.z /= TSI_DEBOUNCE_DATA_CNT;
+
+ priv->tsi_raw_fifo.tail = priv->tsi_raw_fifo.head;
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = temp;
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+#if DA9052_TSI_PRINT_DEBOUNCED_DATA
+ printk("D: X: %d Y: %d Z: %d\n",
+ (u16)temp.x, (u16)temp.y, (u16)temp.z);
+#endif
+
+#endif
+ priv->debounce_over = TRUE;
+
+ return 0;
+}
+#endif
+
+# if (ENABLE_WINDOW_FILTER)
+static s32 diff_within_window(struct da9052_tsi_data *prev_raw_data,
+ struct da9052_tsi_data *cur_raw_data)
+{
+ s32 ret = -EINVAL;
+ s32 x, y;
+ x = ((cur_raw_data->x) - (prev_raw_data->x));
+ y = ((cur_raw_data->y) - (prev_raw_data->y));
+
+ if (WITHIN_WINDOW(x, y)) {
+
+#if DA9052_TSI_WIN_FLT_DATA_PROFILING
+ printk("W\tX\t%4d\tY\t%4d\tZ\t%4d\n",
+ (u16)cur_raw_data->x,
+ (u16)cur_raw_data->y,
+ (u16)cur_raw_data->z);
+#endif
+ ret = 0;
+ }
+ return ret;
+}
+
+static s32 da9052_tsi_window_filter(struct da9052_ts_priv *priv,
+ struct da9052_tsi_data *raw_data)
+{
+ u8 ref_found;
+ u32 cur, next;
+ s32 ret = -EINVAL;
+ static struct da9052_tsi_data prev_raw_data;
+
+ if (priv->win_reference_valid == TRUE) {
+
+#if DA9052_TSI_PRINT_PREVIOUS_DATA
+ printk("P: X: %d Y: %d Z: %d\n",
+ (u16)prev_raw_data.x, (u16)prev_raw_data.y,
+ (u16)prev_raw_data.z);
+#endif
+ ret = diff_within_window(&prev_raw_data, raw_data);
+ if (!ret)
+ prev_raw_data = (*raw_data);
+ } else {
+ priv->tsi_raw_fifo.data[priv->tsi_raw_fifo.tail] = (*raw_data);
+ incr_with_wrap_raw_fifo(priv->tsi_raw_fifo.tail);
+
+ if (get_raw_data_cnt(priv) == SAMPLE_CNT_FOR_WIN_REF) {
+
+ ref_found = FALSE;
+
+ next = cur = priv->tsi_raw_fifo.head;
+ incr_with_wrap_raw_fifo(next);
+
+ while (next <= priv->tsi_raw_fifo.tail) {
+ ret = diff_within_window(
+ &priv->tsi_raw_fifo.data[cur],
+ &priv->tsi_raw_fifo.data[next]
+ );
+ if (ret == SUCCESS) {
+ ref_found = TRUE;
+ break;
+ }
+ incr_with_wrap_raw_fifo(cur);
+ incr_with_wrap_raw_fifo(next);
+
+ }
+
+ if (ref_found == FALSE)
+ priv->tsi_raw_fifo.tail =
+ priv->tsi_raw_fifo.head;
+ else {
+ prev_raw_data = priv->tsi_raw_fifo.data[cur];
+
+ prev_raw_data.x +=
+ priv->tsi_raw_fifo.data[next].x;
+ prev_raw_data.y +=
+ priv->tsi_raw_fifo.data[next].y;
+ prev_raw_data.z +=
+ priv->tsi_raw_fifo.data[next].z;
+
+ prev_raw_data.x = prev_raw_data.x / 2;
+ prev_raw_data.y = prev_raw_data.y / 2;
+ prev_raw_data.z = prev_raw_data.z / 2;
+
+ (*raw_data) = prev_raw_data;
+
+ priv->tsi_raw_fifo.tail =
+ priv->tsi_raw_fifo.head;
+
+ priv->win_reference_valid = TRUE;
+ ret = SUCCESS;
+ }
+ }
+ }
+ return ret;
+}
+#endif
+static inline void clean_tsi_reg_fifo(struct da9052_ts_priv *priv)
+{
+ priv->tsi_reg_fifo.head = FIRST_SAMPLE;
+ priv->tsi_reg_fifo.tail = FIRST_SAMPLE;
+}
+
+static inline void clean_tsi_raw_fifo(struct da9052_ts_priv *priv)
+{
+ priv->tsi_raw_fifo.head = FIRST_SAMPLE;
+ priv->tsi_raw_fifo.tail = FIRST_SAMPLE;
+}
+
diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c
new file mode 100644
index 000000000000..187d4d2372e0
--- /dev/null
+++ b/drivers/leds/leds-da9052.c
@@ -0,0 +1,307 @@
+/*
+ * leds-da9052.c -- LED Driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/led.h>
+
+#define DRIVER_NAME "da9052-leds"
+
+#define DA9052_LED4_PRESENT 1
+#define DA9052_LED5_PRESENT 1
+
+
+struct da9052_led_data {
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct da9052 *da9052;
+ int id;
+ int new_brightness;
+ int is_led4_present;
+ int is_led5_present;
+};
+
+#define GPIO14_PIN 2 /* GPO Open Drain */
+#define GPIO14_TYPE 0 /* VDD_IO1 */
+#define GPIO14_MODE 1 /* Output High */
+
+#define GPIO15_PIN 2 /* GPO Open Drain */
+#define GPIO15_TYPE 0 /* VDD_IO1 */
+#define GPIO15_MODE 1 /* Output High */
+
+#define MAXIMUM_PWM 95
+#define MASK_GPIO14 0x0F
+#define MASK_GPIO15 0xF0
+#define GPIO15_PIN_BIT_POSITION 4
+
+static void da9052_led_work(struct work_struct *work)
+{
+ struct da9052_led_data *led = container_of(work,
+ struct da9052_led_data, work);
+ int reg = 0;
+ int led_dim_bit = 0;
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+ switch (led->id) {
+ case DA9052_LED_4:
+ reg = DA9052_LED4CONT_REG;
+ led_dim_bit = DA9052_LED4CONT_LED4DIM;
+ break;
+ case DA9052_LED_5:
+ reg = DA9052_LED5CONT_REG;
+ led_dim_bit = DA9052_LED5CONT_LED5DIM;
+ break;
+ }
+
+ if (led->new_brightness > MAXIMUM_PWM)
+ led->new_brightness = MAXIMUM_PWM;
+
+ /* Always enable DIM feature
+ * This feature can be disabled if required
+ */
+ msg.addr = reg;
+ msg.data = led->new_brightness | led_dim_bit;
+ da9052_lock(led->da9052);
+ ret = led->da9052->write(led->da9052, &msg);
+ if (ret) {
+ da9052_unlock(led->da9052);
+ return;
+ }
+ da9052_unlock(led->da9052);
+}
+
+static void da9052_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct da9052_led_data *led;
+
+ led = container_of(led_cdev, struct da9052_led_data, cdev);
+ led->new_brightness = value;
+ schedule_work(&led->work);
+}
+
+static int __devinit da9052_led_setup(struct da9052_led_data *led)
+{
+ int reg = 0;
+ int ret = 0;
+
+ struct da9052_ssc_msg msg;
+
+ switch (led->id) {
+ case DA9052_LED_4:
+ reg = DA9052_LED4CONT_REG;
+ break;
+ case DA9052_LED_5:
+ reg = DA9052_LED5CONT_REG;
+ break;
+ }
+
+ msg.addr = reg;
+ msg.data = 0;
+
+ da9052_lock(led->da9052);
+ ret = led->da9052->write(led->da9052, &msg);
+ if (ret) {
+ da9052_unlock(led->da9052);
+ return ret;
+ }
+ da9052_unlock(led->da9052);
+ return ret;
+}
+
+static int da9052_leds_prepare(struct da9052_led_data *led)
+{
+ int ret = 0;
+ struct da9052_ssc_msg msg;
+
+ da9052_lock(led->da9052);
+
+ if (1 == led->is_led4_present) {
+ msg.addr = DA9052_GPIO1415_REG;
+ msg.data = 0;
+
+ ret = led->da9052->read(led->da9052, &msg);
+ if (ret)
+ goto out;
+ msg.data = msg.data & ~(MASK_GPIO14);
+ msg.data = msg.data | (
+ GPIO14_PIN |
+ (GPIO14_TYPE ? DA9052_GPIO1415_GPIO14TYPE : 0) |
+ (GPIO14_MODE ? DA9052_GPIO1415_GPIO14MODE : 0));
+
+ ret = led->da9052->write(led->da9052, &msg);
+ if (ret)
+ goto out;
+ }
+
+ if (1 == led->is_led5_present) {
+ msg.addr = DA9052_GPIO1415_REG;
+ msg.data = 0;
+
+ ret = led->da9052->read(led->da9052, &msg);
+ if (ret)
+ goto out;
+ msg.data = msg.data & ~(MASK_GPIO15);
+ msg.data = msg.data |
+ (((GPIO15_PIN << GPIO15_PIN_BIT_POSITION) |
+ (GPIO15_TYPE ? DA9052_GPIO1415_GPIO15TYPE : 0) |
+ (GPIO15_MODE ? DA9052_GPIO1415_GPIO15MODE : 0))
+ );
+ ret = led->da9052->write(led->da9052, &msg);
+ if (ret)
+ goto out;
+ }
+
+ da9052_unlock(led->da9052);
+ return ret;
+out:
+ da9052_unlock(led->da9052);
+ return ret;
+}
+
+static int __devinit da9052_led_probe(struct platform_device *pdev)
+{
+ struct da9052_leds_platform_data *pdata = (pdev->dev.platform_data);
+ struct da9052_led_platform_data *led_cur;
+ struct da9052_led_data *led, *led_dat;
+ int ret, i;
+ int init_led = 0;
+
+ if (pdata->num_leds < 1 || pdata->num_leds > DA9052_LED_MAX) {
+ dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds);
+ return -EINVAL;
+ }
+
+ led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&pdev->dev, "failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ led->is_led4_present = DA9052_LED4_PRESENT;
+ led->is_led5_present = DA9052_LED5_PRESENT;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ led_dat = &led[i];
+ led_cur = &pdata->led[i];
+ if (led_cur->id < 0) {
+ dev_err(&pdev->dev, "invalid id %d\n", led_cur->id);
+ ret = -EINVAL;
+ goto err_register;
+ }
+
+ if (init_led & (1 << led_cur->id)) {
+ dev_err(&pdev->dev, "led %d already initialized\n",
+ led_cur->id);
+ ret = -EINVAL;
+ goto err_register;
+ }
+
+ init_led |= 1 << led_cur->id;
+
+ led_dat->cdev.name = led_cur->name;
+ // led_dat->cdev.default_trigger = led_cur[i]->default_trigger;
+ led_dat->cdev.brightness_set = da9052_led_set;
+ led_dat->cdev.brightness = LED_OFF;
+ led_dat->id = led_cur->id;
+ led_dat->da9052 = dev_get_drvdata(pdev->dev.parent);
+
+ INIT_WORK(&led_dat->work, da9052_led_work);
+
+ ret = led_classdev_register(pdev->dev.parent, &led_dat->cdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register led %d\n",
+ led_dat->id);
+ goto err_register;
+
+ }
+ ret = da9052_led_setup(led_dat);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to init led %d\n",
+ led_dat->id);
+ i++;
+ goto err_register;
+ }
+ }
+ ret = da9052_leds_prepare(led);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to init led driver\n");
+ goto err_free;
+ }
+
+ platform_set_drvdata(pdev, led);
+ return 0;
+
+err_register:
+ for (i = i - 1; i >= 0; i--) {
+ led_classdev_unregister(&led[i].cdev);
+ cancel_work_sync(&led[i].work);
+ }
+
+err_free:
+ kfree(led);
+ return ret;
+}
+
+static int __devexit da9052_led_remove(struct platform_device *pdev)
+{
+ struct da9052_leds_platform_data *pdata =
+ (struct da9052_leds_platform_data *)pdev->dev.platform_data;
+ struct da9052_led_data *led = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ da9052_led_setup(&led[i]);
+ led_classdev_unregister(&led[i].cdev);
+ cancel_work_sync(&led[i].work);
+ }
+
+ kfree(led);
+ return 0;
+}
+
+static struct platform_driver da9052_led_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_led_probe,
+ .remove = __devexit_p(da9052_led_remove),
+};
+
+static int __init da9052_led_init(void)
+{
+ return platform_driver_register(&da9052_led_driver);
+}
+module_init(da9052_led_init);
+
+static void __exit da9052_led_exit(void)
+{
+ platform_driver_unregister(&da9052_led_driver);
+}
+module_exit(da9052_led_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com> ");
+MODULE_DESCRIPTION("LED driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
new file mode 100644
index 000000000000..b19eeafbc857
--- /dev/null
+++ b/drivers/mfd/da9052-core.c
@@ -0,0 +1,533 @@
+/*
+ * da9052-core.c -- Device access for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/mfd/core.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define SUCCESS 0
+#define FAILURE 1
+
+struct da9052_eh_nb eve_nb_array[EVE_CNT];
+static struct da9052_ssc_ops ssc_ops;
+struct mutex manconv_lock;
+static struct semaphore eve_nb_array_lock;
+
+void da9052_lock(struct da9052 *da9052)
+{
+ mutex_lock(&da9052->ssc_lock);
+}
+EXPORT_SYMBOL(da9052_lock);
+
+void da9052_unlock(struct da9052 *da9052)
+{
+ mutex_unlock(&da9052->ssc_lock);
+}
+EXPORT_SYMBOL(da9052_unlock);
+
+int da9052_ssc_write(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg)
+{
+ int ret = 0;
+
+ /* Reg address should be a valid address on PAGE0 or PAGE1 */
+ if ((sscmsg->addr < DA9052_PAGE0_REG_START) ||
+ (sscmsg->addr > DA9052_PAGE1_REG_END) ||
+ ((sscmsg->addr > DA9052_PAGE0_REG_END) &&
+ (sscmsg->addr < DA9052_PAGE1_REG_START)))
+ return INVALID_REGISTER;
+
+ ret = ssc_ops.write(da9052, sscmsg);
+
+ /* Update local cache if required */
+ if (!ret) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg->addr].type != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg->addr].val = sscmsg->data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg->addr].status = VALID;
+ }
+ }
+
+ return ret;
+}
+
+int da9052_ssc_read(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg)
+{
+ int ret = 0;
+
+ /* Reg addr should be a valid address on PAGE0 or PAGE1 */
+ if ((sscmsg->addr < DA9052_PAGE0_REG_START) ||
+ (sscmsg->addr > DA9052_PAGE1_REG_END) ||
+ ((sscmsg->addr > DA9052_PAGE0_REG_END) &&
+ (sscmsg->addr < DA9052_PAGE1_REG_START)))
+ return INVALID_REGISTER;
+
+ /*
+ * Check if this is a Non-volatile register, if yes then return value -
+ * from cache instead of actual reading from hardware. Before reading -
+ * cache entry, make sure that the entry is valid
+ */
+ /* The read request is for Non-volatile register */
+ /* Check if we have valid cached value for this */
+ if (da9052->ssc_cache[sscmsg->addr].status == VALID) {
+ /* We have valid cached value, copy this value */
+ sscmsg->data = da9052->ssc_cache[sscmsg->addr].val;
+
+ return 0;
+ }
+
+ ret = ssc_ops.read(da9052, sscmsg);
+
+ /* Update local cache if required */
+ if (!ret) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg->addr].type != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg->addr].val = sscmsg->data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg->addr].status = VALID;
+ }
+ }
+
+ return ret;
+}
+
+int da9052_ssc_write_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int ret = 0;
+ int cnt = 0;
+
+ /* Check request size */
+ if (msg_no > MAX_READ_WRITE_CNT)
+ return -EIO;
+
+ ret = ssc_ops.write_many(da9052, sscmsg, msg_no);
+ /* Update local cache, if required */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg[cnt].addr].type != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg[cnt].addr].val =
+ sscmsg[cnt].data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg[cnt].addr].status = VALID;
+ }
+ }
+ return ret;
+}
+
+int da9052_ssc_read_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int ret = 0;
+ int cnt = 0;
+
+ /* Check request size */
+ if (msg_no > MAX_READ_WRITE_CNT)
+ return -EIO;
+
+ ret = ssc_ops.read_many(da9052, sscmsg, msg_no);
+ /* Update local cache, if required */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg[cnt].addr].type
+ != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg[cnt].addr].val =
+ sscmsg[cnt].data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg[cnt].addr].status = VALID;
+ }
+ }
+ return ret;
+}
+
+static irqreturn_t da9052_eh_isr(int irq, void *dev_id)
+{
+ struct da9052 *da9052 = dev_id;
+ /* Schedule work to be done */
+ schedule_work(&da9052->eh_isr_work);
+ /* Disable IRQ */
+ disable_irq_nosync(DA9052_IRQ);
+ return IRQ_HANDLED;
+}
+
+int eh_register_nb(struct da9052 *da9052, struct da9052_eh_nb *nb)
+{
+
+ if (nb == NULL) {
+ printk(KERN_INFO "EH REGISTER FUNCTION FAILED\n");
+ return -EINVAL;
+ }
+
+ if (nb->eve_type >= EVE_CNT) {
+ printk(KERN_INFO "Invalid DA9052 Event Type\n");
+ return -EINVAL;
+ }
+
+ /* Initialize list head inside notifier block */
+ INIT_LIST_HEAD(&nb->nb_list);
+
+ /* Acquire NB array lock */
+ if (down_interruptible(&eve_nb_array_lock))
+ return -EAGAIN;
+
+ /* Add passed NB to corresponding EVENT list */
+ list_add_tail(&nb->nb_list, &(eve_nb_array[nb->eve_type].nb_list));
+
+ /* Release NB array lock */
+ up(&eve_nb_array_lock);
+
+ return 0;
+}
+
+int eh_unregister_nb(struct da9052 *da9052, struct da9052_eh_nb *nb)
+{
+
+ if (nb == NULL)
+ return -EINVAL;
+
+ /* Acquire nb array lock */
+ if (down_interruptible(&eve_nb_array_lock))
+ return -EAGAIN;
+
+ /* Remove passed NB from list */
+ list_del_init(&(nb->nb_list));
+
+ /* Release NB array lock */
+ up(&eve_nb_array_lock);
+
+ return 0;
+}
+
+static int process_events(struct da9052 *da9052, int events_sts)
+{
+
+ int cnt = 0;
+ int tmp_events_sts = 0;
+ unsigned char event = 0;
+
+ struct list_head *ptr;
+ struct da9052_eh_nb *nb_ptr;
+
+ /* Now we have retrieved all events, process them one by one */
+ for (cnt = 0; cnt < EVE_CNT; cnt++) {
+ /*
+ * Starting with highest priority event,
+ * traverse through all event
+ */
+ tmp_events_sts = events_sts;
+
+ /* Find the event associated with higher priority */
+ event = cnt;
+
+ /* Check if interrupt is received for this event */
+ if (!((tmp_events_sts >> cnt) & 0x1))
+ /* Event bit is not set for this event */
+ /* Move to next event */
+ continue;
+
+ if (event == PEN_DOWN_EVE) {
+ if (list_empty(&(eve_nb_array[event].nb_list)))
+ continue;
+ }
+
+ /* Event bit is set, execute all registered call backs */
+ if (down_interruptible(&eve_nb_array_lock)){
+ printk(KERN_CRIT "Can't acquire eve_nb_array_lock \n");
+ return -EIO;
+ }
+
+ list_for_each(ptr, &(eve_nb_array[event].nb_list)) {
+ /*
+ * nb_ptr will point to the structure in which
+ * nb_list is embedded
+ */
+ nb_ptr = list_entry(ptr, struct da9052_eh_nb, nb_list);
+ nb_ptr->call_back(nb_ptr, events_sts);
+ }
+ up(&eve_nb_array_lock);
+ }
+ return 0;
+}
+
+void eh_workqueue_isr(struct work_struct *work)
+{
+ struct da9052 *da9052 =
+ container_of(work, struct da9052, eh_isr_work);
+
+ struct da9052_ssc_msg eve_data[4];
+ struct da9052_ssc_msg eve_mask_data[4];
+ int events_sts, ret;
+ u32 mask;
+ unsigned char cnt = 0;
+
+ /* nIRQ is asserted, read event registeres to know what happened */
+ events_sts = 0;
+ mask = 0;
+
+ /* Prepare ssc message to read all four event registers */
+ for (cnt = 0; cnt < DA9052_EVE_REGISTERS; cnt++) {
+ eve_data[cnt].addr = (DA9052_EVENTA_REG + cnt);
+ eve_data[cnt].data = 0;
+ }
+
+ /* Prepare ssc message to read all four event registers */
+ for (cnt = 0; cnt < DA9052_EVE_REGISTERS; cnt++) {
+ eve_mask_data[cnt].addr = (DA9052_IRQMASKA_REG + cnt);
+ eve_mask_data[cnt].data = 0;
+ }
+
+ /* Now read all event and mask registers */
+ da9052_lock(da9052);
+
+ ret = da9052_ssc_read_many(da9052,eve_data, DA9052_EVE_REGISTERS);
+ if (ret) {
+ enable_irq(DA9052_IRQ);
+ da9052_unlock(da9052);
+ return;
+ }
+
+ ret = da9052_ssc_read_many(da9052,eve_mask_data, DA9052_EVE_REGISTERS);
+ if (ret) {
+ enable_irq(DA9052_IRQ);
+ da9052_unlock(da9052);
+ return;
+ }
+ /* Collect all events */
+ for (cnt = 0; cnt < DA9052_EVE_REGISTERS; cnt++)
+ events_sts |= (eve_data[cnt].data << (DA9052_EVE_REGISTER_SIZE
+ * cnt));
+ /* Collect all mask */
+ for (cnt = 0; cnt < DA9052_EVE_REGISTERS; cnt++)
+ mask |= (eve_mask_data[cnt].data << (DA9052_EVE_REGISTER_SIZE
+ * cnt));
+ events_sts &= ~mask;
+ da9052_unlock(da9052);
+
+ /* Check if we really got any event */
+ if (events_sts == 0) {
+ enable_irq(DA9052_IRQ);
+ da9052_unlock(da9052);
+ return;
+ }
+
+ /* Process all events occurred */
+ process_events(da9052, events_sts);
+
+ da9052_lock(da9052);
+ /* Now clear EVENT registers */
+ for (cnt = 0; cnt < 4; cnt++) {
+ if (eve_data[cnt].data) {
+ ret = da9052_ssc_write(da9052, &eve_data[cnt]);
+ if (ret) {
+ enable_irq(DA9052_IRQ);
+ da9052_unlock(da9052);
+ return;
+ }
+ }
+ }
+ da9052_unlock(da9052);
+
+ /*
+ * This delay is necessary to avoid hardware fake interrupts
+ * from DA9052.
+ */
+#if defined CONFIG_PMIC_DA9052 || defined CONFIG_PMIC_DA9053AA
+ udelay(50);
+#endif
+ /* Enable HOST interrupt */
+ enable_irq(DA9052_IRQ);
+}
+
+static void da9052_eh_restore_irq(void)
+{
+ /* Put your platform and board specific code here */
+ free_irq(DA9052_IRQ, NULL);
+}
+
+static int da9052_add_subdevice_pdata(struct da9052 *da9052,
+ const char *name, void *pdata, size_t pdata_size)
+{
+ struct mfd_cell cell = {
+ .name = name,
+ .platform_data = pdata,
+ .data_size = pdata_size,
+ };
+ return mfd_add_devices(da9052->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int da9052_add_subdevice(struct da9052 *da9052, const char *name)
+{
+ return da9052_add_subdevice_pdata(da9052, name, NULL, 0);
+}
+
+static int add_da9052_devices(struct da9052 *da9052)
+{
+ s32 ret = 0;
+ struct da9052_platform_data *pdata = da9052->dev->platform_data;
+ struct da9052_leds_platform_data leds_data = {
+ .num_leds = pdata->led_data->num_leds,
+ .led = pdata->led_data->led,
+ };
+ struct da9052_regulator_platform_data regulator_pdata = {
+ .regulators = pdata->regulators,
+ };
+
+ struct da9052_tsi_platform_data tsi_data = *(pdata->tsi_data);
+
+ if (pdata && pdata->init) {
+ ret = pdata->init(da9052);
+ if (ret != 0)
+ return ret;
+ } else
+ printk(KERN_CRIT "No platform initialisation supplied \n");
+
+ ret = da9052_add_subdevice(da9052, "da9052-rtc");
+ if (ret)
+ return ret;
+ ret = da9052_add_subdevice(da9052, "da9052-onkey");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "WLED-1");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "WLED-2");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "WLED-3");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "da9052-adc");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "da9052-wdt");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-gpio",
+ pdata, sizeof(*pdata));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-leds",
+ &leds_data, sizeof(leds_data));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-regulator",
+ &regulator_pdata, sizeof(regulator_pdata));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-tsi",
+ &tsi_data, sizeof(tsi_data));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "da9052-bat");
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+int da9052_ssc_init(struct da9052 *da9052)
+{
+ int cnt;
+ struct da9052_platform_data *pdata;
+
+ /* Initialize eve_nb_array */
+ for (cnt = 0; cnt < EVE_CNT; cnt++)
+ INIT_LIST_HEAD(&(eve_nb_array[cnt].nb_list));
+
+ /* Initialize mutex required for ADC Manual read */
+ mutex_init(&manconv_lock);
+
+ /* Initialize NB array lock */
+ init_MUTEX(&eve_nb_array_lock);
+
+ /* Assign the read-write function pointers */
+ da9052->read = da9052_ssc_read;
+ da9052->write = da9052_ssc_write;
+ da9052->read_many = da9052_ssc_read_many;
+ da9052->write_many = da9052_ssc_write_many;
+
+ if (SPI == da9052->connecting_device && ssc_ops.write == NULL) {
+ /* Assign the read/write pointers to SPI/read/write */
+ ssc_ops.write = da9052_spi_write;
+ ssc_ops.read = da9052_spi_read;
+ ssc_ops.write_many = da9052_spi_write_many;
+ ssc_ops.read_many = da9052_spi_read_many;
+ }
+ else if (I2C == da9052->connecting_device && ssc_ops.write == NULL) {
+ /* Assign the read/write pointers to SPI/read/write */
+ ssc_ops.write = da9052_i2c_write;
+ ssc_ops.read = da9052_i2c_read;
+ ssc_ops.write_many = da9052_i2c_write_many;
+ ssc_ops.read_many = da9052_i2c_read_many;
+ } else
+ return -1;
+ /* Assign the EH notifier block register/de-register functions */
+ da9052->register_event_notifier = eh_register_nb;
+ da9052->unregister_event_notifier = eh_unregister_nb;
+
+ /* Initialize ssc lock */
+ mutex_init(&da9052->ssc_lock);
+
+ pdata = da9052->dev->platform_data;
+ add_da9052_devices(da9052);
+
+ INIT_WORK(&da9052->eh_isr_work, eh_workqueue_isr);
+
+ if (request_irq(DA9052_IRQ, da9052_eh_isr, IRQ_TYPE_LEVEL_LOW,
+ DA9052_EH_DEVICE_NAME, da9052))
+ return -EIO;
+
+ return 0;
+}
+
+void da9052_ssc_exit(struct da9052 *da9052)
+{
+ printk(KERN_INFO "DA9052: Unregistering SSC device.\n");
+ mutex_destroy(&manconv_lock);
+ /* Restore IRQ line */
+ da9052_eh_restore_irq();
+ free_irq(DA9052_IRQ, NULL);
+ mutex_destroy(&da9052->ssc_lock);
+ mutex_destroy(&da9052->eve_nb_lock);
+ return;
+}
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 MFD Core");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9052_SSC_DEVICE_NAME);
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
new file mode 100644
index 000000000000..5828b6db4585
--- /dev/null
+++ b/drivers/mfd/da9052-i2c.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * da9052-i2c.c: I2C SSC (Synchronous Serial Communication) driver for DA9052
+ */
+
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/i2c.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+static struct da9052 *da9052_i2c;
+
+#define I2C_CONNECTED 0
+
+static int da9052_i2c_is_connected(void)
+{
+
+ struct da9052_ssc_msg msg;
+
+ //printk("Entered da9052_i2c_is_connected.............\n");
+
+ msg.addr = DA9052_INTERFACE_REG;
+
+ /* Test spi connectivity by performing read of the GPIO_0-1 register */
+ if ( 0 != da9052_i2c_read(da9052_i2c, &msg)) {
+ printk("da9052_i2c_is_connected - i2c read failed.............\n");
+ return -1;
+ }
+ else {
+ printk("da9052_i2c_is_connected - i2c read success..............\n");
+ return 0;
+ }
+
+}
+
+static int __devinit da9052_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter;
+ // printk("\n\tEntered da9052_i2c_is_probe.............\n");
+
+ da9052_i2c = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+
+ if (!da9052_i2c)
+ return -ENOMEM;
+
+ /* Get the bus driver handler */
+ adapter = to_i2c_adapter(client->dev.parent);
+
+ /* Check i2c bus driver supports byte data transfer */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_info(&client->dev,\
+ "Error in %s:i2c_check_functionality\n", __func__);
+ return -ENODEV;;
+ }
+
+ /* Store handle to i2c client */
+ da9052_i2c->i2c_client = client;
+
+ da9052_i2c->dev = &client->dev;
+
+ /* Initialize i2c data structure here*/
+ da9052_i2c->adapter = adapter;
+
+ /* host i2c driver looks only first 7 bits for the slave address */
+ da9052_i2c->slave_addr = DA9052_I2C_ADDR >> 1;
+
+ /* Store the i2c client data */
+ i2c_set_clientdata(client, da9052_i2c);
+
+ /* Validate I2C connectivity */
+ if ( I2C_CONNECTED == da9052_i2c_is_connected()) {
+ /* I2C is connected */
+ da9052_i2c->connecting_device = I2C;
+ if( 0!= da9052_ssc_init(da9052_i2c) )
+ return -ENODEV;
+ }
+ else {
+ return -ENODEV;
+ }
+
+ //printk("Exiting da9052_i2c_probe.....\n");
+
+ return 0;
+}
+
+static int da9052_i2c_remove(struct i2c_client *client)
+{
+
+ struct da9052 *da9052 = i2c_get_clientdata(client);
+
+ mfd_remove_devices(da9052->dev);
+ kfree(da9052);
+ return 0;
+}
+
+int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+ struct i2c_msg i2cmsg;
+ unsigned char buf[2] = {0};
+ int ret = 0;
+
+ /* Copy the ssc msg to local character buffer */
+ buf[0] = msg->addr;
+ buf[1] = msg->data;
+
+ /*Construct a i2c msg for a da9052 driver ssc message request */
+ i2cmsg.addr = da9052->slave_addr;
+ i2cmsg.len = 2;
+ i2cmsg.buf = buf;
+
+ /* To write the data on I2C set flag to zero */
+ i2cmsg.flags = 0;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "_%s:master_xfer Failed!!\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int da9052_i2c_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+
+ /*Get the da9052_i2c client details*/
+ unsigned char buf[2] = {0, 0};
+ struct i2c_msg i2cmsg[2];
+ int ret = 0;
+
+ /* Copy SSC Msg to local character buffer */
+ buf[0] = msg->addr;
+
+ /*Construct a i2c msg for a da9052 driver ssc message request */
+ i2cmsg[0].addr = da9052->slave_addr ;
+ i2cmsg[0].len = 1;
+ i2cmsg[0].buf = &buf[0];
+
+ /*To write the data on I2C set flag to zero */
+ i2cmsg[0].flags = 0;
+
+ /* Read the data from da9052*/
+ /*Construct a i2c msg for a da9052 driver ssc message request */
+ i2cmsg[1].addr = da9052->slave_addr ;
+ i2cmsg[1].len = 1;
+ i2cmsg[1].buf = &buf[1];
+
+ /*To read the data on I2C set flag to I2C_M_RD */
+ i2cmsg[1].flags = I2C_M_RD;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, i2cmsg, 2);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "2 - %s:master_xfer Failed!!\n", __func__);
+ return ret;
+ }
+
+ msg->data = *i2cmsg[1].buf;
+
+ return 0;
+}
+
+int da9052_i2c_write_many(struct da9052 *da9052,
+ struct da9052_ssc_msg *sscmsg, int msg_no)
+{
+
+ struct i2c_msg i2cmsg;
+ unsigned char data_buf[MAX_READ_WRITE_CNT+1];
+ struct da9052_ssc_msg ctrlb_msg;
+ struct da9052_ssc_msg *msg_queue = sscmsg;
+ int ret = 0;
+ /* Flag to check if requested registers are contiguous */
+ unsigned char cont_data = 1;
+ unsigned char cnt = 0;
+
+ /* Check if requested registers are contiguous */
+ for (cnt = 1; cnt < msg_no; cnt++) {
+ if ((msg_queue[cnt].addr - msg_queue[cnt-1].addr) != 1) {
+ /* Difference is not 1, i.e. non-contiguous registers */
+ cont_data = 0;
+ break;
+ }
+ }
+
+ if (cont_data == 0) {
+ /* Requested registers are non-contiguous */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ ret = da9052->write(da9052, &msg_queue[cnt]);
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+ }
+ /*
+ * Requested registers are contiguous
+ * or PAGE WRITE sequence of I2C transactions is as below
+ * (slave_addr + reg_addr + data_1 + data_2 + ...)
+ * First read current WRITE MODE via CONTROL_B register of DA9052
+ */
+ ctrlb_msg.addr = DA9052_CONTROLB_REG;
+ ctrlb_msg.data = 0x0;
+ ret = da9052->read(da9052, &ctrlb_msg);
+
+ if (ret != 0)
+ return ret;
+
+ /* Check if PAGE WRITE mode is set */
+ if (ctrlb_msg.data & DA9052_CONTROLB_WRITEMODE) {
+ /* REPEAT WRITE mode is configured */
+ /* Now set DA9052 into PAGE WRITE mode */
+ ctrlb_msg.data &= ~DA9052_CONTROLB_WRITEMODE;
+ ret = da9052->write(da9052, &ctrlb_msg);
+
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Put first register address */
+ data_buf[0] = msg_queue[0].addr;
+
+ for (cnt = 0; cnt < msg_no; cnt++)
+ data_buf[cnt+1] = msg_queue[cnt].data;
+
+ /* Construct a i2c msg for PAGE WRITE */
+ i2cmsg.addr = da9052->slave_addr ;
+ /* First register address + all data*/
+ i2cmsg.len = (msg_no + 1);
+ i2cmsg.buf = data_buf;
+
+ /*To write the data on I2C set flag to zero */
+ i2cmsg.flags = 0;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "1 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int da9052_i2c_read_many(struct da9052 *da9052,
+ struct da9052_ssc_msg *sscmsg, int msg_no)
+{
+
+ struct i2c_msg i2cmsg;
+ unsigned char data_buf[MAX_READ_WRITE_CNT];
+ struct da9052_ssc_msg *msg_queue = sscmsg;
+ int ret = 0;
+ /* Flag to check if requested registers are contiguous */
+ unsigned char cont_data = 1;
+ unsigned char cnt = 0;
+
+ /* Check if requested registers are contiguous */
+ for (cnt = 1; cnt < msg_no; cnt++) {
+ if ((msg_queue[cnt].addr - msg_queue[cnt-1].addr) != 1) {
+ /* Difference is not 1, i.e. non-contiguous registers */
+ cont_data = 0;
+ break;
+ }
+ }
+
+ if (cont_data == 0) {
+ /* Requested registers are non-contiguous */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ ret = da9052->read(da9052, &msg_queue[cnt]);
+ if (ret != 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "Error in %s", __func__);
+ return ret;
+ }
+ }
+ return 0;
+ }
+
+ /*
+ * We want to perform PAGE READ via I2C
+ * For PAGE READ sequence of I2C transactions is as below
+ * (slave_addr + reg_addr) + (slave_addr + data_1 + data_2 + ...)
+ */
+ /* Copy address of first register */
+ data_buf[0] = msg_queue[0].addr;
+
+ /* Construct a i2c msg for first transaction of PAGE READ i.e. write */
+ i2cmsg.addr = da9052->slave_addr ;
+ i2cmsg.len = 1;
+ i2cmsg.buf = data_buf;
+
+ /*To write the data on I2C set flag to zero */
+ i2cmsg.flags = 0;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "1 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ return ret;
+ }
+
+ /* Now Read the data from da9052 */
+ /* Construct a i2c msg for second transaction of PAGE READ i.e. read */
+ i2cmsg.addr = da9052->slave_addr ;
+ i2cmsg.len = msg_no;
+ i2cmsg.buf = data_buf;
+
+ /*To read the data on I2C set flag to I2C_M_RD */
+ i2cmsg.flags = I2C_M_RD;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter,
+ &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "2 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ return ret;
+ }
+
+ /* Gather READ data */
+ for (cnt = 0; cnt < msg_no; cnt++)
+ sscmsg[cnt].data = data_buf[cnt];
+
+ return 0;
+}
+
+static struct i2c_device_id da9052_ssc_id[] = {
+ { DA9052_SSC_I2C_DEVICE_NAME, 0},
+ {}
+};
+
+static struct i2c_driver da9052_i2c_driver = {
+ .driver = {
+ .name = DA9052_SSC_I2C_DEVICE_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_i2c_probe,
+ .remove = da9052_i2c_remove,
+ .id_table = da9052_ssc_id,
+};
+
+static int __init da9052_i2c_init(void)
+{
+ int ret = 0;
+ // printk("\n\nEntered da9052_i2c_init................\n\n");
+ ret = i2c_add_driver(&da9052_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register %s\n", DA9052_SSC_I2C_DEVICE_NAME);
+ return ret;
+ }
+ return 0;
+}
+module_init(da9052_i2c_init);
+
+static void __exit da9052_i2c_exit(void)
+{
+ i2c_del_driver(&da9052_i2c_driver);
+}
+module_exit(da9052_i2c_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("I2C driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9052_SSC_I2C_DEVICE_NAME);
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
new file mode 100644
index 000000000000..9dd90c2d19f9
--- /dev/null
+++ b/drivers/mfd/da9052-spi.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * da9052-spi.c: SPI SSC (Synchronous Serial Communication) driver for DA9052
+ */
+
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+
+struct da9052 *da9052_spi;
+
+#define SPI_CONNECTED 0
+
+static int da9052_spi_is_connected(void)
+{
+
+ struct da9052_ssc_msg msg;
+
+ //printk("Entered da9052_spi_is_connected.............\n");
+
+ msg.addr = DA9052_INTERFACE_REG;
+
+ /* Test spi connectivity by performing read of the GPIO_0-1 register and then verify the read value*/
+ if ( 0 != da9052_spi_read(da9052_spi, &msg)) {
+ printk("da9052_spi_is_connected - spi read failed.............\n");
+ return -1;
+ }
+ else if( 0x88 != msg.data ){
+ printk("da9052_spi_is_connected - spi read failed. Msg data =%x ..............\n",msg.data);
+ return -1;
+ }
+
+ return 0;
+
+}
+
+static int da9052_spi_probe(struct spi_device *spi)
+{
+ //printk("\n\tEntered da9052_spi_probe.....\n");
+
+ da9052_spi = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+
+ if (!da9052_spi)
+ return -ENOMEM;
+
+
+ spi->mode = SPI_MODE_0 | SPI_CPOL;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ da9052_spi->dev = &spi->dev;
+
+ da9052_spi->spi_dev = spi;
+
+ /*
+ * Allocate memory for RX/TX bufferes used in single register read/write
+ */
+ da9052_spi->spi_rx_buf = kmalloc(2, GFP_KERNEL | GFP_DMA);
+ if (!da9052_spi->spi_rx_buf)
+ return -ENOMEM;
+
+ da9052_spi->spi_tx_buf = kmalloc(2, GFP_KERNEL | GFP_DMA);
+ if (!da9052_spi->spi_tx_buf)
+ return -ENOMEM;
+
+ da9052_spi->spi_active_page = PAGECON_0;
+ da9052_spi->rw_pol = 1;
+
+
+ dev_set_drvdata(&spi->dev, da9052_spi);
+
+
+ /* Validate SPI connectivity */
+ if ( SPI_CONNECTED == da9052_spi_is_connected()) {
+ /* SPI is connected */
+ da9052_spi->connecting_device = SPI;
+ if( 0 != da9052_ssc_init(da9052_spi) )
+ return -ENODEV;
+ }
+ else {
+ return -ENODEV;
+ }
+
+ //printk("Exiting da9052_spi_probe.....\n");
+
+ return 0;
+}
+
+static int da9052_spi_remove(struct spi_device *spi)
+{
+ struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
+
+ printk("Entered da9052_spi_remove()\n");
+ if(SPI == da9052->connecting_device ) {
+ da9052_ssc_exit(da9052);
+ }
+ mfd_remove_devices(&spi->dev);
+ kfree(da9052->spi_rx_buf);
+ kfree(da9052->spi_tx_buf);
+ kfree(da9052);
+ return 0;
+}
+
+static struct spi_driver da9052_spi_driver = {
+ .driver = {
+ .name = DA9052_SSC_SPI_DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_spi_probe,
+ .remove = __devexit_p(da9052_spi_remove),
+};
+
+
+static int da9052_spi_set_page(struct da9052 *da9052, unsigned char page)
+{
+
+ struct da9052_ssc_msg sscmsg;
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int ret = 0;
+
+ printk("Entered da9052_spi_set_page.....\n");
+ if ((page != PAGECON_0) && ((page != PAGECON_128)))
+ return INVALID_PAGE;
+
+ /* Current configuration is PAGE-0 and write request for PAGE-1 */
+ /* set register address */
+ sscmsg.addr = DA9052_PAGECON0_REG;
+ /* set value */
+ sscmsg.data = page;
+
+ /* Check value of R/W_POL bit of INTERFACE register */
+ if (!da9052->rw_pol) {
+ /* We need to set 0th bit for write operation */
+ sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
+ } else {
+ /* We need to reset 0th bit for write operation */
+ sscmsg.addr = (sscmsg.addr << 1);
+ }
+
+ /* SMDK-6410 host SPI driver specific stuff */
+
+ /* Build our spi message */
+ printk("da9052_spi_set_page - Calling spi_message_init.....\n");
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = 2;
+ xfer.tx_buf = da9052->spi_tx_buf;
+ xfer.rx_buf = da9052->spi_rx_buf;
+
+ da9052->spi_tx_buf[0] = sscmsg.addr;
+ da9052->spi_tx_buf[1] = sscmsg.data;
+
+ printk("da9052_spi_set_page - Calling spi_message_add_tail.....\n");
+ spi_message_add_tail(&xfer, &message);
+
+ /* Now, do the i/o */
+ printk("da9052_spi_set_page - Calling spi_sync.....\n");
+ ret = spi_sync(da9052->spi_dev, &message);
+
+ if (ret == 0) {
+ /* Active Page set successfully */
+ da9052->spi_active_page = page;
+ return 0;
+ } else {
+ /* Error in setting Active Page */
+ return ret;
+ }
+
+ return 0;
+}
+
+int da9052_spi_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int ret;
+
+ /*
+ * We need a seperate copy of da9052_ssc_msg so that caller's
+ * copy remains intact
+ */
+ struct da9052_ssc_msg sscmsg;
+
+ /* Copy callers data in to our local copy */
+ sscmsg.addr = msg->addr;
+ sscmsg.data = msg->data;
+
+ if ((sscmsg.addr > PAGE_0_END) &&
+ (da9052->spi_active_page == PAGECON_0)) {
+ /*
+ * Current configuration is PAGE-0 and write request
+ * for PAGE-1
+ */
+ da9052_spi_set_page(da9052, PAGECON_128);
+ /* Set register address accordindly */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ } else if ((sscmsg.addr < PAGE_1_START) &&
+ (da9052->spi_active_page == PAGECON_128)) {
+ /*
+ * Current configuration is PAGE-1 and write request
+ * for PAGE-0
+ */
+ da9052_spi_set_page(da9052, PAGECON_0);
+ } else if (sscmsg.addr > PAGE_0_END) {
+ /*
+ * Current configuration is PAGE-1 and write request
+ * for PAGE-1. Just need to adjust register address
+ */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ }
+
+ /* Check value of R/W_POL bit of INTERFACE register */
+ if (!da9052->rw_pol) {
+ /* We need to set 0th bit for write operation */
+ sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
+ } else {
+ /* We need to reset 0th bit for write operation */
+ sscmsg.addr = (sscmsg.addr << 1);
+ }
+
+ /* SMDK-6410 host SPI driver specific stuff */
+
+ /* Build our spi message */
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = 2;
+ xfer.tx_buf = da9052->spi_tx_buf;
+ xfer.rx_buf = da9052->spi_rx_buf;
+
+ da9052->spi_tx_buf[0] = sscmsg.addr;
+ da9052->spi_tx_buf[1] = sscmsg.data;
+
+ spi_message_add_tail(&xfer, &message);
+
+ /* Now, do the i/o */
+ ret = spi_sync(da9052->spi_dev, &message);
+
+ return ret;
+}
+
+int da9052_spi_write_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int cnt,ret=0;
+
+ for(cnt = 0; cnt < msg_no; cnt++, sscmsg++) {
+ ret = da9052_ssc_write(da9052,sscmsg);
+ if(ret != 0)
+ {
+ printk("Error in %s", __FUNCTION__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int da9052_spi_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int ret;
+
+ /*
+ * We need a seperate copy of da9052_ssc_msg so that
+ * caller's copy remains intact
+ */
+ struct da9052_ssc_msg sscmsg;
+
+
+ /* Copy callers data in to our local copy */
+ sscmsg.addr = msg->addr;
+ sscmsg.data = msg->data;
+
+ if ((sscmsg.addr > PAGE_0_END) &&
+ (da9052->spi_active_page == PAGECON_0)) {
+ /*
+ * Current configuration is PAGE-0 and
+ * read request for PAGE-1
+ */
+ printk("da9052_spi_read - if PAGECON_128.....\n");
+ da9052_spi_set_page(da9052, PAGECON_128);
+ /* Set register address accordindly */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ } else if ((sscmsg.addr < PAGE_1_START) &&
+ (da9052->spi_active_page == PAGECON_128)) {
+ /*
+ * Current configuration is PAGE-1 and
+ * write request for PAGE-0
+ */
+ printk("da9052_spi_read - if PAGECON_0.....\n");
+ da9052_spi_set_page(da9052, PAGECON_0);
+ } else if (sscmsg.addr > PAGE_0_END) {
+ /*
+ * Current configuration is PAGE-1 and write
+ * request for PAGE-1
+ * Just need to adjust register address
+ */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ }
+
+ /* Check value of R/W_POL bit of INTERFACE register */
+ if (da9052->rw_pol) {
+ /* We need to set 0th bit for read operation */
+ sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
+ } else {
+ /* We need to reset 0th bit for write operation */
+ sscmsg.addr = (sscmsg.addr << 1);
+ }
+
+ /* SMDK-6410 host SPI driver specific stuff */
+
+ /* Build our spi message */
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = 2;
+ xfer.tx_buf = da9052->spi_tx_buf;
+ xfer.rx_buf = da9052->spi_rx_buf;
+
+ da9052->spi_tx_buf[0] = sscmsg.addr;
+ da9052->spi_tx_buf[1] = 0xff;
+
+ da9052->spi_rx_buf[0] = 0;
+ da9052->spi_rx_buf[1] = 0;
+
+ spi_message_add_tail(&xfer, &message);
+
+ /* Now, do the i/o */
+ ret = spi_sync(da9052->spi_dev, &message);
+
+ if (ret == 0) {
+ /* Update read value in callers copy */
+ msg->data = da9052->spi_rx_buf[1];
+ return 0;
+ } else {
+ return ret;
+ }
+
+
+ return 0;
+}
+
+int da9052_spi_read_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int cnt,ret=0;
+
+ for(cnt = 0; cnt < msg_no; cnt++, sscmsg++) {
+ ret = da9052_ssc_read(da9052,sscmsg);
+ if(ret != 0)
+ {
+ printk("Error in %s", __FUNCTION__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int __init da9052_spi_init(void)
+{
+ int ret = 0;
+ //printk("Entered da9052_spi_init.....\n");
+ ret = spi_register_driver(&da9052_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register %s\n", DA9052_SSC_SPI_DEVICE_NAME);
+ return ret;
+ }
+ return 0;
+}
+module_init(da9052_spi_init);
+
+static void __exit da9052_spi_exit(void)
+{
+ spi_unregister_driver(&da9052_spi_driver);
+}
+
+module_exit(da9052_spi_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("SPI driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9052_SSC_SPI_DEVICE_NAME);
diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c
new file mode 100644
index 000000000000..85561b463df5
--- /dev/null
+++ b/drivers/power/da9052-battery.c
@@ -0,0 +1,844 @@
+/*
+ * da9052-battery.c -- Batttery Driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/freezer.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/bat.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define DA9052_BAT_DEVICE_NAME "da9052-bat"
+
+static const char __initdata banner[] = KERN_INFO "DA9052 BAT, (c) \
+2009 Dialog semiconductor Ltd.\n";
+
+static struct da9052_bat_hysteresis bat_hysteresis;
+static struct da9052_bat_event_registration event_status;
+
+
+static u16 array_hys_batvoltage[2];
+static u16 bat_volt_arr[3];
+static u8 hys_flag = FALSE;
+
+static int da9052_read(struct da9052 *da9052, u8 reg_address, u8 *reg_data)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ msg.addr = reg_address;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret)
+ goto ssc_comm_err;
+ da9052_unlock(da9052);
+
+ *reg_data = msg.data;
+ return 0;
+ssc_comm_err:
+ da9052_unlock(da9052);
+ return ret;
+}
+
+static s32 da9052_adc_read_ich(struct da9052 *da9052, u16 *data)
+{
+ struct da9052_ssc_msg msg;
+ da9052_lock(da9052);
+ /* Read charging conversion register */
+ msg.addr = DA9052_ICHGAV_REG;
+ msg.data = 0;
+ if (da9052->read(da9052, &msg)) {
+ da9052_unlock(da9052);
+ return DA9052_SSC_FAIL;
+ }
+ da9052_unlock(da9052);
+
+ *data = (u16)msg.data;
+ DA9052_DEBUG(
+ "In function: %s, ICHGAV_REG value read (1)= 0x%X \n",
+ __func__, msg.data);
+ return SUCCESS;
+}
+
+
+static s32 da9052_adc_read_tbat(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+ u8 reg_data;
+
+ ret = da9052_read(da9052, DA9052_TBATRES_REG, &reg_data);
+ if (ret)
+ return ret;
+ *data = (u16)reg_data;
+
+ DA9052_DEBUG("In function: %s, TBATRES_REG value read (1)= 0x%X \n",
+ __func__, msg.data);
+ return SUCCESS;
+}
+
+s32 da9052_adc_read_vbat(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+
+ ret = da9052_manual_read(da9052, DA9052_ADC_VBAT);
+ DA9052_DEBUG("In function: %s, VBAT value read (1)= 0x%X \n",
+ __func__, temp);
+ if (ret == -EIO) {
+ *data = 0;
+ return ret;
+ } else {
+ *data = ret;
+ return 0;
+ }
+ return 0;
+}
+
+
+static u16 filter_sample(u16 *buffer)
+{
+ u8 count;
+ u16 tempvalue = 0;
+ u16 ret;
+
+ if (buffer == NULL)
+ return -EINVAL;
+
+ for (count = 0; count < DA9052_FILTER_SIZE; count++)
+ tempvalue = tempvalue + *(buffer + count);
+
+ ret = tempvalue/DA9052_FILTER_SIZE;
+ return ret;
+}
+
+static s32 da9052_bat_get_battery_temperature(struct da9052_charger_device
+ *chg_device, u16 *buffer)
+{
+
+ u8 count;
+ u16 filterqueue[DA9052_FILTER_SIZE];
+
+ /* Measure the battery temperature using ADC function.
+ Number of read equal to average filter size*/
+
+ for (count = 0; count < DA9052_FILTER_SIZE; count++)
+ if (da9052_adc_read_tbat(chg_device->da9052, &filterqueue[count]))
+ return -EIO;
+
+ /* Apply Average filter */
+ filterqueue[0] = filter_sample(filterqueue);
+
+ chg_device->bat_temp = filterqueue[0];
+ *buffer = chg_device->bat_temp;
+
+ return SUCCESS;
+}
+
+static s32 da9052_bat_get_chg_current(struct da9052_charger_device
+ *chg_device, u16 *buffer)
+{
+ if (chg_device->status == POWER_SUPPLY_STATUS_DISCHARGING)
+ return -EIO;
+
+ /* Measure the Charger current using ADC function */
+ if (da9052_adc_read_ich(chg_device->da9052, buffer))
+ return -EIO;
+
+ /* Convert the raw value in terms of mA */
+ chg_device->chg_current = ichg_reg_to_mA(*buffer);
+ *buffer = chg_device->chg_current;
+
+ return 0;
+}
+
+
+s32 da9052_bat_get_battery_voltage(struct da9052_charger_device *chg_device,
+ u16 *buffer)
+{
+ u8 count;
+ u16 filterqueue[DA9052_FILTER_SIZE];
+
+ /* Measure the battery voltage using ADC function.
+ Number of read equal to average filter size*/
+ for (count = 0; count < DA9052_FILTER_SIZE; count++)
+ if (da9052_adc_read_vbat(chg_device->da9052, &filterqueue[count]))
+ return -EIO;
+
+ /* Apply average filter */
+ filterqueue[0] = filter_sample(filterqueue);
+
+ /* Convert battery voltage raw value in terms of mV */
+ chg_device->bat_voltage = volt_reg_to_mV(filterqueue[0]);
+ *buffer = chg_device->bat_voltage;
+ return 0;
+}
+
+static void da9052_bat_status_update(struct da9052_charger_device
+ *chg_device)
+{
+ struct da9052_ssc_msg msg;
+ u16 current_value = 0;
+ u16 buffer =0;
+ u8 regvalue = 0;
+ u8 old_status = chg_device->status;
+
+ DA9052_DEBUG("FUNCTION = %s \n", __func__);
+
+ /* Read Status A register */
+ msg.addr = DA9052_STATUSA_REG;
+ msg.data = 0;
+ da9052_lock(chg_device->da9052);
+
+ if (chg_device->da9052->read(chg_device->da9052, &msg)) {
+ DA9052_DEBUG("%s : failed\n", __func__);
+ da9052_unlock(chg_device->da9052);
+ return;
+ }
+ regvalue = msg.data;
+
+ /* Read Status B register */
+ msg.addr = DA9052_STATUSB_REG;
+ msg.data = 0;
+ if (chg_device->da9052->read(chg_device->da9052, &msg)) {
+ DA9052_DEBUG("%s : failed\n", __func__);
+ da9052_unlock(chg_device->da9052);
+ return;
+ }
+ da9052_unlock(chg_device->da9052);
+
+ /* If DCINDET and DCINSEL are set then connected charger is
+ WALL Charger unit */
+ if( (regvalue & DA9052_STATUSA_DCINSEL)
+ && (regvalue & DA9052_STATUSA_DCINDET) ) {
+
+ chg_device->charger_type = DA9052_WALL_CHARGER;
+ }
+ /* If VBUS_DET and VBUSEL are set then connected charger is
+ USB Type */
+ else if((regvalue & DA9052_STATUSA_VBUSSEL)
+ && (regvalue & DA9052_STATUSA_VBUSDET)) {
+ if (regvalue & DA9052_STATUSA_VDATDET) {
+ chg_device->charger_type = DA9052_USB_CHARGER;
+ }
+ else {
+ /* Else it has to be USB Host charger */
+ chg_device->charger_type = DA9052_USB_HUB;
+ }
+ }
+ /* Battery is discharging since charging device is not present */
+ else
+ {
+ chg_device->charger_type = DA9052_NOCHARGER;
+ /* Eqv to DISCHARGING_WITHOUT_CHARGER state */
+ chg_device->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+
+
+ if( chg_device->charger_type != DA9052_NOCHARGER ) {
+ /* if Charging end flag is set and Charging current is greater
+ than charging end limit then battery is charging */
+ if ((msg.data & DA9052_STATUSB_CHGEND) != 0) {
+
+ if(da9052_bat_get_chg_current(chg_device,&current_value)) {
+ return;
+ }
+
+ if( current_value >= chg_device->chg_end_current ) {
+ chg_device->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ else {
+ /* Eqv to DISCHARGING_WITH_CHARGER state*/
+ chg_device->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ }
+ /* if Charging end flag is cleared then battery is charging */
+ else {
+ chg_device->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+
+ if( POWER_SUPPLY_STATUS_CHARGING == chg_device->status){
+ if(msg.data != DA9052_STATUSB_CHGPRE) {
+ /* Measure battery voltage. if battery voltage is greater than
+ (VCHG_BAT - VCHG_DROP) then battery is in the termintation mode.
+ */
+ if(da9052_bat_get_battery_voltage(chg_device,&buffer)) {
+ DA9052_DEBUG("%s : failed\n",__FUNCTION__);
+ return ;
+ }
+ if(buffer > (chg_device->bat_target_voltage -
+ chg_device->charger_voltage_drop) &&
+ ( chg_device->cal_capacity >= 99 ) ){
+ chg_device->status = POWER_SUPPLY_STATUS_FULL;
+ }
+
+ }
+ }
+ }
+
+ if(chg_device->illegal)
+ chg_device->health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ else if (chg_device->cal_capacity < chg_device->bat_capacity_limit_low)
+ chg_device->health = POWER_SUPPLY_HEALTH_DEAD;
+ else
+ chg_device->health = POWER_SUPPLY_HEALTH_GOOD;
+
+ if ( chg_device->status != old_status)
+ power_supply_changed(&chg_device->psy);
+
+ return;
+}
+
+static s32 da9052_bat_suspend_charging(struct da9052_charger_device *chg_device)
+{
+ struct da9052_ssc_msg msg;
+
+ if ((chg_device->status == POWER_SUPPLY_STATUS_DISCHARGING) ||
+ (chg_device->status == POWER_SUPPLY_STATUS_NOT_CHARGING))
+ return 0;
+
+ msg.addr = DA9052_INPUTCONT_REG;
+ msg.data = 0;
+ da9052_lock(chg_device->da9052);
+ /* Read Input condition register */
+ if (chg_device->da9052->read(chg_device->da9052, &msg)) {
+ da9052_unlock(chg_device->da9052);
+ return DA9052_SSC_FAIL;
+ }
+
+ /* set both Wall charger and USB charger suspend bit */
+ msg.data = set_bits(msg.data, DA9052_INPUTCONT_DCINSUSP);
+ msg.data = set_bits(msg.data, DA9052_INPUTCONT_VBUSSUSP);
+
+ /* Write to Input control register */
+ if (chg_device->da9052->write(chg_device->da9052, &msg)) {
+ da9052_unlock(chg_device->da9052);
+ DA9052_DEBUG("%s : failed\n", __func__);
+ return DA9052_SSC_FAIL;
+ }
+ da9052_unlock(chg_device->da9052);
+
+ DA9052_DEBUG("%s : Sucess\n", __func__);
+ return 0;
+}
+
+u32 interpolated(u32 vbat_lower, u32 vbat_upper, u32 level_lower,
+ u32 level_upper, u32 bat_voltage)
+{
+ s32 temp;
+ /*apply formula y= yk + (x - xk) * (yk+1 -yk)/(xk+1 -xk) */
+ temp = ((level_upper - level_lower) * 1000)/(vbat_upper - vbat_lower);
+ temp = level_lower + (((bat_voltage - vbat_lower) * temp)/1000);
+
+ return temp;
+}
+
+s32 capture_first_correct_vbat_sample(struct da9052_charger_device *chg_device,
+u16 *battery_voltage)
+{
+ static u8 count;
+ s32 ret = 0;
+ u32 temp_data = 0;
+
+ ret = da9052_bat_get_battery_voltage(chg_device,
+ &bat_volt_arr[count]);
+ if (ret)
+ return ret;
+ count++;
+
+ if (count < chg_device->vbat_first_valid_detect_iteration)
+ return FAILURE;
+ for (count = 0; count <
+ (chg_device->vbat_first_valid_detect_iteration - 1);
+ count++) {
+ temp_data = (bat_volt_arr[count] *
+ (chg_device->hysteresis_window_size))/100;
+ bat_hysteresis.upper_limit = bat_volt_arr[count] + temp_data;
+ bat_hysteresis.lower_limit = bat_volt_arr[count] - temp_data;
+
+ if ((bat_volt_arr[count + 1] < bat_hysteresis.upper_limit) &&
+ (bat_volt_arr[count + 1] >
+ bat_hysteresis.lower_limit)) {
+ *battery_voltage = (bat_volt_arr[count] +
+ bat_volt_arr[count+1]) / 2;
+ hys_flag = TRUE;
+ return 0;
+ }
+ }
+
+ for (count = 0; count <
+ (chg_device->vbat_first_valid_detect_iteration - 1);
+ count++)
+ bat_volt_arr[count] = bat_volt_arr[count + 1];
+
+ return FAILURE;
+}
+
+
+s32 check_hystersis(struct da9052_charger_device *chg_device, u16 *bat_voltage)
+{
+ u8 ret = 0;
+ u32 offset = 0;
+
+ /* Measure battery voltage using BAT internal function*/
+ if (hys_flag == FALSE) {
+ ret = capture_first_correct_vbat_sample
+ (chg_device, &array_hys_batvoltage[0]);
+ if (ret)
+ return ret;
+ }
+
+ ret = da9052_bat_get_battery_voltage
+ (chg_device, &array_hys_batvoltage[1]);
+ if (ret)
+ return ret;
+ *bat_voltage = array_hys_batvoltage[1];
+
+#if DA9052_BAT_FILTER_HYS
+ printk(KERN_CRIT "\nBAT_LOG: Previous Battery Voltage = %d mV\n",
+ array_hys_batvoltage[0]);
+ printk(KERN_CRIT "\nBAT_LOG:Battery Voltage Before Filter = %d mV\n",
+ array_hys_batvoltage[1]);
+#endif
+ /* Check if measured battery voltage value is within the hysteresis
+ window limit using measured battey votlage value */
+ if ((bat_hysteresis.upper_limit < *bat_voltage) ||
+ (bat_hysteresis.lower_limit > *bat_voltage)) {
+
+ bat_hysteresis.index++;
+ if (bat_hysteresis.index ==
+ chg_device->hysteresis_no_of_reading) {
+ /* Hysteresis Window is set to +- of
+ HYSTERESIS_WINDOW_SIZE percentage of current VBAT */
+ bat_hysteresis.index = 0;
+ offset = ((*bat_voltage) *
+ chg_device->hysteresis_window_size)/
+ 100;
+ bat_hysteresis.upper_limit = (*bat_voltage) + offset;
+ bat_hysteresis.lower_limit = (*bat_voltage) - offset;
+ } else {
+#if DA9052_BAT_FILTER_HYS
+ printk(KERN_CRIT "CheckHystersis: Failed\n");
+#endif
+ return -EIO;
+ }
+ } else {
+ bat_hysteresis.index = 0;
+ offset = ((*bat_voltage) *
+ chg_device->hysteresis_window_size)/100;
+ bat_hysteresis.upper_limit = (*bat_voltage) + offset;
+ bat_hysteresis.lower_limit = (*bat_voltage) - offset;
+ }
+
+ /* Digital C Filter, formula Yn = k Yn-1 + (1-k) Xn */
+ *bat_voltage = ((chg_device->chg_hysteresis_const *
+ array_hys_batvoltage[0])/100) +
+ (((100 - chg_device->chg_hysteresis_const) *
+ array_hys_batvoltage[1])/100);
+
+ if ((chg_device->status == POWER_SUPPLY_STATUS_DISCHARGING) &&
+ (*bat_voltage > array_hys_batvoltage[0])) {
+ *bat_voltage = array_hys_batvoltage[0];
+ }
+
+ array_hys_batvoltage[0] = *bat_voltage;
+
+#if DA9052_BAT_FILTER_HYS
+ printk(KERN_CRIT "\nBAT_LOG:Battery Voltage After Filter = %d mV\n",\
+ *bat_voltage);
+
+#endif
+ return 0;
+}
+
+u8 select_temperature(u8 temp_index, u16 bat_temperature)
+{
+ u16 temp_temperature = 0;
+ temp_temperature = (temperature_lookup_ref[temp_index] +
+ temperature_lookup_ref[temp_index+1]) / 2;
+
+ if (bat_temperature >= temp_temperature) {
+ temp_index += 1;
+ return temp_index;
+ } else
+ return temp_index;
+}
+
+s32 da9052_bat_level_update(struct da9052_charger_device *chg_device)
+{
+ u16 bat_temperature;
+ u16 bat_voltage;
+ u32 vbat_lower, vbat_upper, level_upper, level_lower, level;
+ u8 access_index = 0;
+ u8 index = 0, ret;
+ u8 flag = FALSE;
+
+ ret = 0;
+ vbat_lower = 0;
+ vbat_upper = 0;
+ level_upper = 0;
+ level_lower = 0;
+
+ ret = check_hystersis(chg_device, &bat_voltage);
+ if (ret)
+ return ret;
+
+ ret = da9052_bat_get_battery_temperature(chg_device,
+ &bat_temperature);
+ if (ret)
+ return ret;
+
+ for (index = 0; index < (DA9052_NO_OF_LOOKUP_TABLE-1); index++) {
+ if (bat_temperature <= temperature_lookup_ref[0]) {
+ access_index = 0;
+ break;
+ } else if (bat_temperature >
+ temperature_lookup_ref[DA9052_NO_OF_LOOKUP_TABLE]){
+ access_index = DA9052_NO_OF_LOOKUP_TABLE - 1;
+ break;
+ } else if ((bat_temperature >= temperature_lookup_ref[index]) &&
+ (bat_temperature >= temperature_lookup_ref[index+1])) {
+ access_index = select_temperature(index,
+ bat_temperature);
+ break;
+ }
+ }
+ if (bat_voltage >= vbat_vs_capacity_look_up[access_index][0][0]) {
+ chg_device->cal_capacity = 100;
+ return 0;
+ }
+ if (bat_voltage <= vbat_vs_capacity_look_up[access_index]
+ [DA9052_LOOK_UP_TABLE_SIZE-1][0]){
+ chg_device->cal_capacity = 0;
+ return 0;
+ }
+ flag = FALSE;
+
+ for (index = 0; index < (DA9052_LOOK_UP_TABLE_SIZE-1); index++) {
+ if ((bat_voltage <=
+ vbat_vs_capacity_look_up[access_index][index][0]) &&
+ (bat_voltage >=
+ vbat_vs_capacity_look_up[access_index][index+1][0])) {
+ vbat_upper =
+ vbat_vs_capacity_look_up[access_index][index][0];
+ vbat_lower =
+ vbat_vs_capacity_look_up[access_index][index+1][0];
+ level_upper =
+ vbat_vs_capacity_look_up[access_index][index][1];
+ level_lower =
+ vbat_vs_capacity_look_up[access_index][index+1][1];
+ flag = TRUE;
+ break;
+ }
+ }
+ if (!flag)
+ return -EIO;
+
+ level = interpolated(vbat_lower, vbat_upper, level_lower,
+ level_upper, bat_voltage);
+ chg_device->cal_capacity = level;
+ DA9052_DEBUG(" TOTAl_BAT_CAPACITY : %d\n", chg_device->cal_capacity);
+ return 0;
+}
+
+void da9052_bat_tbat_handler(struct da9052_eh_nb *eh_data, unsigned int event)
+{
+ struct da9052_charger_device *chg_device =
+ container_of(eh_data, struct da9052_charger_device, tbat_eh_data);
+
+ chg_device->health = POWER_SUPPLY_HEALTH_OVERHEAT;
+
+}
+
+static s32 da9052_bat_register_event(struct da9052_charger_device *chg_device)
+{
+ s32 ret;
+
+ if (event_status.da9052_event_tbat == FALSE) {
+ chg_device->tbat_eh_data.eve_type = TBAT_EVE;
+ chg_device->tbat_eh_data.call_back =da9052_bat_tbat_handler;
+ DA9052_DEBUG("events = %d\n",TBAT_EVE);
+ ret = chg_device->da9052->register_event_notifier
+ (chg_device->da9052, &chg_device->tbat_eh_data);
+ if (ret)
+ return -EIO;
+ event_status.da9052_event_tbat = TRUE;
+ }
+
+ return 0;
+}
+
+static s32 da9052_bat_unregister_event(struct da9052_charger_device *chg_device)
+{
+ s32 ret;
+
+ if (event_status.da9052_event_tbat) {
+ ret =
+ chg_device->da9052->unregister_event_notifier
+ (chg_device->da9052, &chg_device->tbat_eh_data);
+ if (ret)
+ return -EIO;
+ event_status.da9052_event_tbat = FALSE;
+ }
+
+ return 0;
+}
+
+static int da9052_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct da9052_charger_device *chg_device =
+ container_of(psy, struct da9052_charger_device, psy);
+
+ /* Validate battery presence */
+ if( chg_device->illegal && psp != POWER_SUPPLY_PROP_PRESENT ) {
+ return -ENODEV;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = chg_device->status;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = (chg_device->charger_type == DA9052_NOCHARGER) ? 0: 1;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = chg_device->illegal;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = chg_device->health;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = chg_device->bat_target_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = chg_device->bat_volt_cutoff * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ val->intval = chg_device->bat_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = chg_device->chg_current * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = chg_device->cal_capacity;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = bat_temp_reg_to_C(chg_device->bat_temp);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = chg_device->technology;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+
+static u8 detect_illegal_battery(struct da9052_charger_device *chg_device)
+{
+ u16 buffer = 0;
+ s32 ret = 0;
+
+ /* Measure battery temeperature */
+ ret = da9052_bat_get_battery_temperature(chg_device, &buffer);
+ if (ret) {
+ DA9052_DEBUG("%s: Battery temperature measurement failed \n",
+ __func__);
+ return ret;
+ }
+
+ if (buffer > chg_device->bat_with_no_resistor)
+ chg_device->illegal = TRUE;
+ else
+ chg_device->illegal = FALSE;
+
+
+ /* suspend charging of battery if illegal battey is detected */
+ if (chg_device->illegal)
+ da9052_bat_suspend_charging(chg_device);
+
+ return chg_device->illegal;
+}
+
+void da9052_update_bat_properties(struct da9052_charger_device *chg_device)
+{
+ /* Get Bat status and type */
+ da9052_bat_status_update(chg_device);
+ da9052_bat_level_update(chg_device);
+}
+
+static void da9052_bat_external_power_changed(struct power_supply *psy)
+{
+ struct da9052_charger_device *chg_device =
+ container_of(psy, struct da9052_charger_device, psy);
+
+ cancel_delayed_work(&chg_device->monitor_work);
+ queue_delayed_work(chg_device->monitor_wqueue, &chg_device->monitor_work, HZ/10);
+}
+
+
+static void da9052_bat_work(struct work_struct *work)
+{
+ struct da9052_charger_device *chg_device = container_of(work,
+ struct da9052_charger_device,monitor_work.work);
+
+ da9052_update_bat_properties(chg_device);
+ queue_delayed_work(chg_device->monitor_wqueue, &chg_device->monitor_work, HZ * 8);
+}
+
+static enum power_supply_property da9052_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+
+};
+
+static s32 __devinit da9052_bat_probe(struct platform_device *pdev)
+{
+ struct da9052_charger_device *chg_device;
+ u8 reg_data;
+ int ret;
+
+ chg_device = kzalloc(sizeof(*chg_device), GFP_KERNEL);
+ if (!chg_device)
+ return -ENOMEM;
+
+ chg_device->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, chg_device);
+
+ chg_device->psy.name = DA9052_BAT_DEVICE_NAME;
+ chg_device->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ chg_device->psy.properties = da9052_bat_props;
+ chg_device->psy.num_properties = ARRAY_SIZE(da9052_bat_props);
+ chg_device->psy.get_property = da9052_bat_get_property;
+ chg_device->psy.external_power_changed = da9052_bat_external_power_changed;
+ chg_device->psy.use_for_apm = 1;
+ chg_device->charger_type = DA9052_NOCHARGER;
+ chg_device->status = POWER_SUPPLY_STATUS_UNKNOWN;
+ chg_device->health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ chg_device->technology = POWER_SUPPLY_TECHNOLOGY_LION;
+ chg_device->bat_with_no_resistor = 62;
+ chg_device->bat_capacity_limit_low = 4;
+ chg_device->bat_capacity_limit_high = 70;
+ chg_device->bat_capacity_full = 100;
+ chg_device->bat_volt_cutoff = 2800;
+ chg_device->vbat_first_valid_detect_iteration = 3;
+ chg_device->hysteresis_window_size =1;
+ chg_device->chg_hysteresis_const =89;
+ chg_device->hysteresis_reading_interval =1000;
+ chg_device->hysteresis_no_of_reading =10;
+
+ ret = da9052_read(chg_device->da9052, DA9052_CHGCONT_REG, &reg_data);
+ if (ret)
+ goto err_charger_init;
+ chg_device->charger_voltage_drop = bat_drop_reg_to_mV(reg_data &&
+ DA9052_CHGCONT_TCTR);
+ chg_device->bat_target_voltage =
+ bat_reg_to_mV(reg_data && DA9052_CHGCONT_VCHGBAT);
+
+ ret = da9052_read(chg_device->da9052, DA9052_ICHGEND_REG, &reg_data);
+ if (ret)
+ goto err_charger_init;
+ chg_device->chg_end_current = ichg_reg_to_mA(reg_data);
+
+ bat_hysteresis.upper_limit = 0;
+ bat_hysteresis.lower_limit = 0;
+ bat_hysteresis.hys_flag = 0;
+
+ chg_device->illegal = FALSE;
+ detect_illegal_battery(chg_device);
+
+ da9052_bat_register_event(chg_device);
+ if (ret)
+ goto err_charger_init;
+
+ ret = power_supply_register(&pdev->dev, &chg_device->psy);
+ if (ret)
+ goto err_charger_init;
+
+ INIT_DELAYED_WORK(&chg_device->monitor_work, da9052_bat_work);
+ chg_device->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id);
+ if (!chg_device->monitor_wqueue) {
+ goto err_charger_init;
+ }
+ queue_delayed_work(chg_device->monitor_wqueue, &chg_device->monitor_work, HZ * 1);
+
+ return 0;
+
+err_charger_init:
+ platform_set_drvdata(pdev, NULL);
+ kfree(chg_device);
+ return ret;
+}
+static int __devexit da9052_bat_remove(struct platform_device *dev)
+{
+ struct da9052_charger_device *chg_device = platform_get_drvdata(dev);
+
+ /* unregister the events.*/
+ da9052_bat_unregister_event(chg_device);
+
+ cancel_rearming_delayed_workqueue(chg_device->monitor_wqueue,
+ &chg_device->monitor_work);
+ destroy_workqueue(chg_device->monitor_wqueue);
+
+ power_supply_unregister(&chg_device->psy);
+
+ return 0;
+}
+
+static struct platform_driver da9052_bat_driver = {
+ .probe = da9052_bat_probe,
+ .remove = __devexit_p(da9052_bat_remove),
+ .driver.name = DA9052_BAT_DEVICE_NAME,
+ .driver.owner = THIS_MODULE,
+};
+
+static int __init da9052_bat_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&da9052_bat_driver);
+}
+
+static void __exit da9052_bat_exit(void)
+{
+ // To remove printk("DA9052: Unregistering BAT device.\n");
+ platform_driver_unregister(&da9052_bat_driver);
+}
+
+module_init(da9052_bat_init);
+module_exit(da9052_bat_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd");
+MODULE_DESCRIPTION("DA9052 BAT Device Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c
new file mode 100644
index 000000000000..780c82427b95
--- /dev/null
+++ b/drivers/regulator/da9052-regulator.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * da9052-regulator.c: Regulator driver for DA9052
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/pm.h>
+
+static struct regulator_ops da9052_ldo_buck_ops;
+
+
+struct regulator {
+ struct device *dev;
+ struct list_head list;
+ int uA_load;
+ int min_uV;
+ int max_uV;
+ int enabled; /* client has called enabled */
+ char *supply_name;
+ struct device_attribute dev_attr;
+ struct regulator_dev *rdev;
+};
+
+
+
+
+#define DA9052_LDO(_id, max, min, step_v, reg, mbits, cbits) \
+{\
+ .reg_desc = {\
+ .name = #_id,\
+ .ops = &da9052_ldo_buck_ops,\
+ .type = REGULATOR_VOLTAGE,\
+ .id = _id,\
+ .owner = THIS_MODULE,\
+ },\
+ .reg_const = {\
+ .max_uV = (max) * 1000,\
+ .min_uV = (min) * 1000,\
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE\
+ | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE,\
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,\
+ },\
+ .step_uV = (step_v) * 1000,\
+ .reg_add = (reg),\
+ .mask_bits = (mbits),\
+ .en_bit_mask = (cbits),\
+}
+
+struct regulator_info {
+ struct regulator_desc reg_desc;
+ struct regulation_constraints reg_const;
+ int step_uV;
+ unsigned char reg_add;
+ unsigned char mask_bits;
+ unsigned char en_bit_mask;
+};
+
+struct da9052_regulator_priv {
+ struct da9052 *da9052;
+ struct regulator_dev *regulators[];
+};
+
+struct regulator_info da9052_regulators[] = {
+ /* LD01 - LDO10*/
+ DA9052_LDO(DA9052_LDO1, DA9052_LDO1_VOLT_UPPER, DA9052_LDO1_VOLT_LOWER,
+ DA9052_LDO1_VOLT_STEP, DA9052_LDO1_REG,
+ DA9052_LDO1_VLDO1, DA9052_LDO1_LDO1EN),
+
+ DA9052_LDO(DA9052_LDO2,
+ DA9052_LDO2_VOLT_UPPER, DA9052_LDO2_VOLT_LOWER,
+ DA9052_LDO2_VOLT_STEP, DA9052_LDO2_REG,
+ DA9052_LDO2_VLDO2,
+ DA9052_LDO2_LDO2EN),
+
+ DA9052_LDO(DA9052_LDO3, DA9052_LDO34_VOLT_UPPER,
+ DA9052_LDO34_VOLT_LOWER,
+ DA9052_LDO34_VOLT_STEP, DA9052_LDO3_REG,
+ DA9052_LDO3_VLDO3, DA9052_LDO3_LDO3EN),
+
+ DA9052_LDO(DA9052_LDO4, DA9052_LDO34_VOLT_UPPER,
+ DA9052_LDO34_VOLT_LOWER,
+ DA9052_LDO34_VOLT_STEP, DA9052_LDO4_REG,
+ DA9052_LDO4_VLDO4, DA9052_LDO4_LDO4EN),
+
+ DA9052_LDO(DA9052_LDO5, DA9052_LDO567810_VOLT_UPPER,
+ DA9052_LDO567810_VOLT_LOWER,
+ DA9052_LDO567810_VOLT_STEP, DA9052_LDO5_REG,
+ DA9052_LDO5_VLDO5, DA9052_LDO5_LDO5EN),
+
+ DA9052_LDO(DA9052_LDO6, DA9052_LDO567810_VOLT_UPPER,
+ DA9052_LDO567810_VOLT_LOWER,
+ DA9052_LDO567810_VOLT_STEP, DA9052_LDO6_REG,
+ DA9052_LDO6_VLDO6, DA9052_LDO6_LDO6EN),
+
+ DA9052_LDO(DA9052_LDO7, DA9052_LDO567810_VOLT_UPPER,
+ DA9052_LDO567810_VOLT_LOWER,
+ DA9052_LDO567810_VOLT_STEP, DA9052_LDO7_REG,
+ DA9052_LDO7_VLDO7, DA9052_LDO7_LDO7EN),
+
+ DA9052_LDO(DA9052_LDO8, DA9052_LDO567810_VOLT_UPPER,
+ DA9052_LDO567810_VOLT_LOWER,
+ DA9052_LDO567810_VOLT_STEP, DA9052_LDO8_REG,
+ DA9052_LDO8_VLDO8, DA9052_LDO8_LDO8EN),
+
+ DA9052_LDO(DA9052_LDO9, DA9052_LDO9_VOLT_UPPER,
+ DA9052_LDO9_VOLT_LOWER,
+ DA9052_LDO9_VOLT_STEP,
+ DA9052_LDO9_REG, DA9052_LDO9_VLDO9,
+ DA9052_LDO9_LDO9EN),
+
+ DA9052_LDO(DA9052_LDO10, DA9052_LDO567810_VOLT_UPPER,
+ DA9052_LDO567810_VOLT_LOWER,
+ DA9052_LDO567810_VOLT_STEP, DA9052_LDO10_REG,
+ DA9052_LDO10_VLDO10, DA9052_LDO10_LDO10EN),
+
+ /* BUCKS */
+ DA9052_LDO(DA9052_BUCK_CORE, DA9052_BUCK_CORE_PRO_VOLT_UPPER,
+ DA9052_BUCK_CORE_PRO_VOLT_LOWER,
+ DA9052_BUCK_CORE_PRO_STEP, DA9052_BUCKCORE_REG,
+ DA9052_BUCKCORE_VBCORE, DA9052_BUCKCORE_BCOREEN),
+
+ DA9052_LDO(DA9052_BUCK_PRO, DA9052_BUCK_CORE_PRO_VOLT_UPPER,
+ DA9052_BUCK_CORE_PRO_VOLT_LOWER,
+ DA9052_BUCK_CORE_PRO_STEP, DA9052_BUCKPRO_REG,
+ DA9052_BUCKPRO_VBPRO, DA9052_BUCKPRO_BPROEN),
+
+ DA9052_LDO(DA9052_BUCK_MEM, DA9052_BUCK_MEM_VOLT_UPPER,
+ DA9052_BUCK_MEM_VOLT_LOWER,
+ DA9052_BUCK_MEM_STEP, DA9052_BUCKMEM_REG,
+ DA9052_BUCKMEM_VBMEM, DA9052_BUCKMEM_BMEMEN),
+#if defined (CONFIG_PMIC_DA9052)
+ DA9052_LDO(DA9052_BUCK_PERI, DA9052_BUCK_PERI_VOLT_UPPER,
+ DA9052_BUCK_PERI_VOLT_LOWER,
+ DA9052_BUCK_PERI_STEP_BELOW_3000, DA9052_BUCKPERI_REG,
+ DA9052_BUCKPERI_VBPERI, DA9052_BUCKPERI_BPERIEN),
+#elif defined (CONFIG_PMIC_DA9053AA) || (CONFIG_PMIC_DA9053Bx)
+ DA9052_LDO(DA9052_BUCK_PERI, DA9052_BUCK_PERI_VOLT_UPPER,
+ DA9052_BUCK_PERI_VOLT_LOWER,
+ DA9052_BUCK_PERI_STEP, DA9052_BUCKPERI_REG,
+ DA9052_BUCKPERI_VBPERI, DA9052_BUCKPERI_BPERIEN),
+#endif
+};
+
+int da9052_ldo_buck_enable(struct regulator_dev *rdev)
+{
+ struct da9052_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = da9052_regulators[id].reg_add;
+ ssc_msg.data = 0;
+
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+
+ ssc_msg.data = (ssc_msg.data | da9052_regulators[id].en_bit_mask);
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da9052_ldo_buck_enable);
+/* Code added to support additional attribure in sysfs - changestate */
+
+
+
+int da9052_ldo_buck_disable(struct regulator_dev *rdev)
+{
+ struct da9052_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int ret;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = da9052_regulators[id].reg_add;
+ ssc_msg.data = 0;
+
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+
+ ssc_msg.data = (ssc_msg.data & ~(da9052_regulators[id].en_bit_mask));
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da9052_ldo_buck_disable);
+/* Code added to support additional attribure in sysfs - changestate */
+
+static int da9052_ldo_buck_is_enabled(struct regulator_dev *rdev)
+{
+ struct da9052_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int ret;
+ struct da9052_ssc_msg ssc_msg;
+ ssc_msg.addr = da9052_regulators[id].reg_add;
+ ssc_msg.data = 0;
+
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+ da9052_unlock(priv->da9052);
+ return (ssc_msg.data & da9052_regulators[id].en_bit_mask) != 0;
+}
+
+int da9052_ldo_buck_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct da9052_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct da9052_ssc_msg ssc_msg;
+ int id = rdev_get_id(rdev);
+ int ret;
+ int ldo_volt = 0;
+
+ /* Below if condition is there for added setvoltage attribute
+ in sysfs */
+ if (0 == max_uV)
+ max_uV = da9052_regulators[id].reg_const.max_uV;
+
+ /* Compare voltage range */
+ if (min_uV > max_uV)
+ return -EINVAL;
+
+ /* Check Minimum/ Maximum voltage range */
+ if (min_uV < da9052_regulators[id].reg_const.min_uV ||
+ min_uV > da9052_regulators[id].reg_const.max_uV)
+ return -EINVAL;
+ if (max_uV < da9052_regulators[id].reg_const.min_uV ||
+ max_uV > da9052_regulators[id].reg_const.max_uV)
+ return -EINVAL;
+#if defined (CONFIG_PMIC_DA9052)
+ /* Get the ldo register value */
+ /* Varying step size for BUCK PERI */
+ if ((da9052_regulators[id].reg_desc.id == DA9052_BUCK_PERI) &&
+ (min_uV >= DA9052_BUCK_PERI_VALUES_3000)) {
+ ldo_volt = (DA9052_BUCK_PERI_VALUES_3000 -
+ da9052_regulators[id].reg_const.min_uV)/
+ (da9052_regulators[id].step_uV);
+ ldo_volt += (min_uV - DA9052_BUCK_PERI_VALUES_3000)/
+ (DA9052_BUCK_PERI_STEP_ABOVE_3000);
+ } else{
+ ldo_volt = (min_uV - da9052_regulators[id].reg_const.min_uV)/
+ (da9052_regulators[id].step_uV);
+ /* Check for maximum value */
+ if ((ldo_volt * da9052_regulators[id].step_uV) +
+ da9052_regulators[id].reg_const.min_uV > max_uV)
+ return -EINVAL;
+ }
+#elif defined (CONFIG_PMIC_DA9053AA) ||(CONFIG_PMIC_DA9053Bx)
+ ldo_volt = (min_uV - da9052_regulators[id].reg_const.min_uV)/
+ (da9052_regulators[id].step_uV);
+ /* Check for maximum value */
+ if ((ldo_volt * da9052_regulators[id].step_uV) +
+ da9052_regulators[id].reg_const.min_uV > max_uV)
+ return -EINVAL;
+#endif
+ /* Configure LDO Voltage, CONF bits */
+ ssc_msg.addr = da9052_regulators[id].reg_add;
+ ssc_msg.data = 0;
+
+ /* Read register */
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+
+ ssc_msg.data = (ssc_msg.data & ~(da9052_regulators[id].mask_bits));
+ ssc_msg.data |= ldo_volt;
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+
+ /* Set the GO LDO/BUCk bits so that the voltage changes */
+ ssc_msg.addr = DA9052_SUPPLY_REG;
+ ssc_msg.data = 0;
+
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+
+ switch (id) {
+ case DA9052_LDO2:
+ ssc_msg.data = (ssc_msg.data | DA9052_SUPPLY_VLDO2GO);
+ break;
+ case DA9052_LDO3:
+ ssc_msg.data = (ssc_msg.data | DA9052_SUPPLY_VLDO3GO);
+ break;
+ case DA9052_BUCK_CORE:
+ ssc_msg.data = (ssc_msg.data | DA9052_SUPPLY_VBCOREGO);
+ break;
+ case DA9052_BUCK_PRO:
+ ssc_msg.data = (ssc_msg.data | DA9052_SUPPLY_VBPROGO);
+ break;
+ case DA9052_BUCK_MEM:
+ ssc_msg.data = (ssc_msg.data | DA9052_SUPPLY_VBMEMGO);
+ break;
+ default:
+ da9052_unlock(priv->da9052);
+ return -EINVAL;
+ }
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+
+ da9052_unlock(priv->da9052);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da9052_ldo_buck_set_voltage);
+/* Code added to support additional attributes in sysfs - setvoltage */
+
+
+int da9052_ldo_buck_get_voltage(struct regulator_dev *rdev)
+{
+ struct da9052_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct da9052_ssc_msg ssc_msg;
+ int id = rdev_get_id(rdev);
+ int ldo_volt = 0;
+ int ldo_volt_uV = 0;
+ int ret;
+
+ ssc_msg.addr = da9052_regulators[id].reg_add;
+ ssc_msg.data = 0;
+ /* Read register */
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret) {
+ da9052_unlock(priv->da9052);
+ return -EIO;
+ }
+ da9052_unlock(priv->da9052);
+
+ ldo_volt = ssc_msg.data & da9052_regulators[id].mask_bits;
+#if defined (CONFIG_PMIC_DA9052)
+ if (da9052_regulators[id].reg_desc.id == DA9052_BUCK_PERI) {
+ if (ldo_volt >= DA9052_BUCK_PERI_VALUES_UPTO_3000) {
+ ldo_volt_uV = ((DA9052_BUCK_PERI_VALUES_UPTO_3000 *
+ da9052_regulators[id].step_uV)
+ + da9052_regulators[id].reg_const.min_uV);
+ ldo_volt_uV = (ldo_volt_uV +
+ (ldo_volt - DA9052_BUCK_PERI_VALUES_UPTO_3000)
+ * (DA9052_BUCK_PERI_STEP_ABOVE_3000));
+ } else {
+ ldo_volt_uV =
+ (ldo_volt * da9052_regulators[id].step_uV)
+ + da9052_regulators[id].reg_const.min_uV;
+ }
+ } else {
+ ldo_volt_uV = (ldo_volt * da9052_regulators[id].step_uV) +
+ da9052_regulators[id].reg_const.min_uV;
+ }
+#elif defined (CONFIG_PMIC_DA9053AA) || (CONFIG_PMIC_DA9053Bx)
+ ldo_volt_uV = (ldo_volt * da9052_regulators[id].step_uV) +
+ da9052_regulators[id].reg_const.min_uV;
+#endif
+ return ldo_volt_uV;
+}
+EXPORT_SYMBOL_GPL(da9052_ldo_buck_get_voltage);
+/* Code added to support additional attributes in sysfs - setvoltage */
+
+
+static struct regulator_ops da9052_ldo_buck_ops = {
+ .is_enabled = da9052_ldo_buck_is_enabled,
+ .enable = da9052_ldo_buck_enable,
+ .disable = da9052_ldo_buck_disable,
+ .get_voltage = da9052_ldo_buck_get_voltage,
+ .set_voltage = da9052_ldo_buck_set_voltage,
+};
+
+static int __devinit da9052_regulator_probe(struct platform_device *pdev)
+{
+ struct da9052_regulator_priv *priv;
+ struct da9052_regulator_platform_data *pdata =
+ (pdev->dev.platform_data);
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_init_data *init_data;
+ int i, ret = 0;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ priv->da9052 = da9052;
+ for (i = 0; i < 14; i++) {
+
+ init_data = &pdata->regulators[i];
+ init_data->driver_data = da9052;
+ pdev->dev.platform_data = init_data;
+ priv->regulators[i] = regulator_register(
+ &da9052_regulators[i].reg_desc,
+ &pdev->dev, priv);
+ if (IS_ERR(priv->regulators[i])) {
+ ret = PTR_ERR(priv->regulators[i]);
+ goto err;
+ }
+ }
+ platform_set_drvdata(pdev, priv);
+ return 0;
+err:
+ while (--i >= 0)
+ regulator_unregister(priv->regulators[i]);
+ kfree(priv);
+ return ret;
+}
+
+static int __devexit da9052_regulator_remove(struct platform_device *pdev)
+{
+ struct da9052_regulator_priv *priv = platform_get_drvdata(pdev);
+ struct da9052_platform_data *pdata = pdev->dev.platform_data;
+ int i;
+
+ for (i = 0; i < pdata->num_regulators; i++)
+ regulator_unregister(priv->regulators[i]);
+
+ return 0;
+}
+
+static struct platform_driver da9052_regulator_driver = {
+ .probe = da9052_regulator_probe,
+ .remove = __devexit_p(da9052_regulator_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_regulator_init(void)
+{
+ return platform_driver_register(&da9052_regulator_driver);
+}
+subsys_initcall(da9052_regulator_init);
+
+static void __exit da9052_regulator_exit(void)
+{
+ platform_driver_unregister(&da9052_regulator_driver);
+}
+module_exit(da9052_regulator_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
new file mode 100644
index 000000000000..be80c228d5a3
--- /dev/null
+++ b/drivers/rtc/rtc-da9052.c
@@ -0,0 +1,694 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * rtc-da9052.c: RTC driver for DA9052
+ */
+
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/rtc.h>
+
+#define DRIVER_NAME "da9052-rtc"
+#define ENABLE 1
+#define DISABLE 0
+
+struct da9052_rtc {
+ struct rtc_device *rtc;
+ struct da9052 *da9052;
+ struct da9052_eh_nb eh_data;
+ unsigned char is_min_alarm;
+ unsigned char enable_tick_alarm;
+ unsigned char enable_clk_buffer;
+ unsigned char set_osc_trim_freq;
+};
+
+static int da9052_rtc_enable_alarm(struct da9052 *da9052, unsigned char flag);
+
+void da9052_rtc_notifier(struct da9052_eh_nb *eh_data, unsigned int event)
+{
+ struct da9052_rtc *rtc =
+ container_of(eh_data, struct da9052_rtc, eh_data);
+ struct da9052_ssc_msg msg;
+ unsigned int ret;
+
+ /* Check the alarm type - TIMER or TICK */
+ msg.addr = DA9052_ALARMMI_REG;
+
+ da9052_lock(rtc->da9052);
+ ret = rtc->da9052->read(rtc->da9052, &msg);
+ if (ret != 0) {
+ da9052_unlock(rtc->da9052);
+ return;
+ }
+
+ da9052_unlock(rtc->da9052);
+
+
+ if (msg.data & DA9052_ALARMMI_ALARMTYPE) {
+ da9052_rtc_enable_alarm(rtc->da9052, 0);
+ printk(KERN_INFO "RTC: TIMER ALARM\n");
+ } else {
+ kobject_uevent(&rtc->rtc->dev.kobj, KOBJ_CHANGE);
+ printk(KERN_INFO "RTC: TICK ALARM\n");
+ }
+}
+
+static int da9052_rtc_validate_parameters(struct rtc_time *rtc_tm)
+{
+
+ if (rtc_tm->tm_sec > DA9052_RTC_SECONDS_LIMIT)
+ return DA9052_RTC_INVALID_SECONDS;
+
+ if (rtc_tm->tm_min > DA9052_RTC_MINUTES_LIMIT)
+ return DA9052_RTC_INVALID_MINUTES;
+
+ if (rtc_tm->tm_hour > DA9052_RTC_HOURS_LIMIT)
+ return DA9052_RTC_INVALID_HOURS;
+
+ if (rtc_tm->tm_mday == 0)
+ return DA9052_RTC_INVALID_DAYS;
+
+ if ((rtc_tm->tm_mon > DA9052_RTC_MONTHS_LIMIT) ||
+ (rtc_tm->tm_mon == 0))
+ return DA9052_RTC_INVALID_MONTHS;
+
+ if (rtc_tm->tm_year > DA9052_RTC_YEARS_LIMIT)
+ return DA9052_RTC_INVALID_YEARS;
+
+ if ((rtc_tm->tm_mon == FEBRUARY)) {
+ if (((rtc_tm->tm_year % 4 == 0) &&
+ (rtc_tm->tm_year % 100 != 0)) ||
+ (rtc_tm->tm_year % 400 == 0)) {
+ if (rtc_tm->tm_mday > 29)
+ return DA9052_RTC_INVALID_DAYS;
+ } else if (rtc_tm->tm_mday > 28) {
+ return DA9052_RTC_INVALID_DAYS;
+ }
+ }
+
+ if (((rtc_tm->tm_mon == APRIL) || (rtc_tm->tm_mon == JUNE) ||
+ (rtc_tm->tm_mon == SEPTEMBER) || (rtc_tm->tm_mon == NOVEMBER))
+ && (rtc_tm->tm_mday == 31)) {
+ return DA9052_RTC_INVALID_DAYS;
+ }
+
+
+ return 0;
+}
+
+static int da9052_rtc_settime(struct da9052 *da9052, struct rtc_time *rtc_tm)
+{
+
+ struct da9052_ssc_msg msg_arr[6];
+ int validate_param = 0;
+ unsigned char loop_index = 0;
+ int ret = 0;
+
+
+ /* System compatability */
+ rtc_tm->tm_year -= 100;
+ rtc_tm->tm_mon += 1;
+
+ validate_param = da9052_rtc_validate_parameters(rtc_tm);
+ if (validate_param)
+ return validate_param;
+
+ msg_arr[loop_index].addr = DA9052_COUNTS_REG;
+ msg_arr[loop_index++].data = DA9052_COUNTS_MONITOR | rtc_tm->tm_sec;
+
+ msg_arr[loop_index].addr = DA9052_COUNTMI_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_min;
+
+ msg_arr[loop_index].addr = DA9052_COUNTH_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_hour;
+
+ msg_arr[loop_index].addr = DA9052_COUNTD_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_mday;
+
+ msg_arr[loop_index].addr = DA9052_COUNTMO_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_mon;
+
+ msg_arr[loop_index].addr = DA9052_COUNTY_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_year;
+
+ da9052_lock(da9052);
+ ret = da9052->write_many(da9052, msg_arr, loop_index);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ da9052_unlock(da9052);
+ return 0;
+}
+
+static int da9052_rtc_gettime(struct da9052 *da9052, struct rtc_time *rtc_tm)
+{
+
+ struct da9052_ssc_msg msg[6];
+ unsigned char loop_index = 0;
+ int validate_param = 0;
+ int ret = 0;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_COUNTS_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_COUNTMI_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_COUNTH_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_COUNTD_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_COUNTMO_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_COUNTY_REG;
+
+ da9052_lock(da9052);
+ ret = da9052->read_many(da9052, msg, loop_index);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+ da9052_unlock(da9052);
+
+ rtc_tm->tm_year = msg[--loop_index].data & DA9052_COUNTY_COUNTYEAR;
+ rtc_tm->tm_mon = msg[--loop_index].data & DA9052_COUNTMO_COUNTMONTH;
+ rtc_tm->tm_mday = msg[--loop_index].data & DA9052_COUNTD_COUNTDAY;
+ rtc_tm->tm_hour = msg[--loop_index].data & DA9052_COUNTH_COUNTHOUR;
+ rtc_tm->tm_min = msg[--loop_index].data & DA9052_COUNTMI_COUNTMIN;
+ rtc_tm->tm_sec = msg[--loop_index].data & DA9052_COUNTS_COUNTSEC;
+
+ validate_param = da9052_rtc_validate_parameters(rtc_tm);
+ if (validate_param)
+ return validate_param;
+
+ /* System compatability */
+ rtc_tm->tm_year += 100;
+ rtc_tm->tm_mon -= 1;
+ return 0;
+}
+
+static int da9052_alarm_gettime(struct da9052 *da9052, struct rtc_time *rtc_tm)
+{
+ struct da9052_ssc_msg msg[5];
+ unsigned char loop_index = 0;
+ int validate_param = 0;
+ int ret = 0;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_ALARMMI_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_ALARMH_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_ALARMD_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_ALARMMO_REG;
+
+ msg[loop_index].data = 0;
+ msg[loop_index++].addr = DA9052_ALARMY_REG;
+
+ da9052_lock(da9052);
+ ret = da9052->read_many(da9052, msg, loop_index);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+ da9052_unlock(da9052);
+
+ rtc_tm->tm_year = msg[--loop_index].data & DA9052_ALARMY_ALARMYEAR;
+ rtc_tm->tm_mon = msg[--loop_index].data & DA9052_ALARMMO_ALARMMONTH;
+ rtc_tm->tm_mday = msg[--loop_index].data & DA9052_ALARMD_ALARMDAY;
+ rtc_tm->tm_hour = msg[--loop_index].data & DA9052_ALARMH_ALARMHOUR;
+ rtc_tm->tm_min = msg[--loop_index].data & DA9052_ALARMMI_ALARMMIN;
+
+ validate_param = da9052_rtc_validate_parameters(rtc_tm);
+ if (validate_param)
+ return validate_param;
+
+ /* System compatability */
+ rtc_tm->tm_year += 100;
+ rtc_tm->tm_mon -= 1;
+
+ return 0;
+}
+
+static int da9052_alarm_settime(struct da9052 *da9052, struct rtc_time *rtc_tm)
+{
+
+ struct da9052_ssc_msg msg_arr[5];
+ struct da9052_ssc_msg msg;
+ int validate_param = 0;
+ unsigned char loop_index = 0;
+ int ret = 0;
+
+ rtc_tm->tm_sec = 0;
+
+ /* System compatability */
+ rtc_tm->tm_year -= 100;
+ rtc_tm->tm_mon += 1;
+
+ validate_param = da9052_rtc_validate_parameters(rtc_tm);
+ if (validate_param)
+ return validate_param;
+
+ msg.addr = DA9052_ALARMMI_REG;
+ msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ msg.data = msg.data & ~(DA9052_ALARMMI_ALARMMIN);
+ msg.data |= rtc_tm->tm_min;
+
+ msg_arr[loop_index].addr = DA9052_ALARMMI_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = msg.data;
+
+ msg_arr[loop_index].addr = DA9052_ALARMH_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_hour;
+
+ msg_arr[loop_index].addr = DA9052_ALARMD_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_mday;
+
+ msg_arr[loop_index].addr = DA9052_ALARMMO_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = rtc_tm->tm_mon;
+
+ msg.addr = DA9052_ALARMY_REG;
+ msg.data = 0;
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ msg.data = msg.data & ~(DA9052_ALARMY_ALARMYEAR);
+
+
+ msg.data |= rtc_tm->tm_year;
+ msg_arr[loop_index].addr = DA9052_ALARMY_REG;
+ msg_arr[loop_index].data = 0;
+ msg_arr[loop_index++].data = msg.data;
+
+ ret = da9052->write_many(da9052, msg_arr, loop_index);
+ if (ret) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ da9052_unlock(da9052);
+ return 0;
+}
+
+static int da9052_rtc_get_alarm_status(struct da9052 *da9052)
+{
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+ msg.addr = DA9052_ALARMY_REG;
+ msg.data = 0;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ da9052_unlock(da9052);
+ msg.data &= DA9052_ALARMY_ALARMON;
+
+ return (msg.data > 0) ? 1 : 0;
+}
+
+
+static int da9052_rtc_enable_alarm(struct da9052 *da9052, unsigned char flag)
+{
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+ msg.addr = DA9052_ALARMY_REG;
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ if (flag)
+ msg.data = msg.data | DA9052_ALARMY_ALARMON;
+ else
+ msg.data = msg.data & ~(DA9052_ALARMY_ALARMON);
+
+ ret = da9052->write(da9052, &msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+ da9052_unlock(da9052);
+
+ return 0;
+}
+
+
+static ssize_t da9052_rtc_mask_irq(struct da9052 *da9052)
+ {
+ unsigned char data = 0;
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = DA9052_IRQMASKA_REG;
+ ssc_msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ data = ret;
+ ssc_msg.data = data |= DA9052_IRQMASKA_MALRAM;
+
+ ret = da9052->write(da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ da9052_unlock(da9052);
+ return 0;
+}
+
+
+static ssize_t da9052_rtc_unmask_irq(struct da9052 *da9052)
+{
+ unsigned char data = 0;
+ ssize_t ret = 0;
+ struct da9052_ssc_msg ssc_msg;
+
+ ssc_msg.addr = DA9052_IRQMASKA_REG;
+ ssc_msg.data = 0;
+
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ data = ret;
+ ssc_msg.data = data &= ~DA9052_IRQMASKA_MALRAM;
+
+ ret = da9052->write(da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(da9052);
+ return ret;
+ }
+
+ da9052_unlock(da9052);
+ return 0;
+
+}
+
+static int da9052_rtc_class_ops_gettime
+ (struct device *dev, struct rtc_time *rtc_tm)
+{
+ int ret;
+ struct da9052 *da9052 = dev->parent->driver_data;
+ ret = da9052_rtc_gettime(da9052, rtc_tm);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+
+static int da9052_rtc_class_ops_settime(struct device *dev, struct rtc_time *tm)
+{
+ int ret;
+ struct da9052 *da9052 = dev->parent->driver_data;
+ ret = da9052_rtc_settime(da9052, tm);
+
+ return ret;
+}
+
+static int da9052_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9052 *da9052 = dev->parent->driver_data;
+ ret = da9052_alarm_gettime(da9052, tm);
+
+ if (ret)
+ return ret;
+
+ alrm->enabled = da9052_rtc_get_alarm_status(da9052);
+
+ return 0;
+
+}
+
+static int da9052_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret = 0;
+ struct rtc_time *tm = &alrm->time;
+ struct da9052 *da9052 = dev->parent->driver_data;
+
+ ret = da9052_alarm_settime(da9052, tm);
+
+ if (ret)
+ return ret;
+
+ ret = da9052_rtc_enable_alarm(da9052, 1);
+
+ return ret;
+}
+
+static int da9052_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct da9052_rtc *priv = dev_get_drvdata(dev);
+ int ret = -ENODATA;
+
+ da9052_lock(priv->da9052);
+
+ ret = (enabled ? da9052_rtc_unmask_irq : da9052_rtc_mask_irq)
+ (priv->da9052);
+
+ da9052_unlock(priv->da9052);
+
+ return ret;
+}
+
+static int da9052_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct da9052_rtc *priv = dev_get_drvdata(dev);
+
+ if (enabled)
+ return da9052_rtc_enable_alarm(priv->da9052, enabled);
+ else
+ return da9052_rtc_enable_alarm(priv->da9052, enabled);
+}
+
+static const struct rtc_class_ops da9052_rtc_ops = {
+ .read_time = da9052_rtc_class_ops_gettime,
+ .set_time = da9052_rtc_class_ops_settime,
+ .read_alarm = da9052_rtc_readalarm,
+ .set_alarm = da9052_rtc_setalarm,
+#if 0
+ .update_irq_enable = da9052_rtc_update_irq_enable,
+ .alarm_irq_enable = da9052_rtc_alarm_irq_enable,
+#endif
+};
+
+
+static int __devinit da9052_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct da9052_rtc *priv;
+ struct da9052_ssc_msg ssc_msg;
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, priv);
+
+ /* Added to support sysfs wakealarm attribute */
+ pdev->dev.power.can_wakeup = 1;
+ /* Added to support sysfs wakealarm attribute */
+
+ /* Set the EH structure */
+ priv->eh_data.eve_type = ALARM_EVE;
+ priv->eh_data.call_back = &da9052_rtc_notifier;
+ ret = priv->da9052->register_event_notifier(priv->da9052,
+ &priv->eh_data);
+ if (ret)
+ goto err_register_alarm;
+
+ priv->is_min_alarm = 1;
+ priv->enable_tick_alarm = 1;
+ priv->enable_clk_buffer = 1;
+ priv->set_osc_trim_freq = 5;
+ /* Enable/Disable TICK Alarm */
+ /* Read ALARM YEAR register */
+ ssc_msg.addr = DA9052_ALARMY_REG;
+ ssc_msg.data = 0;
+
+ da9052_lock(priv->da9052);
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+
+ if (priv->enable_tick_alarm)
+ ssc_msg.data = (ssc_msg.data | DA9052_ALARMY_TICKON);
+ else
+ ssc_msg.data =
+ ((ssc_msg.data & ~(DA9052_ALARMY_TICKON)));
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+
+ /* Set TICK Alarm to 1 minute or 1 sec */
+ /* Read ALARM MINUTES register */
+ ssc_msg.addr = DA9052_ALARMMI_REG;
+ ssc_msg.data = 0;
+
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+
+ if (priv->is_min_alarm)
+ /* Set 1 minute tick type */
+ ssc_msg.data = (ssc_msg.data | DA9052_ALARMMI_TICKTYPE);
+ else
+ /* Set 1 sec tick type */
+ ssc_msg.data = (ssc_msg.data & ~(DA9052_ALARMMI_TICKTYPE));
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+
+ /* Enable/Disable Clock buffer in Power Down Mode */
+ ssc_msg.addr = DA9052_PDDIS_REG;
+ ssc_msg.data = 0;
+
+ ret = priv->da9052->read(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+
+ if (priv->enable_clk_buffer)
+ ssc_msg.data = (ssc_msg.data | DA9052_PDDIS_OUT32KPD);
+ else
+ ssc_msg.data = (ssc_msg.data & ~(DA9052_PDDIS_OUT32KPD));
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+
+ /* Set clock trim frequency value */
+ ssc_msg.addr = DA9052_OSCTRIM_REG;
+ ssc_msg.data = priv->set_osc_trim_freq;
+
+ ret = priv->da9052->write(priv->da9052, &ssc_msg);
+ if (ret != 0) {
+ da9052_unlock(priv->da9052);
+ goto err_ssc_comm;
+ }
+ da9052_unlock(priv->da9052);
+
+ priv->rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &da9052_rtc_ops, THIS_MODULE);
+ if (IS_ERR(priv->rtc)) {
+ ret = PTR_ERR(priv->rtc);
+ goto err_ssc_comm;
+ }
+ return 0;
+
+err_ssc_comm:
+ priv->da9052->unregister_event_notifier
+ (priv->da9052, &priv->eh_data);
+err_register_alarm:
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit da9052_rtc_remove(struct platform_device *pdev)
+{
+ struct da9052_rtc *priv = platform_get_drvdata(pdev);
+ rtc_device_unregister(priv->rtc);
+ da9052_lock(priv->da9052);
+ priv->da9052->unregister_event_notifier(priv->da9052, &priv->eh_data);
+ da9052_unlock(priv->da9052);
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver da9052_rtc_driver = {
+ .probe = da9052_rtc_probe,
+ .remove = __devexit_p(da9052_rtc_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init da9052_rtc_init(void)
+{
+ return platform_driver_register(&da9052_rtc_driver);
+}
+module_init(da9052_rtc_init);
+
+static void __exit da9052_rtc_exit(void)
+{
+ platform_driver_unregister(&da9052_rtc_driver);
+}
+module_exit(da9052_rtc_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/spi/spi_sam.c b/drivers/spi/spi_sam.c
new file mode 100644
index 000000000000..a67a01368010
--- /dev/null
+++ b/drivers/spi/spi_sam.c
@@ -0,0 +1,1161 @@
+/*
+ * spi_sam.c - Samsung SOC SPI controller driver.
+ * By -- Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <asm/gpio.h>
+#include <asm/dma.h>
+
+#include "spi_sam.h"
+
+//#define DEBUGSPI
+
+#ifdef DEBUGSPI
+
+#define dbg_printk(x...) printk(x)
+
+static void dump_regs(struct samspi_bus *sspi)
+{
+ u32 val;
+
+ val = readl(sspi->regs + SAMSPI_CH_CFG);
+ printk("CHN-%x\t", val);
+ val = readl(sspi->regs + SAMSPI_CLK_CFG);
+ printk("CLK-%x\t", val);
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ printk("MOD-%x\t", val);
+ val = readl(sspi->regs + SAMSPI_SLAVE_SEL);
+ printk("SLVSEL-%x\t", val);
+ val = readl(sspi->regs + SAMSPI_SPI_STATUS);
+ if(val & SPI_STUS_TX_DONE)
+ printk("TX_done\t");
+ if(val & SPI_STUS_TRAILCNT_ZERO)
+ printk("TrailZ\t");
+ if(val & SPI_STUS_RX_OVERRUN_ERR)
+ printk("RX_Ovrn\t");
+ if(val & SPI_STUS_RX_UNDERRUN_ERR)
+ printk("Rx_Unrn\t");
+ if(val & SPI_STUS_TX_OVERRUN_ERR)
+ printk("Tx_Ovrn\t");
+ if(val & SPI_STUS_TX_UNDERRUN_ERR)
+ printk("Tx_Unrn\t");
+ if(val & SPI_STUS_RX_FIFORDY)
+ printk("Rx_Rdy\t");
+ if(val & SPI_STUS_TX_FIFORDY)
+ printk("Tx_Rdy\t");
+ printk("Rx/TxLvl=%d,%d\n", (val>>13)&0x7f, (val>>6)&0x7f);
+}
+
+static void dump_spidevice_info(struct spi_device *spi)
+{
+ dbg_printk("Modalias = %s\n", spi->modalias);
+ dbg_printk("Slave-%d on Bus-%d\n", spi->chip_select, spi->master->bus_num);
+ dbg_printk("max_speed_hz = %d\n", spi->max_speed_hz);
+ dbg_printk("bits_per_word = %d\n", spi->bits_per_word);
+ dbg_printk("irq = %d\n", spi->irq);
+ dbg_printk("Clk Phs = %d\n", spi->mode & SPI_CPHA);
+ dbg_printk("Clk Pol = %d\n", spi->mode & SPI_CPOL);
+ dbg_printk("ActiveCS = %s\n", (spi->mode & (1<<2)) ? "high" : "low" );
+ dbg_printk("Our Mode = %s\n", (spi->mode & SPI_SLAVE) ? "Slave" : "Master");
+}
+
+#else
+
+#define dbg_printk(x...) /**/
+#define dump_regs(sspi) /**/
+#define dump_spidevice_info(spi) /**/
+
+#endif
+
+static void dump_spi_regs(struct samspi_bus *sspi)
+{
+ printk(KERN_CRIT "Reg Info \n");
+ printk(KERN_CRIT "CH_CFG = 0x%8.8x\n", readl(sspi->regs + SAMSPI_CH_CFG));
+ printk(KERN_CRIT "CLK_CFG = 0x%8.8x\n", readl(sspi->regs + SAMSPI_CLK_CFG));
+ printk(KERN_CRIT "MODE_CFG = 0x%8.8x\n", readl(sspi->regs + SAMSPI_MODE_CFG));
+ printk(KERN_CRIT "CS_REG = 0x%8.8x\n", readl(sspi->regs + SAMSPI_SLAVE_SEL));
+ printk(KERN_CRIT "SPI_INT_EN = 0x%8.8x\n", readl(sspi->regs + SAMSPI_SPI_INT_EN));
+ printk(KERN_CRIT "SPI_STATUS = 0x%8.8x\n", readl(sspi->regs + SAMSPI_SPI_STATUS));
+// printk(KERN_CRIT "SAMSPI_SPI_RX_DATA = 0x%8.8x\n", readl(sspi->regs + SAMSPI_SPI_RX_DATA));
+// printk(KERN_CRIT "SAMSPI_SPI_TX_DATA = 0x%8.8x\n", readl(sspi->regs + SAMSPI_SPI_TX_DATA));
+}
+
+static struct s3c2410_dma_client samspi_dma_client = {
+ .name = "samspi-dma",
+};
+
+static int sspi_getclcks(struct samspi_bus *sspi)
+{
+ struct clk *cspi, *cp, *cm, *cf;
+
+ cp = NULL;
+ cm = NULL;
+ cf = NULL;
+ cspi = sspi->clk;
+
+ if(cspi == NULL){
+ cspi = clk_get(&sspi->pdev->dev, "spi");
+ if(IS_ERR(cspi)){
+ printk("Unable to get spi!\n");
+ return -EBUSY;
+ }
+ }
+ dbg_printk("%s:%d Got clk=spi\n", __func__, __LINE__);
+
+#if defined(CONFIG_SPICLK_SRC_SCLK48M) || defined(CONFIG_SPICLK_SRC_EPLL) || defined(CONFIG_SPICLK_SRC_SPIEXT)
+ cp = clk_get(&sspi->pdev->dev, spiclk_src);
+ if(IS_ERR(cp)){
+ printk("Unable to get parent clock(%s)!\n", spiclk_src);
+ if(sspi->clk == NULL){
+ clk_disable(cspi);
+ clk_put(cspi);
+ }
+ return -EBUSY;
+ }
+ dbg_printk("%s:%d Got clk=%s\n", __func__, __LINE__, spiclk_src);
+
+#if defined(CONFIG_SPICLK_SRC_EPLL) || defined(CONFIG_SPICLK_SRC_SPIEXT)
+ cm = clk_get(&sspi->pdev->dev, spisclk_src);
+ if(IS_ERR(cm)){
+ printk("Unable to get %s\n", spisclk_src);
+ clk_put(cp);
+ return -EBUSY;
+ }
+ dbg_printk("%s:%d Got clk=%s\n", __func__, __LINE__, spisclk_src);
+ if(clk_set_parent(cp, cm)){
+ printk("failed to set %s as the parent of %s\n", spisclk_src, spiclk_src);
+ clk_put(cm);
+ clk_put(cp);
+ return -EBUSY;
+ }
+ dbg_printk("Set %s as the parent of %s\n", spisclk_src, spiclk_src);
+
+#if defined(CONFIG_SPICLK_EPLL_MOUTEPLL) /* MOUTepll through EPLL */
+ cf = clk_get(&sspi->pdev->dev, "fout_epll");
+ if(IS_ERR(cf)){
+ printk("Unable to get fout_epll\n");
+ clk_put(cm);
+ clk_put(cp);
+ return -EBUSY;
+ }
+ dbg_printk("Got fout_epll\n");
+ if(clk_set_parent(cm, cf)){
+ printk("failed to set FOUTepll as parent of %s\n", spisclk_src);
+ clk_put(cf);
+ clk_put(cm);
+ clk_put(cp);
+ return -EBUSY;
+ }
+ dbg_printk("Set FOUTepll as parent of %s\n", spisclk_src);
+ clk_put(cf);
+#endif
+ clk_put(cm);
+#endif
+
+ sspi->prnt_clk = cp;
+#endif
+
+ sspi->clk = cspi;
+ return 0;
+}
+
+static void sspi_putclcks(struct samspi_bus *sspi)
+{
+ if(sspi->prnt_clk != NULL)
+ clk_put(sspi->prnt_clk);
+
+ clk_put(sspi->clk);
+}
+
+static int sspi_enclcks(struct samspi_bus *sspi)
+{
+ if(sspi->prnt_clk != NULL)
+ clk_enable(sspi->prnt_clk);
+
+ return clk_enable(sspi->clk);
+}
+
+static void sspi_disclcks(struct samspi_bus *sspi)
+{
+ if(sspi->prnt_clk != NULL)
+ clk_disable(sspi->prnt_clk);
+
+ clk_disable(sspi->clk);
+}
+
+static unsigned long sspi_getrate(struct samspi_bus *sspi)
+{
+ if(sspi->prnt_clk != NULL)
+ return clk_get_rate(sspi->prnt_clk);
+ else
+ return clk_get_rate(sspi->clk);
+}
+
+static int sspi_setrate(struct samspi_bus *sspi, unsigned long r)
+{
+ /* We don't take charge of the Src Clock, yet */
+ return 0;
+}
+
+static inline void enable_spidma(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ u32 val;
+
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~(SPI_MODE_TXDMA_ON | SPI_MODE_RXDMA_ON);
+ if(xfer->tx_buf != NULL)
+ val |= SPI_MODE_TXDMA_ON;
+ if(xfer->rx_buf != NULL)
+ val |= SPI_MODE_RXDMA_ON;
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+}
+
+static inline void flush_dma(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ if(xfer->tx_buf != NULL)
+ s3c2410_dma_ctrl(sspi->tx_dmach, S3C2410_DMAOP_FLUSH);
+ if(xfer->rx_buf != NULL)
+ s3c2410_dma_ctrl(sspi->rx_dmach, S3C2410_DMAOP_FLUSH);
+}
+
+static inline void flush_spi(struct samspi_bus *sspi)
+{
+ u32 val;
+
+ val = readl(sspi->regs + SAMSPI_CH_CFG);
+ val |= SPI_CH_SW_RST;
+ val &= ~SPI_CH_HS_EN;
+ if((sspi->cur_speed > 30000000UL) && !(sspi->cur_mode & SPI_SLAVE)) /* TODO ??? */
+ val |= SPI_CH_HS_EN;
+ writel(val, sspi->regs + SAMSPI_CH_CFG);
+
+ /* Flush TxFIFO*/
+ do{
+ val = readl(sspi->regs + SAMSPI_SPI_STATUS);
+ val = (val>>6) & 0x7f;
+ }while(val);
+
+ /* Flush RxFIFO*/
+ val = readl(sspi->regs + SAMSPI_SPI_STATUS);
+ val = (val>>13) & 0x7f;
+ while(val){
+ readl(sspi->regs + SAMSPI_SPI_RX_DATA);
+ val = readl(sspi->regs + SAMSPI_SPI_STATUS);
+ val = (val>>13) & 0x7f;
+ }
+
+ val = readl(sspi->regs + SAMSPI_CH_CFG);
+ val &= ~SPI_CH_SW_RST;
+ writel(val, sspi->regs + SAMSPI_CH_CFG);
+}
+
+static inline void enable_spichan(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ u32 val;
+
+ //printk(KERN_CRIT "@@@@@@@@ enable_spichan() \n");
+
+ val = readl(sspi->regs + SAMSPI_CH_CFG);
+ val &= ~(SPI_CH_RXCH_ON | SPI_CH_TXCH_ON);
+ if(xfer->tx_buf != NULL){
+ val |= SPI_CH_TXCH_ON;
+ }
+ if(xfer->rx_buf != NULL){
+ if(!(sspi->cur_mode & SPI_SLAVE)){
+ writel((xfer->len & 0xffff) | SPI_PACKET_CNT_EN,
+ sspi->regs + SAMSPI_PACKET_CNT); /* XXX TODO Bytes or number of SPI-Words? */
+ }
+ val |= SPI_CH_RXCH_ON;
+ }
+ writel(val, sspi->regs + SAMSPI_CH_CFG);
+}
+
+static inline void enable_spiintr(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ u32 val = 0;
+
+ if(xfer->tx_buf != NULL){
+ val |= SPI_INT_TX_OVERRUN_EN;
+ if(!(sspi->cur_mode & SPI_SLAVE))
+ val |= SPI_INT_TX_UNDERRUN_EN;
+ }
+ if(xfer->rx_buf != NULL){
+ val |= (SPI_INT_RX_UNDERRUN_EN | SPI_INT_RX_OVERRUN_EN | SPI_INT_TRAILING_EN);
+ }
+ writel(val, sspi->regs + SAMSPI_SPI_INT_EN);
+}
+
+static inline void enable_spienqueue(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ if(xfer->rx_buf != NULL){
+ sspi->rx_done = BUSY;
+ s3c2410_dma_config(sspi->rx_dmach, sspi->cur_bpw/8, 0);
+ s3c2410_dma_enqueue(sspi->rx_dmach, (void *)sspi, xfer->rx_dma, xfer->len);
+ }
+ if(xfer->tx_buf != NULL){
+ sspi->tx_done = BUSY;
+ s3c2410_dma_config(sspi->tx_dmach, sspi->cur_bpw/8, 0);
+ s3c2410_dma_enqueue(sspi->tx_dmach, (void *)sspi, xfer->tx_dma, xfer->len);
+ }
+}
+
+static inline void enable_cs(struct samspi_bus *sspi, struct spi_device *spi)
+{
+ u32 val;
+ struct sam_spi_pdata *spd = (struct sam_spi_pdata *)spi->controller_data;
+
+ val = readl(sspi->regs + SAMSPI_SLAVE_SEL);
+
+ if(sspi->cur_mode & SPI_SLAVE){
+ val |= SPI_SLAVE_AUTO; /* Auto Mode */
+ val |= SPI_SLAVE_SIG_INACT;
+ }else{ /* Master Mode */
+ val &= ~SPI_SLAVE_AUTO; /* Manual Mode */
+ val &= ~SPI_SLAVE_SIG_INACT; /* Activate CS */
+ if(spi->mode & SPI_CS_HIGH){ // spd->cs_act_high){
+ spd->cs_set(spd->cs_pin, CS_HIGH);
+ spd->cs_level = CS_HIGH;
+ }else{
+ spd->cs_set(spd->cs_pin, CS_LOW);
+ spd->cs_level = CS_LOW;
+ }
+ }
+
+ writel(val, sspi->regs + SAMSPI_SLAVE_SEL);
+}
+
+static inline void disable_cs(struct samspi_bus *sspi, struct spi_device *spi)
+{
+ u32 val;
+ struct sam_spi_pdata *spd = (struct sam_spi_pdata *)spi->controller_data;
+
+ if(!(spi->mode & SPI_CS_HIGH) && spd->cs_act_high){
+ dbg_printk("%s:%s:%d Slave supports SPI_CS_HIGH, but not requested by Master!\n", __FILE__, __func__, __LINE__);
+ }
+
+ val = readl(sspi->regs + SAMSPI_SLAVE_SEL);
+
+ if(sspi->cur_mode & SPI_SLAVE){
+ val |= SPI_SLAVE_AUTO; /* Auto Mode */
+ }else{ /* Master Mode */
+ val &= ~SPI_SLAVE_AUTO; /* Manual Mode */
+ val |= SPI_SLAVE_SIG_INACT; /* DeActivate CS */
+ if(spi->mode & SPI_CS_HIGH){ // spd->cs_act_high){
+ spd->cs_set(spd->cs_pin, CS_LOW);
+ spd->cs_level = CS_LOW;
+ }else{
+ spd->cs_set(spd->cs_pin, CS_HIGH);
+ spd->cs_level = CS_HIGH;
+ }
+ }
+
+ writel(val, sspi->regs + SAMSPI_SLAVE_SEL);
+}
+
+static inline void set_polarity(struct samspi_bus *sspi)
+{
+ u32 val;
+
+ val = readl(sspi->regs + SAMSPI_CH_CFG);
+ val &= ~(SPI_CH_SLAVE | SPI_CPOL_L | SPI_CPHA_B);
+ if(sspi->cur_mode & SPI_SLAVE)
+ val |= SPI_CH_SLAVE;
+ if(!(sspi->cur_mode & SPI_CPOL))
+ val |= SPI_CPOL_L;
+ if(sspi->cur_mode & SPI_CPHA)
+ val |= SPI_CPHA_B;
+ writel(val, sspi->regs + SAMSPI_CH_CFG);
+}
+
+static inline void set_clock(struct samspi_bus *sspi)
+{
+ u32 val;
+
+ val = readl(sspi->regs + SAMSPI_CLK_CFG);
+ val &= ~(SPI_CLKSEL_SRCMSK | SPI_ENCLK_ENABLE | 0xff);
+ val |= SPI_CLKSEL_SRC;
+ if(!(sspi->cur_mode & SPI_SLAVE)){
+ val |= ((sspi_getrate(sspi) / sspi->cur_speed / 2 - 1) << 0); // PCLK and PSR
+ val |= SPI_ENCLK_ENABLE;
+ }
+ writel(val, sspi->regs + SAMSPI_CLK_CFG);
+}
+
+static inline void set_dmachan(struct samspi_bus *sspi)
+{
+ u32 val;
+
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~((0x3<<17) | (0x3<<29));
+ if(sspi->cur_bpw == 8){
+ val |= SPI_MODE_CH_TSZ_BYTE;
+ val |= SPI_MODE_BUS_TSZ_BYTE;
+ }else if(sspi->cur_bpw == 16){
+ val |= SPI_MODE_CH_TSZ_HALFWORD;
+ val |= SPI_MODE_BUS_TSZ_HALFWORD;
+ }else if(sspi->cur_bpw == 32){
+ val |= SPI_MODE_CH_TSZ_WORD;
+ val |= SPI_MODE_BUS_TSZ_WORD;
+ }else{
+ printk("Invalid Bits/Word!\n");
+ }
+ val &= ~(SPI_MODE_4BURST | SPI_MODE_TXDMA_ON | SPI_MODE_RXDMA_ON);
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+}
+
+static void config_sspi(struct samspi_bus *sspi)
+{
+ /* Set Polarity and Phase */
+ set_polarity(sspi);
+
+ /* Set Channel & DMA Mode */
+ set_dmachan(sspi);
+}
+
+static void samspi_hwinit(struct samspi_bus *sspi, int channel)
+{
+ unsigned int val;
+
+ writel(SPI_SLAVE_SIG_INACT, sspi->regs + SAMSPI_SLAVE_SEL);
+
+ /* Disable Interrupts */
+ writel(0, sspi->regs + SAMSPI_SPI_INT_EN);
+
+#ifdef CONFIG_CPU_S3C6410
+ writel((readl(S3C64XX_SPC_BASE) & ~(3<<28)) | (3<<28), S3C64XX_SPC_BASE);
+ writel((readl(S3C64XX_SPC_BASE) & ~(3<<18)) | (3<<18), S3C64XX_SPC_BASE);
+#elif defined (CONFIG_CPU_S5P6440)
+ writel((readl(S5P64XX_SPC_BASE) & ~(3<<28)) | (3<<28), S5P64XX_SPC_BASE);
+ writel((readl(S5P64XX_SPC_BASE) & ~(3<<18)) | (3<<18), S5P64XX_SPC_BASE);
+#elif defined (CONFIG_CPU_S5P6440)
+ /* How to control drive strength, if we must? */
+#endif
+
+ writel(SPI_CLKSEL_SRC, sspi->regs + SAMSPI_CLK_CFG);
+ writel(0, sspi->regs + SAMSPI_MODE_CFG);
+ writel(SPI_SLAVE_SIG_INACT, sspi->regs + SAMSPI_SLAVE_SEL);
+ writel(0, sspi->regs + SAMSPI_PACKET_CNT);
+ writel(readl(sspi->regs + SAMSPI_PENDING_CLR), sspi->regs + SAMSPI_PENDING_CLR);
+ writel(SPI_FBCLK_0NS, sspi->regs + SAMSPI_FB_CLK);
+
+ flush_spi(sspi);
+
+ writel(0, sspi->regs + SAMSPI_SWAP_CFG);
+ writel(SPI_FBCLK_9NS, sspi->regs + SAMSPI_FB_CLK);
+
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~(SPI_MAX_TRAILCNT << SPI_TRAILCNT_OFF);
+ if(channel == 0)
+ SET_MODECFG(val, 0);
+ else
+ SET_MODECFG(val, 1);
+ val |= (SPI_TRAILCNT << SPI_TRAILCNT_OFF);
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+}
+
+static irqreturn_t samspi_interrupt(int irq, void *dev_id)
+{
+ u32 val;
+ struct samspi_bus *sspi = (struct samspi_bus *)dev_id;
+
+ dump_regs(sspi);
+ val = readl(sspi->regs + SAMSPI_PENDING_CLR);
+ dbg_printk("PENDING=%x\n", val);
+ writel(val, sspi->regs + SAMSPI_PENDING_CLR);
+
+ /* We get interrupted only for bad news */
+ if(sspi->tx_done != PASS){
+ printk(KERN_CRIT "TX FAILED \n");
+ sspi->tx_done = FAIL;
+ }
+ if(sspi->rx_done != PASS){
+ printk(KERN_CRIT "RX FAILED \n");
+ sspi->rx_done = FAIL;
+ }
+ sspi->state = STOPPED;
+ complete(&sspi->xfer_completion);
+
+ return IRQ_HANDLED;
+}
+
+void samspi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, int size, enum s3c2410_dma_buffresult res)
+{
+ struct samspi_bus *sspi = (struct samspi_bus *)buf_id;
+
+ if(res == S3C2410_RES_OK){
+ sspi->rx_done = PASS;
+ dbg_printk("DmaRx-%d ", size);
+ }else{
+ sspi->rx_done = FAIL;
+ dbg_printk("DmaAbrtRx-%d ", size);
+ }
+
+ if(sspi->tx_done != BUSY && sspi->state != STOPPED) /* If other done and all OK */
+ complete(&sspi->xfer_completion);
+}
+
+void samspi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id, int size, enum s3c2410_dma_buffresult res)
+{
+ struct samspi_bus *sspi = (struct samspi_bus *)buf_id;
+
+ if(res == S3C2410_RES_OK){
+ sspi->tx_done = PASS;
+ dbg_printk("DmaTx-%d ", size);
+ }else{
+ sspi->tx_done = FAIL;
+ dbg_printk("DmaAbrtTx-%d ", size);
+ }
+
+ if(sspi->rx_done != BUSY && sspi->state != STOPPED) /* If other done and all OK */
+ complete(&sspi->xfer_completion);
+}
+
+static int wait_for_txshiftout(struct samspi_bus *sspi, unsigned long t)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + t;
+ while((__raw_readl(sspi->regs + SAMSPI_SPI_STATUS) >> 6) & 0x7f){
+ if(time_after(jiffies, timeout))
+ return -1;
+ cpu_relax();
+ }
+ return 0;
+}
+
+static int wait_for_xfer(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ int status;
+ u32 val;
+
+ val = msecs_to_jiffies(xfer->len / (sspi->min_speed / 8 / 1000)); /* time to xfer data at min. speed */
+ if(sspi->cur_mode & SPI_SLAVE)
+ val += msecs_to_jiffies(5000); /* 5secs to switch on the Master */
+ else
+ val += msecs_to_jiffies(10); /* just some more */
+ status = wait_for_completion_interruptible_timeout(&sspi->xfer_completion, val);
+
+ //printk(KERN_CRIT "Return from waitforcompletion = %d \n", status);
+
+ if(status == 0)
+ status = -ETIMEDOUT;
+ else if(status == -ERESTARTSYS)
+ status = -EINTR;
+ else if((sspi->tx_done != PASS) || (sspi->rx_done != PASS)) /* Some Xfer failed */
+ status = -EIO;
+ else
+ status = 0; /* All OK */
+
+ /* When TxLen <= SPI-FifoLen in Slave mode, DMA returns naively */
+ if(!status && (sspi->cur_mode & SPI_SLAVE) && (xfer->tx_buf != NULL)){
+ val = msecs_to_jiffies(xfer->len / (sspi->min_speed / 8 / 1000)); /* Be lenient */
+ val += msecs_to_jiffies(5000); /* 5secs to switch on the Master */
+ status = wait_for_txshiftout(sspi, val);
+ if(status == -1)
+ status = -ETIMEDOUT;
+ else
+ status = 0;
+ }
+
+ return status;
+}
+
+#define INVALID_DMA_ADDRESS 0xffffffff
+/* First, try to map buf onto phys addr as such.
+ * If xfer->r/tx_buf was not on contiguous memory,
+ * allocate from our preallocated DMA buffer.
+ */
+static int samspi_map_xfer(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ struct device *dev = &sspi->pdev->dev;
+
+ sspi->rx_tmp = NULL;
+ sspi->tx_tmp = NULL;
+
+ xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
+ if(xfer->tx_buf != NULL){
+ xfer->tx_dma = dma_map_single(dev,
+ (void *) xfer->tx_buf, xfer->len,
+ DMA_TO_DEVICE);
+ if(dma_mapping_error(dev, xfer->tx_dma))
+ goto alloc_from_buffer;
+ }
+ if(xfer->rx_buf != NULL){
+ xfer->rx_dma = dma_map_single(dev,
+ xfer->rx_buf, xfer->len,
+ DMA_FROM_DEVICE);
+ if(dma_mapping_error(dev, xfer->rx_dma)){
+ if(xfer->tx_buf)
+ dma_unmap_single(dev,
+ xfer->tx_dma, xfer->len,
+ DMA_TO_DEVICE);
+ goto alloc_from_buffer;
+ }
+ }
+ return 0;
+
+alloc_from_buffer: /* If the xfer->[r/t]x_buf was not on contiguous memory */
+
+ printk(KERN_CRIT "############ Allocating from buffer...\n");
+
+ if(xfer->len <= SAMSPI_DMABUF_LEN){
+ if(xfer->rx_buf != NULL){
+ xfer->rx_dma = sspi->rx_dma_phys;
+ sspi->rx_tmp = (void *)sspi->rx_dma_cpu;
+ }
+ if(xfer->tx_buf != NULL){
+ xfer->tx_dma = sspi->tx_dma_phys;
+ sspi->tx_tmp = (void *)sspi->tx_dma_cpu;
+ }
+ }else{
+ dbg_printk("If you plan to use this Xfer size often, increase SAMSPI_DMABUF_LEN\n");
+ if(xfer->rx_buf != NULL){
+ sspi->rx_tmp = dma_alloc_coherent(&sspi->pdev->dev, SAMSPI_DMABUF_LEN,
+ &xfer->rx_dma, GFP_KERNEL | GFP_DMA);
+ if(sspi->rx_tmp == NULL)
+ return -ENOMEM;
+ }
+ if(xfer->tx_buf != NULL){
+ sspi->tx_tmp = dma_alloc_coherent(&sspi->pdev->dev,
+ SAMSPI_DMABUF_LEN, &xfer->tx_dma, GFP_KERNEL | GFP_DMA);
+ if(sspi->tx_tmp == NULL){
+ if(xfer->rx_buf != NULL)
+ dma_free_coherent(&sspi->pdev->dev,
+ SAMSPI_DMABUF_LEN, sspi->rx_tmp, xfer->rx_dma);
+ return -ENOMEM;
+ }
+ }
+ }
+
+ if(xfer->tx_buf != NULL)
+ memcpy(sspi->tx_tmp, xfer->tx_buf, xfer->len);
+
+ return 0;
+}
+
+static void samspi_unmap_xfer(struct samspi_bus *sspi, struct spi_transfer *xfer)
+{
+ if((sspi->rx_tmp == NULL) && (sspi->tx_tmp == NULL)) /* if map_single'd */
+ return;
+
+ if((xfer->rx_buf != NULL) && (sspi->rx_tmp != NULL))
+ memcpy(xfer->rx_buf, sspi->rx_tmp, xfer->len);
+
+ if(xfer->len > SAMSPI_DMABUF_LEN){
+ if(xfer->rx_buf != NULL)
+ dma_free_coherent(&sspi->pdev->dev, SAMSPI_DMABUF_LEN, sspi->rx_tmp, xfer->rx_dma);
+ if(xfer->tx_buf != NULL)
+ dma_free_coherent(&sspi->pdev->dev, SAMSPI_DMABUF_LEN, sspi->tx_tmp, xfer->tx_dma);
+ }else{
+ sspi->rx_tmp = NULL;
+ sspi->tx_tmp = NULL;
+ }
+}
+
+static void handle_msg(struct samspi_bus *sspi, struct spi_message *msg)
+{
+ u8 bpw;
+ u32 speed, val;
+ int status = 0;
+ struct spi_transfer *xfer;
+ struct spi_device *spi = msg->spi;
+
+ config_sspi(sspi);
+
+ list_for_each_entry (xfer, &msg->transfers, transfer_list) {
+
+ if(!msg->is_dma_mapped && samspi_map_xfer(sspi, xfer)){
+ dev_err(&spi->dev, "Xfer: Unable to allocate DMA buffer!\n");
+ status = -ENOMEM;
+ goto out;
+ }
+
+ INIT_COMPLETION(sspi->xfer_completion);
+
+ /* Only BPW and Speed may change across transfers */
+ bpw = xfer->bits_per_word ? : spi->bits_per_word;
+ speed = xfer->speed_hz ? : spi->max_speed_hz;
+
+ if(sspi->cur_bpw != bpw || sspi->cur_speed != speed){
+ sspi->cur_bpw = bpw;
+ sspi->cur_speed = speed;
+ config_sspi(sspi);
+ }
+
+ /* Pending only which is to be done */
+ sspi->rx_done = PASS;
+ sspi->tx_done = PASS;
+ sspi->state = RUNNING;
+
+ /* Configure Clock */
+ set_clock(sspi);
+
+ /* Enable Interrupts */
+ enable_spiintr(sspi, xfer);
+
+ if(!(sspi->cur_mode & SPI_SLAVE))
+ flush_spi(sspi);
+
+ /* Enqueue data on DMA */
+ enable_spienqueue(sspi, xfer);
+
+ /* Enable DMA */
+ enable_spidma(sspi, xfer);
+
+ /* Enable TX/RX */
+ enable_spichan(sspi, xfer);
+
+ /* Slave Select */
+ enable_cs(sspi, spi);
+
+ status = wait_for_xfer(sspi, xfer);
+
+ /**************
+ * Block Here *
+ **************/
+
+ if(status == -ETIMEDOUT){
+ dev_err(&spi->dev, "Xfer: Timeout!\n");
+ dump_regs(sspi);
+ sspi->state = STOPPED;
+ /* DMA Disable*/
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~(SPI_MODE_TXDMA_ON | SPI_MODE_RXDMA_ON);
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+ flush_dma(sspi, xfer);
+ flush_spi(sspi);
+ if(!msg->is_dma_mapped)
+ samspi_unmap_xfer(sspi, xfer);
+ goto out;
+ }
+ if(status == -EINTR){
+ dev_err(&spi->dev, "Xfer: Interrupted!\n");
+ dump_regs(sspi);
+ sspi->state = STOPPED;
+ /* DMA Disable*/
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~(SPI_MODE_TXDMA_ON | SPI_MODE_RXDMA_ON);
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+ flush_dma(sspi, xfer);
+ flush_spi(sspi);
+ if(!msg->is_dma_mapped)
+ samspi_unmap_xfer(sspi, xfer);
+ goto out;
+ }
+ if(status == -EIO){ /* Some Xfer failed */
+ dev_err(&spi->dev, "Xfer: Failed!\n");
+ dump_regs(sspi);
+ sspi->state = STOPPED;
+ /* DMA Disable*/
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~(SPI_MODE_TXDMA_ON | SPI_MODE_RXDMA_ON);
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+ flush_dma(sspi, xfer);
+ flush_spi(sspi);
+ if(!msg->is_dma_mapped)
+ samspi_unmap_xfer(sspi, xfer);
+ goto out;
+ }
+
+ if(xfer->delay_usecs){
+ udelay(xfer->delay_usecs);
+ dbg_printk("%s:%s:%d Unverified Control Flow Path!\n", __FILE__, __func__, __LINE__);
+ }
+
+ if(xfer->cs_change && !(sspi->cur_mode & SPI_SLAVE)){
+ disable_cs(sspi, spi);
+ dbg_printk("%s:%s:%d Unverified Control Flow Path!\n", __FILE__, __func__, __LINE__);
+ }
+
+ msg->actual_length += xfer->len;
+
+ if(!msg->is_dma_mapped)
+ samspi_unmap_xfer(sspi, xfer);
+
+ }
+
+out:
+ /* Slave Deselect */
+ if(!(sspi->cur_mode & SPI_SLAVE))
+ disable_cs(sspi, spi);
+
+ /* Disable Interrupts */
+ writel(0, sspi->regs + SAMSPI_SPI_INT_EN);
+
+ /* Tx/Rx Disable */
+ val = readl(sspi->regs + SAMSPI_CH_CFG);
+ val &= ~(SPI_CH_RXCH_ON | SPI_CH_TXCH_ON);
+ writel(val, sspi->regs + SAMSPI_CH_CFG);
+
+ /* DMA Disable*/
+ val = readl(sspi->regs + SAMSPI_MODE_CFG);
+ val &= ~(SPI_MODE_TXDMA_ON | SPI_MODE_RXDMA_ON);
+ writel(val, sspi->regs + SAMSPI_MODE_CFG);
+
+ msg->status = status;
+ if(msg->complete)
+ msg->complete(msg->context);
+}
+
+static void samspi_work(struct work_struct *work)
+{
+ struct samspi_bus *sspi = container_of(work, struct samspi_bus, work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sspi->lock, flags);
+ while (!list_empty(&sspi->queue)) {
+ struct spi_message *msg;
+
+ msg = container_of(sspi->queue.next, struct spi_message, queue);
+ list_del_init(&msg->queue);
+ spin_unlock_irqrestore(&sspi->lock, flags);
+
+ handle_msg(sspi, msg);
+
+ spin_lock_irqsave(&sspi->lock, flags);
+ }
+ spin_unlock_irqrestore(&sspi->lock, flags);
+}
+
+static void samspi_cleanup(struct spi_device *spi)
+{
+ dbg_printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
+}
+
+static int samspi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct spi_master *master = spi->master;
+ struct samspi_bus *sspi = spi_master_get_devdata(master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sspi->lock, flags);
+ msg->actual_length = 0;
+ list_add_tail(&msg->queue, &sspi->queue);
+ queue_work(sspi->workqueue, &sspi->work);
+ spin_unlock_irqrestore(&sspi->lock, flags);
+
+ return 0;
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_SLAVE | (spd->cs_act_high ? SPI_CS_HIGH : 0) )
+/*
+ * Here we only check the validity of requested configuration and
+ * save the configuration in a local data-structure.
+ * The controller is actually configured only just before
+ * we get a message to transfer _and_ if no other message is pending(already configured).
+ */
+static int samspi_setup(struct spi_device *spi)
+{
+ unsigned long flags;
+ unsigned int psr;
+ struct samspi_bus *sspi = spi_master_get_devdata(spi->master);
+ struct sam_spi_pdata *spd = (struct sam_spi_pdata *)spi->controller_data;
+
+ spin_lock_irqsave(&sspi->lock, flags);
+ if(!list_empty(&sspi->queue)){ /* Any pending message? */
+ spin_unlock_irqrestore(&sspi->lock, flags);
+ dev_dbg(&spi->dev, "setup: attempt while messages in queue!\n");
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&sspi->lock, flags);
+
+ if (spi->chip_select > spi->master->num_chipselect) {
+ dev_dbg(&spi->dev, "setup: invalid chipselect %u (%u defined)\n",
+ spi->chip_select, spi->master->num_chipselect);
+ return -EINVAL;
+ }
+
+ spi->bits_per_word = spi->bits_per_word ? : 8;
+
+ if((spi->bits_per_word != 8) &&
+ (spi->bits_per_word != 16) &&
+ (spi->bits_per_word != 32)){
+ dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n", spi->bits_per_word);
+ return -EINVAL;
+ }
+
+ spi->max_speed_hz = spi->max_speed_hz ? : sspi->max_speed;
+
+ /* Round-off max_speed_hz */
+ psr = sspi_getrate(sspi) / spi->max_speed_hz / 2 - 1;
+ psr &= 0xff;
+ if(spi->max_speed_hz < sspi_getrate(sspi) / 2 / (psr + 1))
+ psr = (psr+1) & 0xff;
+
+ spi->max_speed_hz = sspi_getrate(sspi) / 2 / (psr + 1);
+
+ if (spi->max_speed_hz > sspi->max_speed
+ || spi->max_speed_hz < sspi->min_speed){
+ dev_err(&spi->dev, "setup: req speed(%u) out of range[%u-%u]\n",
+ spi->max_speed_hz, sspi->min_speed, sspi->max_speed);
+ return -EINVAL;
+ }
+
+ if (spi->mode & ~MODEBITS) {
+ dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", spi->mode & ~MODEBITS);
+ return -EINVAL;
+ }
+
+ if(!(spi->mode & SPI_SLAVE) && (spd->cs_level == CS_FLOAT)){
+ spd->cs_config(spd->cs_pin, spd->cs_mode, (spi->mode & SPI_CS_HIGH)/*spd->cs_act_high*/ ? CS_LOW : CS_HIGH);
+ disable_cs(sspi, spi);
+ }
+
+ if((sspi->cur_bpw == spi->bits_per_word) &&
+ (sspi->cur_speed == spi->max_speed_hz) &&
+ (sspi->cur_mode == spi->mode)) /* If no change in configuration, do nothing */
+ return 0;
+
+ sspi->cur_bpw = spi->bits_per_word;
+ sspi->cur_speed = spi->max_speed_hz;
+ sspi->cur_mode = spi->mode;
+
+ SAM_SETGPIOPULL(sspi);
+ return 0;
+}
+
+static int __init samspi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct samspi_bus *sspi;
+ int ret = -ENODEV;
+
+ dbg_printk("%s:%s:%d ID=%d\n", __FILE__, __func__, __LINE__, pdev->id);
+ master = spi_alloc_master(&pdev->dev, sizeof(struct samspi_bus)); /* Allocate contiguous SPI controller */
+ if (master == NULL)
+ return ret;
+ sspi = spi_master_get_devdata(master);
+ sspi->pdev = pdev;
+ sspi->spi_mstinfo = (struct sam_spi_mstr_info *)pdev->dev.platform_data;
+ sspi->master = master;
+ platform_set_drvdata(pdev, master);
+
+ INIT_WORK(&sspi->work, samspi_work);
+ spin_lock_init(&sspi->lock);
+ INIT_LIST_HEAD(&sspi->queue);
+ init_completion(&sspi->xfer_completion);
+
+ ret = sspi_getclcks(sspi);
+ if(ret){
+ dev_err(&pdev->dev, "cannot acquire clock \n");
+ ret = -EBUSY;
+ goto lb1;
+ }
+ ret = sspi_enclcks(sspi);
+ if(ret){
+ dev_err(&pdev->dev, "cannot enable clock \n");
+ ret = -EBUSY;
+ goto lb2;
+ }
+
+ sspi->max_speed = sspi_getrate(sspi) / 2 / (0x0 + 1);
+ sspi->min_speed = sspi_getrate(sspi) / 2 / (0xff + 1);
+
+ sspi->cur_bpw = 8;
+ sspi->cur_mode = SPI_SLAVE; /* Start in Slave mode */
+ sspi->cur_speed = sspi->min_speed;
+
+ /* Get and Map Resources */
+ sspi->iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (sspi->iores == NULL) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ ret = -ENOENT;
+ goto lb3;
+ }
+
+ sspi->ioarea = request_mem_region(sspi->iores->start, sspi->iores->end - sspi->iores->start + 1, pdev->name);
+ if (sspi->ioarea == NULL) {
+ dev_err(&pdev->dev, "cannot request IO\n");
+ ret = -ENXIO;
+ goto lb4;
+ }
+
+ sspi->regs = ioremap(sspi->iores->start, sspi->iores->end - sspi->iores->start + 1);
+ if (sspi->regs == NULL) {
+ dev_err(&pdev->dev, "cannot map IO\n");
+ ret = -ENXIO;
+ goto lb5;
+ }
+
+ sspi->tx_dma_cpu = dma_alloc_coherent(&pdev->dev, SAMSPI_DMABUF_LEN, &sspi->tx_dma_phys, GFP_KERNEL | GFP_DMA);
+ if(sspi->tx_dma_cpu == NULL){
+ dev_err(&pdev->dev, "Unable to allocate TX DMA buffers\n");
+ ret = -ENOMEM;
+ goto lb6;
+ }
+
+ sspi->rx_dma_cpu = dma_alloc_coherent(&pdev->dev, SAMSPI_DMABUF_LEN, &sspi->rx_dma_phys, GFP_KERNEL | GFP_DMA);
+ if(sspi->rx_dma_cpu == NULL){
+ dev_err(&pdev->dev, "Unable to allocate RX DMA buffers\n");
+ ret = -ENOMEM;
+ goto lb7;
+ }
+
+ sspi->irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if(sspi->irqres == NULL){
+ dev_err(&pdev->dev, "cannot find IRQ\n");
+ ret = -ENOENT;
+ goto lb8;
+ }
+
+ ret = request_irq(sspi->irqres->start, samspi_interrupt, IRQF_DISABLED,
+ pdev->name, sspi);
+ if(ret){
+ dev_err(&pdev->dev, "cannot acquire IRQ\n");
+ ret = -EBUSY;
+ goto lb9;
+ }
+
+ sspi->workqueue = create_singlethread_workqueue(master->dev.parent->bus_id);
+ if(!sspi->workqueue){
+ dev_err(&pdev->dev, "cannot create workqueue\n");
+ ret = -EBUSY;
+ goto lb10;
+ }
+
+ master->bus_num = pdev->id;
+ master->setup = samspi_setup;
+ master->transfer = samspi_transfer;
+ master->cleanup = samspi_cleanup;
+ master->num_chipselect = sspi->spi_mstinfo->num_slaves;
+
+ if(spi_register_master(master)){
+ dev_err(&pdev->dev, "cannot register SPI master\n");
+ ret = -EBUSY;
+ goto lb11;
+ }
+
+ /* Configure GPIOs */
+ if(pdev->id == 0)
+ SETUP_SPI(sspi, 0);
+ else if(pdev->id == 1)
+ SETUP_SPI(sspi, 1);
+ SAM_SETGPIOPULL(sspi);
+
+ if(s3c2410_dma_request(sspi->rx_dmach, &samspi_dma_client, NULL)){
+ dev_err(&pdev->dev, "cannot get RxDMA\n");
+ ret = -EBUSY;
+ goto lb12;
+ }
+ s3c2410_dma_set_buffdone_fn(sspi->rx_dmach, samspi_dma_rxcb);
+ s3c2410_dma_devconfig(sspi->rx_dmach, S3C2410_DMASRC_HW, 0, sspi->sfr_phyaddr + SAMSPI_SPI_RX_DATA);
+ s3c2410_dma_config(sspi->rx_dmach, sspi->cur_bpw/8, 0);
+ s3c2410_dma_setflags(sspi->rx_dmach, S3C2410_DMAF_AUTOSTART);
+
+ if(s3c2410_dma_request(sspi->tx_dmach, &samspi_dma_client, NULL)){
+ dev_err(&pdev->dev, "cannot get TxDMA\n");
+ ret = -EBUSY;
+ goto lb13;
+ }
+ s3c2410_dma_set_buffdone_fn(sspi->tx_dmach, samspi_dma_txcb);
+ s3c2410_dma_devconfig(sspi->tx_dmach, S3C2410_DMASRC_MEM, 0, sspi->sfr_phyaddr + SAMSPI_SPI_TX_DATA);
+ s3c2410_dma_config(sspi->tx_dmach, sspi->cur_bpw/8, 0);
+ s3c2410_dma_setflags(sspi->tx_dmach, S3C2410_DMAF_AUTOSTART);
+
+ /* Setup Deufult Mode */
+ samspi_hwinit(sspi, pdev->id);
+
+ printk("Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", pdev->id, master->num_chipselect);
+ printk("\tMax,Min-Speed [%d, %d]Hz\n", sspi->max_speed, sspi->min_speed);
+ printk("\tIrq=%d\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
+ sspi->irqres->start,
+ sspi->iores->end, sspi->iores->start,
+ sspi->rx_dmach, sspi->tx_dmach);
+
+ return 0;
+
+lb13:
+ s3c2410_dma_free(sspi->rx_dmach, &samspi_dma_client);
+lb12:
+ spi_unregister_master(master);
+lb11:
+ destroy_workqueue(sspi->workqueue);
+lb10:
+ free_irq(sspi->irqres->start, sspi);
+lb9:
+lb8:
+ dma_free_coherent(&pdev->dev, SAMSPI_DMABUF_LEN, sspi->rx_dma_cpu, sspi->rx_dma_phys);
+lb7:
+ dma_free_coherent(&pdev->dev, SAMSPI_DMABUF_LEN, sspi->tx_dma_cpu, sspi->tx_dma_phys);
+lb6:
+ iounmap((void *) sspi->regs);
+lb5:
+ release_mem_region(sspi->iores->start, sspi->iores->end - sspi->iores->start + 1);
+lb4:
+lb3:
+ sspi_disclcks(sspi);
+lb2:
+ sspi_putclcks(sspi);
+lb1:
+ platform_set_drvdata(pdev, NULL);
+ spi_master_put(master);
+
+ return ret;
+}
+
+static int __exit samspi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+ struct samspi_bus *sspi = spi_master_get_devdata(master);
+
+ s3c2410_dma_free(sspi->tx_dmach, &samspi_dma_client);
+ s3c2410_dma_free(sspi->rx_dmach, &samspi_dma_client);
+ spi_unregister_master(master);
+ destroy_workqueue(sspi->workqueue);
+ free_irq(sspi->irqres->start, sspi);
+ dma_free_coherent(&pdev->dev, SAMSPI_DMABUF_LEN, sspi->rx_dma_cpu, sspi->rx_dma_phys);
+ dma_free_coherent(&pdev->dev, SAMSPI_DMABUF_LEN, sspi->tx_dma_cpu, sspi->tx_dma_phys);
+ iounmap((void *) sspi->regs);
+ release_mem_region(sspi->iores->start, sspi->iores->end - sspi->iores->start + 1);
+ sspi_disclcks(sspi);
+ sspi_putclcks(sspi);
+ platform_set_drvdata(pdev, NULL);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver sam_spi_driver = {
+ .driver = {
+ .name = "sam-spi",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+// .remove = sam_spi_remove,
+// .shutdown = sam_spi_shutdown,
+// .suspend = sam_spi_suspend,
+// .resume = sam_spi_resume,
+};
+
+static int __init sam_spi_init(void)
+{
+ dbg_printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return platform_driver_probe(&sam_spi_driver, samspi_probe);
+}
+//module_init(sam_spi_init);
+subsys_initcall(sam_spi_init);
+
+static void __exit sam_spi_exit(void)
+{
+ platform_driver_unregister(&sam_spi_driver);
+}
+module_exit(sam_spi_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jaswinder Singh Brar <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("Samsung SOC SPI Controller");
diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c
new file mode 100644
index 000000000000..060cd894a267
--- /dev/null
+++ b/drivers/video/backlight/da9052_bl.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * da9052_bl.c: Backlight driver for DA9052
+ */
+
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include <linux/delay.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/bl.h>
+
+
+#define DRIVER_NAME "da9052-backlight"
+#define DRIVER_NAME1 "WLED-1"
+#define DRIVER_NAME2 "WLED-2"
+#define DRIVER_NAME3 "WLED-3"
+
+/* These flags define if Backlight LEDs are present */
+/* Set the following macros to 1, if LEDs are present. Otherwise set to 0 */
+#define DA9052_LED1_PRESENT 1
+#define DA9052_LED2_PRESENT 1
+#define DA9052_LED3_PRESENT 1
+
+#define DA9052_MAX_BRIGHTNESS 0xFF
+
+struct da9052_backlight_data {
+ struct device *da9052_dev;
+ int current_brightness;
+ struct da9052 *da9052;
+
+ int is_led1_present;
+ int is_led2_present;
+ int is_led3_present;
+};
+
+enum da9052_led_number {
+ LED1 = 1,
+ LED2,
+ LED3,
+};
+
+static int da9052_backlight_brightness_set(struct da9052_backlight_data *data,
+ int brightness, enum da9052_led_number led)
+{
+ /*
+ * Mechanism for brightness control:
+ * For brightness control, current is used.
+ * PWM feature is not used.
+ * To use PWM feature, a fixed value of current should be defined.
+ */
+
+ int ret = 0;
+ unsigned int led_ramp_bit;
+ unsigned int led_current_register;
+ unsigned int led_current_sink_bit;
+ unsigned int led_boost_en_bit;
+ struct da9052_ssc_msg msg;
+
+ switch (led) {
+ case LED1:
+ led_ramp_bit = DA9052_LEDCONT_LED1RAMP;
+ led_current_register = DA9052_LED1CONF_REG;
+ led_current_sink_bit = DA9052_LEDCONT_LED1EN;
+ led_boost_en_bit = DA9052_BOOST_LED1INEN;
+ break;
+ case LED2:
+ led_ramp_bit = DA9052_LEDCONT_LED2RAMP;
+ led_current_register = DA9052_LED2CONF_REG;
+ led_current_sink_bit = DA9052_LEDCONT_LED2EN;
+ led_boost_en_bit = DA9052_BOOST_LED2INEN;
+ break;
+ case LED3:
+ led_ramp_bit = DA9052_LEDCONT_LED3RAMP;
+ led_current_register = DA9052_LED3CONF_REG;
+ led_current_sink_bit = DA9052_LEDCONT_LED3EN;
+ led_boost_en_bit = DA9052_BOOST_LED3INEN;
+ break;
+ default:
+ return -EIO;
+ }
+
+ /*
+ 1. Configure the boost register
+ 2. Configure the LED _CONT register
+ 3. Configure the LEDx_CONF registers to the brightness value.
+ */
+ msg.addr = DA9052_BOOST_REG;
+ msg.data = 0x3F;
+ if (brightness) {
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ if (ret) {
+ da9052_unlock(data->da9052);
+ return ret;
+ }
+ da9052_unlock(data->da9052);
+ }
+
+ msg.addr = DA9052_LEDCONT_REG;
+ msg.data = 0xFF;
+ if (brightness) {
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ if (ret) {
+ da9052_unlock(data->da9052);
+ return ret;
+ }
+ da9052_unlock(data->da9052);
+ }
+
+ msg.addr = led_current_register;
+ msg.data = 0;
+ /* Write to the DA9052 register */
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ if (ret) {
+ da9052_unlock(data->da9052);
+ return ret;
+ }
+ da9052_unlock(data->da9052);
+ msleep(20);
+ msg.data = brightness;
+ /* Write to the DA9052 register */
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ if (ret) {
+ da9052_unlock(data->da9052);
+ return ret;
+ }
+ da9052_unlock(data->da9052);
+
+ return 0;
+}
+
+static int da9052_backlight_set(struct backlight_device *bl, int brightness)
+{
+ struct da9052_backlight_data *data = bl_get_data(bl);
+ int ret = 0;
+ /* Check for LED1 */
+ if (1 == data->is_led1_present) {
+ ret = da9052_backlight_brightness_set(data, brightness, LED1);
+ if (ret)
+ return ret;
+ }
+ /* Check for LED2 */
+ if (1 == data->is_led2_present) {
+ ret = da9052_backlight_brightness_set(data, brightness, LED2);
+ if (ret)
+ return ret;
+ }
+ /* Check for LED3 */
+ if (1 == data->is_led3_present) {
+ ret = da9052_backlight_brightness_set(data, brightness, LED3);
+ if (ret)
+ return ret;
+ }
+
+ data->current_brightness = brightness;
+ return 0;
+}
+
+static int da9052_init_WLED(struct da9052_backlight_data *data,
+ enum da9052_led_number led)
+{
+ int ret = 0;
+ unsigned int led_current_register;
+ struct da9052_ssc_msg msg;
+
+ switch (led) {
+ case LED1:
+ led_current_register = DA9052_LED1CONF_REG;
+ break;
+ case LED2:
+ led_current_register = DA9052_LED2CONF_REG;
+ break;
+ case LED3:
+ led_current_register = DA9052_LED3CONF_REG;
+ break;
+ default:
+ return -EIO;
+ }
+
+ msg.addr = DA9052_BOOST_REG;
+ msg.data = 0x00;
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ if (ret) {
+ da9052_unlock(data->da9052);
+ return ret;
+ }
+ da9052_unlock(data->da9052);
+
+ msg.addr = DA9052_LEDCONT_REG;
+ msg.data = 0x00;
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ if (ret) {
+ da9052_unlock(data->da9052);
+ return ret;
+ }
+ da9052_unlock(data->da9052);
+
+ msg.addr = led_current_register;
+ msg.data = 0;
+ da9052_lock(data->da9052);
+ ret = data->da9052->write(data->da9052, &msg);
+ da9052_unlock(data->da9052);
+ return ret;
+}
+
+static int da9052_backlight_update_status(struct backlight_device *bl)
+{
+ int brightness = bl->props.brightness;
+
+ if (bl->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+ return da9052_backlight_set(bl, brightness);
+}
+
+static int da9052_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct da9052_backlight_data *data = bl_get_data(bl);
+ return data->current_brightness;
+}
+
+struct backlight_ops da9052_backlight_ops = {
+ .update_status = da9052_backlight_update_status,
+ .get_brightness = da9052_backlight_get_brightness,
+};
+
+static int da9052_backlight_probe1(struct platform_device *pdev)
+{
+ struct da9052_backlight_data *data;
+ struct backlight_device *bl;
+ int ret = 0;
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ data->da9052_dev = pdev->dev.parent;
+ data->da9052 = da9052;
+ data->current_brightness = 0;
+ data->is_led1_present = DA9052_LED1_PRESENT;
+
+ /* Init the WLED-1 bank */
+ ret = da9052_init_WLED(data, LED1);
+ if (ret)
+ return ret;
+
+ bl = backlight_device_register(pdev->name, data->da9052_dev,
+ data, &da9052_backlight_ops);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ kfree(data);
+ return PTR_ERR(bl);
+ }
+
+ bl->props.max_brightness = DA9052_MAX_BRIGHTNESS;
+ bl->props.brightness = 0;
+
+ platform_set_drvdata(pdev, bl);
+ backlight_update_status(bl);
+
+ return 0;
+}
+static int da9052_backlight_probe2(struct platform_device *pdev)
+{
+ struct da9052_backlight_data *data;
+ struct backlight_device *bl;
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ int ret = 0;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ data->da9052_dev = pdev->dev.parent;
+ data->da9052 = da9052;
+ data->current_brightness = 0;
+ data->is_led2_present = DA9052_LED2_PRESENT;
+
+ /* Init the WLED-2 bank */
+ ret = da9052_init_WLED(data, LED2);
+ if (ret)
+ return ret;
+
+ bl = backlight_device_register(pdev->name, data->da9052_dev,
+ data, &da9052_backlight_ops);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ kfree(data);
+ return PTR_ERR(bl);
+ }
+
+ bl->props.max_brightness = DA9052_MAX_BRIGHTNESS;
+ bl->props.brightness = 0;
+
+ platform_set_drvdata(pdev, bl);
+ backlight_update_status(bl);
+
+ return 0;
+}
+static int da9052_backlight_probe3(struct platform_device *pdev)
+{
+ struct da9052_backlight_data *data;
+ struct backlight_device *bl;
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ int ret = 0;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ data->da9052_dev = pdev->dev.parent;
+ data->da9052 = da9052;
+ data->current_brightness = 0;
+ data->is_led3_present = DA9052_LED3_PRESENT;
+
+ /* Init the WLED-2 bank */
+ ret = da9052_init_WLED(data, LED3);
+ if (ret)
+ return ret;
+
+ bl = backlight_device_register(pdev->name, data->da9052_dev,
+ data, &da9052_backlight_ops);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ kfree(data);
+ return PTR_ERR(bl);
+ }
+
+ bl->props.max_brightness = DA9052_MAX_BRIGHTNESS;
+ bl->props.brightness = 0;
+
+ platform_set_drvdata(pdev, bl);
+
+ backlight_update_status(bl);
+ return 0;
+}
+
+static int da9052_backlight_remove1(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct da9052_backlight_data *data = bl_get_data(bl);
+ int ret = 0;
+
+ /* Switch off the WLED-1 */
+ ret = da9052_init_WLED(data, LED1);
+ if (ret)
+ return ret;
+
+ backlight_device_unregister(bl);
+ kfree(data);
+ return 0;
+}
+
+static int da9052_backlight_remove2(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct da9052_backlight_data *data = bl_get_data(bl);
+ int ret = 0;
+
+ /* Switch off the WLED-2 */
+ ret = da9052_init_WLED(data, LED2);
+ if (ret)
+ return ret;
+
+ backlight_device_unregister(bl);
+ kfree(data);
+ return 0;
+}
+static int da9052_backlight_remove3(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct da9052_backlight_data *data = bl_get_data(bl);
+ int ret;
+
+ /* Switch off the WLED-3 */
+ ret = da9052_init_WLED(data, LED3);
+ if (ret)
+ return ret;
+
+ backlight_device_unregister(bl);
+ kfree(data);
+ return 0;
+}
+
+static struct platform_driver da9052_backlight_driver1 = {
+ .driver = {
+ .name = DRIVER_NAME1,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_backlight_probe1,
+ .remove = da9052_backlight_remove1,
+};
+static struct platform_driver da9052_backlight_driver2 = {
+ .driver = {
+ .name = DRIVER_NAME2,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_backlight_probe2,
+ .remove = da9052_backlight_remove2,
+};
+static struct platform_driver da9052_backlight_driver3 = {
+ .driver = {
+ .name = DRIVER_NAME3,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_backlight_probe3,
+ .remove = da9052_backlight_remove3,
+};
+
+static int __init da9052_backlight_init(void)
+{
+ s32 ret;
+ ret = platform_driver_register(&da9052_backlight_driver1);
+ if (ret)
+ return ret;
+ ret = platform_driver_register(&da9052_backlight_driver2);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&da9052_backlight_driver3);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+module_init(da9052_backlight_init);
+
+static void __exit da9052_backlight_exit(void)
+{
+ platform_driver_unregister(&da9052_backlight_driver1);
+ platform_driver_unregister(&da9052_backlight_driver2);
+ platform_driver_unregister(&da9052_backlight_driver3);
+}
+module_exit(da9052_backlight_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Backlight driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
new file mode 100644
index 000000000000..11e1d75212c3
--- /dev/null
+++ b/drivers/watchdog/da9052_wdt.c
@@ -0,0 +1,542 @@
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/watchdog.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/wdt.h>
+
+#define DRIVER_NAME "da9052-wdt"
+
+#define DA9052_STROBING_FILTER_ENABLE 0x0001
+#define DA9052_STROBING_FILTER_DISABLE 0x0002
+#define DA9052_SET_STROBING_MODE_MANUAL 0x0004
+#define DA9052_SET_STROBING_MODE_AUTO 0x0008
+
+#define KERNEL_MODULE 0
+#define ENABLE 1
+#define DISABLE 0
+
+static u8 sm_strobe_filter_flag = DISABLE;
+static u8 sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
+static u32 sm_mon_interval = DA9052_ADC_TWDMIN_TIME;
+static u8 sm_str_req = DISABLE;
+static u8 da9052_sm_scale = DA9052_WDT_DISABLE;
+module_param(sm_strobe_filter_flag, byte, 0);
+MODULE_PARM_DESC(sm_strobe_filter_flag,
+ "DA9052 SM driver strobe filter flag default = DISABLE");
+
+module_param(sm_strobe_mode_flag, byte, 0);
+MODULE_PARM_DESC(sm_strobe_mode_flag,
+ "DA9052 SM driver watchdog strobing mode default\
+ = DA9052_STROBE_MANUAL");
+
+module_param(da9052_sm_scale, byte, 0);
+MODULE_PARM_DESC(da9052_sm_scale,
+ "DA9052 SM driver scaling value used to calculate the\
+ time for the strobing filter default = 0");
+
+module_param(sm_str_req, byte, 0);
+MODULE_PARM_DESC(sm_str_req,
+ "DA9052 SM driver strobe request flag default = DISABLE");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static struct timer_list *monitoring_timer;
+
+struct da9052_wdt {
+ struct platform_device *pdev;
+ struct da9052 *da9052;
+};
+static struct miscdevice da9052_wdt_miscdev;
+static unsigned long da9052_wdt_users;
+static int da9052_wdt_expect_close;
+
+static struct da9052_wdt *get_wdt_da9052(void)
+{
+ /*return dev_get_drvdata(da9052_wdt_miscdev.parent);*/
+ return platform_get_drvdata
+ (to_platform_device(da9052_wdt_miscdev.parent));
+}
+
+void start_strobing(struct work_struct *work)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+ struct da9052_wdt *wdt = get_wdt_da9052();
+
+
+ if (NULL == wdt) {
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+ return;
+ }
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ return;
+ }
+ da9052_unlock(wdt->da9052);
+
+ msg.data = (msg.data | DA9052_CONTROLD_WATCHDOG);
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ return;
+ }
+ da9052_unlock(wdt->da9052);
+
+ sm_str_req = DISABLE;
+
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+ return;
+}
+
+
+void timer_callback(void)
+{
+ if (((sm_strobe_mode_flag) &&
+ (sm_strobe_mode_flag == DA9052_STROBE_MANUAL)) ||
+ (sm_strobe_mode_flag == DA9052_STROBE_AUTO)) {
+ schedule_work(&strobing_action);
+ } else {
+ if (sm_strobe_mode_flag == DA9052_STROBE_MANUAL) {
+ mod_timer(monitoring_timer, jiffies +
+ sm_mon_interval);
+ }
+ }
+}
+
+static int da9052_sm_hw_init(struct da9052_wdt *wdt)
+{
+ /* Create timer structure */
+ monitoring_timer = kzalloc(sizeof(struct timer_list), GFP_KERNEL);
+ if (!monitoring_timer)
+ return -ENOMEM;
+
+ init_timer(monitoring_timer);
+ monitoring_timer->expires = jiffies + sm_mon_interval;
+ monitoring_timer->function = (void *)&timer_callback;
+
+ sm_strobe_filter_flag = DA9052_SM_STROBE_CONF;
+ sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
+
+ return 0;
+}
+
+static int da9052_sm_hw_deinit(struct da9052_wdt *wdt)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ if (monitoring_timer != NULL)
+ del_timer(monitoring_timer);
+ kfree(monitoring_timer);
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ return 0;
+ssc_err:
+ da9052_unlock(wdt->da9052);
+ return -EIO;
+}
+
+ s32 da9052_sm_set_strobing_filter(struct da9052_wdt *wdt,
+ u8 strobing_filter_state)
+ {
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ msg.data = (msg.data & DA9052_CONTROLD_TWDSCALE);
+
+ if (strobing_filter_state == ENABLE) {
+ sm_strobe_filter_flag = ENABLE;
+ if (DA9052_WDT_DISABLE == msg.data) {
+ sm_str_req = DISABLE;
+ del_timer(monitoring_timer);
+ return 0;
+ }
+ if (DA9052_SCALE_64X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X64_WINDOW);
+ else if (DA9052_SCALE_32X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X32_WINDOW);
+ else if (DA9052_SCALE_16X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X16_WINDOW);
+ else if (DA9052_SCALE_8X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X8_WINDOW);
+ else if (DA9052_SCALE_4X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X4_WINDOW);
+ else if (DA9052_SCALE_2X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X2_WINDOW);
+ else
+ sm_mon_interval = msecs_to_jiffies(DA9052_X1_WINDOW);
+
+ } else if (strobing_filter_state == DISABLE) {
+ sm_strobe_filter_flag = DISABLE;
+ sm_mon_interval = msecs_to_jiffies(DA9052_ADC_TWDMIN_TIME);
+ if (DA9052_WDT_DISABLE == msg.data) {
+ sm_str_req = DISABLE;
+ del_timer(monitoring_timer);
+ return 0;
+ }
+ } else {
+ return STROBING_FILTER_ERROR;
+ }
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+
+ return 0;
+ssc_err:
+ da9052_unlock(wdt->da9052);
+ return -EIO;
+}
+
+int da9052_sm_set_strobing_mode(u8 strobing_mode_state)
+{
+ if (strobing_mode_state == DA9052_STROBE_AUTO)
+ sm_strobe_mode_flag = DA9052_STROBE_AUTO;
+ else if (strobing_mode_state == DA9052_STROBE_MANUAL)
+ sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
+ else
+ return STROBING_MODE_ERROR;
+
+ return 0;
+}
+
+int da9052_sm_strobe_wdt(void)
+{
+ sm_str_req = ENABLE;
+ return 0;
+}
+
+ s32 da9052_sm_set_wdt(struct da9052_wdt *wdt, u8 wdt_scaling)
+{
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+
+ if (wdt_scaling > DA9052_SCALE_64X)
+ return INVALID_SCALING_VALUE;
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ if (!((DA9052_WDT_DISABLE == (msg.data & DA9052_CONTROLD_TWDSCALE)) &&
+ (DA9052_WDT_DISABLE == wdt_scaling))) {
+ msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
+ msg.addr = DA9052_CONTROLD_REG;
+
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ msleep(1);
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+
+ msg.data |= wdt_scaling;
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ sm_str_req = DISABLE;
+ if (DA9052_WDT_DISABLE == wdt_scaling) {
+ del_timer(monitoring_timer);
+ return 0;
+ }
+ if (sm_strobe_filter_flag == ENABLE) {
+ if (DA9052_SCALE_64X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X64_WINDOW);
+ } else if (DA9052_SCALE_32X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X32_WINDOW);
+ } else if (DA9052_SCALE_16X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X16_WINDOW);
+ } else if (DA9052_SCALE_8X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X8_WINDOW);
+ } else if (DA9052_SCALE_4X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X4_WINDOW);
+ } else if (DA9052_SCALE_2X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X2_WINDOW);
+ } else {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X1_WINDOW);
+ }
+ } else {
+ sm_mon_interval = msecs_to_jiffies(
+ DA9052_ADC_TWDMIN_TIME);
+ }
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+ }
+
+ return 0;
+ssc_err:
+ da9052_unlock(wdt->da9052);
+ return -EIO;
+}
+
+static int da9052_wdt_open(struct inode *inode, struct file *file)
+{
+ struct da9052_wdt *wdt = get_wdt_da9052();
+ int ret;
+ printk(KERN_INFO"IN WDT OPEN \n");
+
+ if (!wdt) {
+ printk(KERN_INFO"Returning no device\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO"IN WDT OPEN 1\n");
+
+ if (test_and_set_bit(0, &da9052_wdt_users))
+ return -EBUSY;
+
+ ret = da9052_sm_hw_init(wdt);
+ if (ret != 0) {
+ printk(KERN_ERR "Watchdog hw init failed\n");
+ return ret;
+ }
+
+ return nonseekable_open(inode, file);
+}
+
+static int da9052_wdt_release(struct inode *inode, struct file *file)
+{
+ struct da9052_wdt *wdt = get_wdt_da9052();
+
+ if (da9052_wdt_expect_close == 42)
+ da9052_sm_hw_deinit(wdt);
+ else
+ da9052_sm_strobe_wdt();
+ da9052_wdt_expect_close = 0;
+ clear_bit(0, &da9052_wdt_users);
+ return 0;
+}
+
+static ssize_t da9052_wdt_write(struct file *file,
+ const char __user *data, size_t count,
+ loff_t *ppos)
+{
+ size_t i;
+
+ if (count) {
+ if (!nowayout) {
+ /* In case it was set long ago */
+ da9052_wdt_expect_close = 0;
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ da9052_wdt_expect_close = 42;
+ }
+ }
+ da9052_sm_strobe_wdt();
+ }
+ return count;
+}
+
+static struct watchdog_info da9052_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "DA9052_SM Watchdog",
+};
+
+static long da9052_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct da9052_wdt *wdt = get_wdt_da9052();
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ unsigned char new_value;
+
+ switch (cmd) {
+
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &da9052_wdt_info,
+ sizeof(da9052_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (new_value & DA9052_STROBING_FILTER_ENABLE)
+ da9052_sm_set_strobing_filter(wdt, ENABLE);
+ if (new_value & DA9052_STROBING_FILTER_DISABLE)
+ da9052_sm_set_strobing_filter(wdt, DISABLE);
+ if (new_value & DA9052_SET_STROBING_MODE_MANUAL)
+ da9052_sm_set_strobing_mode(DA9052_STROBE_MANUAL);
+ if (new_value & DA9052_SET_STROBING_MODE_AUTO)
+ da9052_sm_set_strobing_mode(DA9052_STROBE_AUTO);
+ return 0;
+ case WDIOC_KEEPALIVE:
+ if (da9052_sm_strobe_wdt())
+ return -EFAULT;
+ else
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ da9052_sm_scale = new_value;
+ if (da9052_sm_set_wdt(wdt, da9052_sm_scale))
+ return -EFAULT;
+ case WDIOC_GETTIMEOUT:
+ return put_user(sm_mon_interval, p);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct file_operations da9052_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = da9052_wdt_ioctl,
+ .write = da9052_wdt_write,
+ .open = da9052_wdt_open,
+ .release = da9052_wdt_release,
+};
+
+static struct miscdevice da9052_wdt_miscdev = {
+ .minor = 255,
+ .name = "da9052-wdt",
+ .fops = &da9052_wdt_fops,
+};
+
+static int __devinit da9052_sm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct da9052_wdt *wdt;
+ struct da9052_ssc_msg msg;
+
+ wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, wdt);
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ goto err_ssc_comm;
+ }
+ printk(KERN_INFO"DA9052 SM probe - 0 \n");
+
+ msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ goto err_ssc_comm;
+ }
+ da9052_unlock(wdt->da9052);
+
+ da9052_wdt_miscdev.parent = &pdev->dev;
+
+ ret = misc_register(&da9052_wdt_miscdev);
+ if (ret != 0) {
+ platform_set_drvdata(pdev, NULL);
+ kfree(wdt);
+ return -EFAULT;
+ }
+ return 0;
+err_ssc_comm:
+ platform_set_drvdata(pdev, NULL);
+ kfree(wdt);
+ return -EIO;
+}
+
+static int __devexit da9052_sm_remove(struct platform_device *dev)
+{
+ misc_deregister(&da9052_wdt_miscdev);
+
+ return 0;
+}
+
+static struct platform_driver da9052_sm_driver = {
+ .probe = da9052_sm_probe,
+ .remove = __devexit_p(da9052_sm_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_sm_init(void)
+{
+ return platform_driver_register(&da9052_sm_driver);
+}
+module_init(da9052_sm_init);
+
+static void __exit da9052_sm_exit(void)
+{
+ platform_driver_unregister(&da9052_sm_driver);
+}
+module_exit(da9052_sm_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>")
+MODULE_DESCRIPTION("DA9052 SM Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);