From: Zhou Jingyu Date: Wed, 2 Mar 2011 07:36:28 +0000 (+0800) Subject: ENGR00141391-1 PMIC: Add DA9053 source code from Dialog X-Git-Tag: v3.0.35-fsl~2470 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=5015ab91bfc18b19260c80a83301f2c9b4f17c2f;p=karo-tx-linux.git ENGR00141391-1 PMIC: Add DA9053 source code from Dialog Add DA9053 original source code Signed-off-by: Zhou Jingyu Acked-by: Lily Zhang --- 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 "); +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ") +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 +#include +#include +#include + +#include +#include + +#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 "); +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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 "); +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 + * + * 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 + +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 + * + * 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 +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 "); +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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", + ®ulator_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 "); +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 +#include +#include +#include +#include + +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 "); +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 +#include +#include +#include +#include + + +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 "); +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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, ®_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,¤t_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, ®_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, ®_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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 "); +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 +#include +#include +#include +#include + +#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 "); +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 + * + * Copyright (C) 2009 Samsung Electronics Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 +#include +#include + +#include + +#include +#include +#include + + +#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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#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 ") +MODULE_DESCRIPTION("DA9052 SM Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/include/linux/mfd/da9052/adc.h b/include/linux/mfd/da9052/adc.h new file mode 100644 index 000000000000..49c7d181d150 --- /dev/null +++ b/include/linux/mfd/da9052/adc.h @@ -0,0 +1,66 @@ +/* + * da9052 ADC module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_ADC_H +#define __LINUX_MFD_DA9052_ADC_H + +#include "gpio.h" + +#define DA9052_ADC_DEVICE_NAME "da9052_adc" + +/* Channel Definations */ +#define DA9052_ADC_VDDOUT 0 +#define DA9052_ADC_ICH 1 +#define DA9052_ADC_TBAT 2 +#define DA9052_ADC_VBAT 3 +#define DA9052_ADC_ADCIN4 4 +#define DA9052_ADC_ADCIN5 5 +#define DA9052_ADC_ADCIN6 6 +#define DA9052_ADC_TSI 7 +#define DA9052_ADC_TJUNC 8 +#define DA9052_ADC_VBBAT 9 + +#if (DA9052_GPIO_PIN_0 == DA9052_GPIO_CONFIG_ADC) +#define DA9052_ADC_CONF_ADC4 1 +#else +#define DA9052_ADC_CONF_ADC4 0 +#endif +#if (DA9052_GPIO_PIN_1 == DA9052_GPIO_CONFIG_ADC) +#define DA9052_ADC_CONF_ADC5 1 +#else +#define DA9052_ADC_CONF_ADC5 0 +#endif +#if (DA9052_GPIO_PIN_2 == DA9052_GPIO_CONFIG_ADC) +#define DA9052_ADC_CONF_ADC6 1 +#else +#define DA9052_ADC_CONF_ADC6 0 +#endif + +/* Maximum retry count to check manual conversion over */ +#define DA9052_ADC_MAX_MANCONV_RETRY_COUNT 8 + +struct da9052_adc_priv { + struct da9052 *da9052; + struct device *hwmon_dev; + struct mutex manconv_lock; +}; + +#endif /* __LINUX_MFD_DA9052_ADC_H */ diff --git a/include/linux/mfd/da9052/bat.h b/include/linux/mfd/da9052/bat.h new file mode 100644 index 000000000000..ac2404fe46e4 --- /dev/null +++ b/include/linux/mfd/da9052/bat.h @@ -0,0 +1,264 @@ +/* + * da9052 BAT module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_BAT_H +#define __LINUX_MFD_DA9052_BAT_H + +#include + +/* STATIC CONFIGURATION */ +#define DA9052_LOOK_UP_TABLE_SIZE 68 +#define DA9052_NO_OF_LOOKUP_TABLE 3 +#define DA9052_FILTER_SIZE 4 +#define DA9052_NUMBER_OF_STORE_CURENT_READING 4 +#define DA9052_BAT_FILTER_HYS 0 + + +static const u16 temperature_lookup_ref + [DA9052_NO_OF_LOOKUP_TABLE] = {10, 25, 40}; +static u32 const vbat_vs_capacity_look_up[DA9052_NO_OF_LOOKUP_TABLE] + [DA9052_LOOK_UP_TABLE_SIZE][2] = { + /* For temperature 10 degree celisus*/ + { + {4082, 100}, {4036, 98}, + {4020, 96}, {4008, 95}, + {3997, 93}, {3983, 91}, + {3964, 90}, {3943, 88}, + {3926, 87}, {3912, 85}, + {3900, 84}, {3890, 82}, + {3881, 80}, {3873, 79}, + {3865, 77}, {3857, 76}, + {3848, 74}, {3839, 73}, + {3829, 71}, {3820, 70}, + {3811, 68}, {3802, 67}, + {3794, 65}, {3785, 64}, + {3778, 62}, {3770, 61}, + {3763, 59}, {3756, 58}, + {3750, 56}, {3744, 55}, + {3738, 53}, {3732, 52}, + {3727, 50}, {3722, 49}, + {3717, 47}, {3712, 46}, + {3708, 44}, {3703, 43}, + {3700, 41}, {3696, 40}, + {3693, 38}, {3691, 37}, + {3688, 35}, {3686, 34}, + {3683, 32}, {3681, 31}, + {3678, 29}, {3675, 28}, + {3672, 26}, {3669, 25}, + {3665, 23}, {3661, 22}, + {3656, 21}, {3651, 19}, + {3645, 18}, {3639, 16}, + {3631, 15}, {3622, 13}, + {3611, 12}, {3600, 10}, + {3587, 9}, {3572, 7}, + {3548, 6}, {3503, 5}, + {3420, 3}, {3268, 2}, + {2992, 1}, {2746, 0} + }, + /* For temperature 25 degree celisus */ + { + {4102, 100}, {4065, 98}, + {4048, 96}, {4034, 95}, + {4021, 93}, {4011, 92}, + {4001, 90}, {3986, 88}, + {3968, 87}, {3952, 85}, + {3938, 84}, {3926, 82}, + {3916, 81}, {3908, 79}, + {3900, 77}, {3892, 76}, + {3883, 74}, {3874, 73}, + {3864, 71}, {3855, 70}, + {3846, 68}, {3836, 67}, + {3827, 65}, {3819, 64}, + {3810, 62}, {3801, 61}, + {3793, 59}, {3786, 58}, + {3778, 56}, {3772, 55}, + {3765, 53}, {3759, 52}, + {3754, 50}, {3748, 49}, + {3743, 47}, {3738, 46}, + {3733, 44}, {3728, 43}, + {3724, 41}, {3720, 40}, + {3716, 38}, {3712, 37}, + {3709, 35}, {3706, 34}, + {3703, 33}, {3701, 31}, + {3698, 30}, {3696, 28}, + {3693, 27}, {3690, 25}, + {3687, 24}, {3683, 22}, + {3680, 21}, {3675, 19}, + {3671, 18}, {3666, 17}, + {3660, 15}, {3654, 14}, + {3647, 12}, {3639, 11}, + {3630, 9}, {3621, 8}, + {3613, 6}, {3606, 5}, + {3597, 4}, {3582, 2}, + {3546, 1}, {2747, 0} + }, + /* For temperature 40 degree celisus*/ + { + {4114, 100}, {4081, 98}, + {4065, 96}, {4050, 95}, + {4036, 93}, {4024, 92}, + {4013, 90}, {4002, 88}, + {3990, 87}, {3976, 85}, + {3962, 84}, {3950, 82}, + {3939, 81}, {3930, 79}, + {3921, 77}, {3912, 76}, + {3902, 74}, {3893, 73}, + {3883, 71}, {3874, 70}, + {3865, 68}, {3856, 67}, + {3847, 65}, {3838, 64}, + {3829, 62}, {3820, 61}, + {3812, 59}, {3803, 58}, + {3795, 56}, {3787, 55}, + {3780, 53}, {3773, 52}, + {3767, 50}, {3761, 49}, + {3756, 47}, {3751, 46}, + {3746, 44}, {3741, 43}, + {3736, 41}, {3732, 40}, + {3728, 38}, {3724, 37}, + {3720, 35}, {3716, 34}, + {3713, 33}, {3710, 31}, + {3707, 30}, {3704, 28}, + {3701, 27}, {3698, 25}, + {3695, 24}, {3691, 22}, + {3686, 21}, {3681, 19}, + {3676, 18}, {3671, 17}, + {3666, 15}, {3661, 14}, + {3655, 12}, {3648, 11}, + {3640, 9}, {3632, 8}, + {3622, 6}, {3616, 5}, + {3611, 4}, {3604, 2}, + {3594, 1}, {2747, 0} + } +}; + +enum charger_type_enum { + DA9052_NOCHARGER = 1, + DA9052_USB_HUB, + DA9052_USB_CHARGER, + DA9052_WALL_CHARGER +}; + +struct da9052_bat_event_registration { + u8 da9052_event_tbat:1; +}; + +struct da9052_bat_hysteresis { + u16 bat_volt_arr[3]; + u16 array_hys_batvoltage[2]; + u16 upper_limit; + u16 lower_limit; + u8 index; + u8 hys_flag; +}; + +struct da9052_charger_device { + struct da9052 *da9052; + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; + struct power_supply psy; + struct da9052_eh_nb tbat_eh_data; + u8 cal_capacity; + u8 charger_type; + u8 health; + u8 status; + u8 illegal; + u16 technology; + u16 chg_current; + u16 bat_voltage; + u16 bat_capacity_limit_low; + u16 bat_capacity_full; + u16 bat_capacity_limit_high; + u16 bat_volt_cutoff; + u16 bat_with_no_resistor; + u16 bat_temp; + u8 hys_flag; + u16 charger_voltage_drop; + u16 bat_target_voltage; + u16 chg_end_current; + u16 hysteresis_window_size; + u16 chg_hysteresis_const; + u16 hysteresis_reading_interval; + u16 hysteresis_no_of_reading; + u16 vbat_first_valid_detect_iteration; +}; + + +static inline u8 bat_temp_reg_to_C(u16 value) { return (55 - value); } +static inline u8 bat_mV_to_reg(u16 value) { return (((value-4100)/100)<<4); } +static inline u8 bat_drop_mV_to_reg(u16 value) + { return (((value-100)/100)<<6); } +static inline u16 bat_reg_to_mV(u8 value) { return ((value*100) + 4100); } +static inline u16 bat_drop_reg_to_mV(u8 value) { return ((value*100)+100); } +static inline u8 vch_thr_mV_to_reg(u16 value) { return ((value-3700)/100); } +static inline u8 precharge_mA_to_reg(u8 value) { return ((value/20)<<6); } +static inline u8 vddout_mon_mV_to_reg(u16 value) + { return (((value-2500)*128)/1000); } +static inline u16 vddout_reg_to_mV(u8 value) + { return ((value*1000)/128)+2500; } +static inline u16 volt_reg_to_mV(u16 value) + { return ((value*1000)/512)+2500; } +static inline u8 ichg_mA_to_reg(u16 value) { return (value/4); } +static inline u16 ichg_reg_to_mA(u8 value) { return ((value*3900)/1000); } +static inline u8 iset_mA_to_reg(u16 iset_value) + {\ + if ((70 <= iset_value) && (iset_value <= 120)) \ + return (iset_value-70)/10; \ + else if ((400 <= iset_value) && (iset_value <= 700)) \ + return ((iset_value-400)/50)+6; \ + else if ((900 <= iset_value) && (iset_value <= 1300)) \ + return ((iset_value-900)/200)+13; else return 0; + } + +#define DA9052_BAT_DEBUG 0 + +#define DA9052_BAT_PROFILE 0 +#define SUCCESS 0 +#define FAILURE 1 + +#define TRUE 1 +#define FALSE 0 + +#define set_bits(value, mask) (value | mask) +#define clear_bits(value, mask) (value & ~(mask)) + +#undef DA9052_DEBUG +#if DA9052_BAT_DEBUG +#define DA9052_DEBUG(fmt, args...) printk(KERN_CRIT "" fmt, ##args) +#else +#define DA9052_DEBUG(fmt, args...) +#endif + + +/* SSC Read or Write Error */ +#define DA9052_SSC_FAIL 150 + +/* To enable debug output for your module, set this to 1 */ +#define DA9052_SSC_DEBUG 0 + +#undef DA9052_DEBUG +#if DA9052_SSC_DEBUG +#define DA9052_DEBUG(fmt, args...) printk(KERN_CRIT "" fmt, ##args) +#else +#define DA9052_DEBUG(fmt, args...) +#endif + + +#endif /* __LINUX_MFD_DA9052_BAT_H */ diff --git a/include/linux/mfd/da9052/bl.h b/include/linux/mfd/da9052/bl.h new file mode 100644 index 000000000000..9217eb95a77e --- /dev/null +++ b/include/linux/mfd/da9052/bl.h @@ -0,0 +1,289 @@ +/* + * da9052 Backlight module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_BL_H +#define __LINUX_MFD_DA9052_BL_H + +/* + * enum da9052_bl_current_value - Enum for current values of LED + * DA9052_BL_CURRENT_50_0UA: represents 50.0 uA current value + */ +enum da9052_bl_current_value { +/* Current Value for LEDMIN, LED1_CONF, LED2_CONF, LED3_CONF Registers */ + DA9052_BL_CURRENT_50_0UA = 0, + DA9052_BL_CURRENT_51_2UA, + DA9052_BL_CURRENT_52_5UA, + DA9052_BL_CURRENT_53_7UA, + DA9052_BL_CURRENT_55_1UA, + DA9052_BL_CURRENT_56_4UA, + DA9052_BL_CURRENT_57_8UA, + DA9052_BL_CURRENT_59_3UA, + DA9052_BL_CURRENT_60_7UA, + DA9052_BL_CURRENT_62_2UA, + DA9052_BL_CURRENT_63_8UA, + DA9052_BL_CURRENT_65_3UA, + DA9052_BL_CURRENT_67_0UA, + DA9052_BL_CURRENT_68_6UA, + DA9052_BL_CURRENT_70_3UA, + DA9052_BL_CURRENT_72_0UA, + DA9052_BL_CURRENT_73_8UA, + DA9052_BL_CURRENT_75_7UA, + DA9052_BL_CURRENT_77_5UA, + DA9052_BL_CURRENT_79_4UA, + DA9052_BL_CURRENT_81_4UA, + DA9052_BL_CURRENT_83_4UA, + DA9052_BL_CURRENT_85_5UA, + DA9052_BL_CURRENT_87_6UA, + DA9052_BL_CURRENT_89_8UA, + DA9052_BL_CURRENT_92_0UA, + DA9052_BL_CURRENT_94_2UA, + DA9052_BL_CURRENT_96_6UA, + DA9052_BL_CURRENT_99_0UA, + DA9052_BL_CURRENT_101_4UA, + DA9052_BL_CURRENT_103_9UA, + DA9052_BL_CURRENT_106_5UA, + DA9052_BL_CURRENT_109_1UA, + DA9052_BL_CURRENT_111_8UA, + DA9052_BL_CURRENT_114_6UA, + DA9052_BL_CURRENT_117_4UA, + DA9052_BL_CURRENT_120_6UA, + DA9052_BL_CURRENT_123_3UA, + DA9052_BL_CURRENT_126_3UA, + DA9052_BL_CURRENT_129_4UA, + DA9052_BL_CURRENT_132_6UA, + DA9052_BL_CURRENT_135_9UA, + DA9052_BL_CURRENT_139_3UA, + DA9052_BL_CURRENT_142_7UA, + DA9052_BL_CURRENT_146_2UA, + DA9052_BL_CURRENT_149_9UA, + DA9052_BL_CURRENT_153_6UA, + DA9052_BL_CURRENT_157_4UA, + DA9052_BL_CURRENT_161_2UA, + DA9052_BL_CURRENT_165_2UA, + DA9052_BL_CURRENT_169_3UA, + DA9052_BL_CURRENT_173_5UA, + DA9052_BL_CURRENT_177_8UA, + DA9052_BL_CURRENT_182_2UA, + DA9052_BL_CURRENT_186_7UA, + DA9052_BL_CURRENT_191_3UA, + DA9052_BL_CURRENT_196_0UA, + DA9052_BL_CURRENT_200_9UA, + DA9052_BL_CURRENT_205_8UA, + DA9052_BL_CURRENT_210_9UA, + DA9052_BL_CURRENT_216_1UA, + DA9052_BL_CURRENT_221_4UA, + DA9052_BL_CURRENT_226_9UA, + DA9052_BL_CURRENT_232_5UA, + DA9052_BL_CURRENT_238_3UA, + DA9052_BL_CURRENT_244_2UA, + DA9052_BL_CURRENT_250_2UA, + DA9052_BL_CURRENT_256_4UA, + DA9052_BL_CURRENT_262_7UA, + DA9052_BL_CURRENT_269_2UA, + DA9052_BL_CURRENT_275_8UA, + DA9052_BL_CURRENT_282_7UA, + DA9052_BL_CURRENT_289_6UA, + DA9052_BL_CURRENT_296_8UA, + DA9052_BL_CURRENT_304_1UA, + DA9052_BL_CURRENT_311_6UA, + DA9052_BL_CURRENT_319_3UA, + DA9052_BL_CURRENT_327_2UA, + DA9052_BL_CURRENT_335_3UA, + DA9052_BL_CURRENT_343_6UA, + DA9052_BL_CURRENT_352_1UA, + DA9052_BL_CURRENT_360_8UA, + DA9052_BL_CURRENT_369_7UA, + DA9052_BL_CURRENT_378_8UA, + DA9052_BL_CURRENT_388_2UA, + DA9052_BL_CURRENT_397_8UA, + DA9052_BL_CURRENT_407_6UA, + DA9052_BL_CURRENT_417_7UA, + DA9052_BL_CURRENT_428_0UA, + DA9052_BL_CURRENT_438_6UA, + DA9052_BL_CURRENT_449_4UA, + DA9052_BL_CURRENT_460_5UA, + DA9052_BL_CURRENT_471_9UA, + DA9052_BL_CURRENT_483_5UA, + DA9052_BL_CURRENT_495_5UA, + DA9052_BL_CURRENT_507_7UA, + DA9052_BL_CURRENT_520_3UA, + DA9052_BL_CURRENT_533_1UA, + DA9052_BL_CURRENT_546_3UA, + DA9052_BL_CURRENT_559_8UA, + DA9052_BL_CURRENT_573_6UA, + DA9052_BL_CURRENT_587_8UA, + DA9052_BL_CURRENT_602_3UA, + DA9052_BL_CURRENT_617_2UA, + DA9052_BL_CURRENT_632_4UA, + DA9052_BL_CURRENT_648_0UA, + DA9052_BL_CURRENT_664_0UA, + DA9052_BL_CURRENT_680_4UA, + DA9052_BL_CURRENT_697_2UA, + DA9052_BL_CURRENT_714_5UA, + DA9052_BL_CURRENT_732_1UA, + DA9052_BL_CURRENT_750_2UA, + DA9052_BL_CURRENT_768_7UA, + DA9052_BL_CURRENT_787_7UA, + DA9052_BL_CURRENT_807_2UA, + DA9052_BL_CURRENT_827_1UA, + DA9052_BL_CURRENT_847_6UA, + DA9052_BL_CURRENT_868_5UA, + DA9052_BL_CURRENT_889_9UA, + DA9052_BL_CURRENT_911_9UA, + DA9052_BL_CURRENT_934_4UA, + DA9052_BL_CURRENT_957_5UA, + DA9052_BL_CURRENT_981_2UA, + DA9052_BL_CURRENT_1005_4UA, + DA9052_BL_CURRENT_1030_3UA, + DA9052_BL_CURRENT_1055_7UA, + DA9052_BL_CURRENT_1081_8UA, + DA9052_BL_CURRENT_1108_5UA, + DA9052_BL_CURRENT_1135_9UA, + DA9052_BL_CURRENT_1163_9UA, + DA9052_BL_CURRENT_1192_7UA, + DA9052_BL_CURRENT_1222_2UA, + DA9052_BL_CURRENT_1252_3UA, + DA9052_BL_CURRENT_1283_3UA, + DA9052_BL_CURRENT_1315_0UA, + DA9052_BL_CURRENT_1347_5UA, + DA9052_BL_CURRENT_1380_7UA, + DA9052_BL_CURRENT_1414_8UA, + DA9052_BL_CURRENT_1449_8UA, + DA9052_BL_CURRENT_1485_6UA, + DA9052_BL_CURRENT_1522_3UA, + DA9052_BL_CURRENT_1559_9UA, + DA9052_BL_CURRENT_1598_4UA, + DA9052_BL_CURRENT_1637_9UA, + DA9052_BL_CURRENT_1678_4UA, + DA9052_BL_CURRENT_1719_8UA, + DA9052_BL_CURRENT_1762_3UA, + DA9052_BL_CURRENT_1805_8UA, + DA9052_BL_CURRENT_1850_4UA, + DA9052_BL_CURRENT_1896_1UA, + DA9052_BL_CURRENT_1943_0UA, + DA9052_BL_CURRENT_1991_0UA, + DA9052_BL_CURRENT_2040_2UA, + DA9052_BL_CURRENT_2090_5UA, + DA9052_BL_CURRENT_2142_2UA, + DA9052_BL_CURRENT_2195_1UA, + DA9052_BL_CURRENT_2249_3UA, + DA9052_BL_CURRENT_2304_9UA, + DA9052_BL_CURRENT_2361_8UA, + DA9052_BL_CURRENT_2420_1UA, + DA9052_BL_CURRENT_2479_9UA, + DA9052_BL_CURRENT_2541_2UA, + DA9052_BL_CURRENT_2604_0UA, + DA9052_BL_CURRENT_2668_3UA, + DA9052_BL_CURRENT_2734_2UA, + DA9052_BL_CURRENT_2801_7UA, + DA9052_BL_CURRENT_2870_9UA, + DA9052_BL_CURRENT_2941_8UA, + DA9052_BL_CURRENT_3014_5UA, + DA9052_BL_CURRENT_3089_0UA, + DA9052_BL_CURRENT_3165_3UA, + DA9052_BL_CURRENT_3243_4UA, + DA9052_BL_CURRENT_3323_5UA, + DA9052_BL_CURRENT_3405_6UA, + DA9052_BL_CURRENT_3489_8UA, + DA9052_BL_CURRENT_3576_0UA, + DA9052_BL_CURRENT_3664_3UA, + DA9052_BL_CURRENT_3754_8UA, + DA9052_BL_CURRENT_3847_5UA, + DA9052_BL_CURRENT_3942_6UA, + DA9052_BL_CURRENT_4040_0UA, + DA9052_BL_CURRENT_4139_7UA, + DA9052_BL_CURRENT_4242_0UA, + DA9052_BL_CURRENT_4346_8UA, + DA9052_BL_CURRENT_4454_1UA, + DA9052_BL_CURRENT_4564_2UA, + DA9052_BL_CURRENT_4676_9UA, + DA9052_BL_CURRENT_4792_4UA, + DA9052_BL_CURRENT_4910_8UA, + DA9052_BL_CURRENT_5032_1UA, + DA9052_BL_CURRENT_5156_4UA, + DA9052_BL_CURRENT_5283_7UA, + DA9052_BL_CURRENT_5414_3UA, + DA9052_BL_CURRENT_5548_0UA, + DA9052_BL_CURRENT_5685_0UA, + DA9052_BL_CURRENT_5825_5UA, + DA9052_BL_CURRENT_5969_3UA, + DA9052_BL_CURRENT_6116_8UA, + DA9052_BL_CURRENT_6267_9UA, + DA9052_BL_CURRENT_6422_7UA, + DA9052_BL_CURRENT_6581_3UA, + DA9052_BL_CURRENT_6743_9UA, + DA9052_BL_CURRENT_6910_5UA, + DA9052_BL_CURRENT_7081_2UA, + DA9052_BL_CURRENT_7256_1UA, + DA9052_BL_CURRENT_7435_3UA, + DA9052_BL_CURRENT_7618_9UA, + DA9052_BL_CURRENT_7807_1UA, + DA9052_BL_CURRENT_8000_0UA, + DA9052_BL_CURRENT_8197_6UA, + DA9052_BL_CURRENT_8400_0UA, + DA9052_BL_CURRENT_8607_5UA, + DA9052_BL_CURRENT_8820_1UA, + DA9052_BL_CURRENT_9038_0UA, + DA9052_BL_CURRENT_9261_2UA, + DA9052_BL_CURRENT_9490_0UA, + DA9052_BL_CURRENT_9724_4UA, + DA9052_BL_CURRENT_9964_6UA, + DA9052_BL_CURRENT_10210_7UA, + DA9052_BL_CURRENT_10462_9UA, + DA9052_BL_CURRENT_10721_4UA, + DA9052_BL_CURRENT_10986_4UA, + DA9052_BL_CURRENT_11257_5UA, + DA9052_BL_CURRENT_11535_6UA, + DA9052_BL_CURRENT_11820_5UA, + DA9052_BL_CURRENT_12112_5UA, + DA9052_BL_CURRENT_12411_7UA, + DA9052_BL_CURRENT_12718_2UA, + DA9052_BL_CURRENT_13032_4UA, + DA9052_BL_CURRENT_13354_3UA, + DA9052_BL_CURRENT_13684_1UA, + DA9052_BL_CURRENT_14022_1UA, + DA9052_BL_CURRENT_14368_5UA, + DA9052_BL_CURRENT_14723_4UA, + DA9052_BL_CURRENT_15087_1UA, + DA9052_BL_CURRENT_15459_7UA, + DA9052_BL_CURRENT_15841_6UA, + DA9052_BL_CURRENT_16232_9UA, + DA9052_BL_CURRENT_16633_8UA, + DA9052_BL_CURRENT_17044_7UA, + DA9052_BL_CURRENT_17465_7UA, + DA9052_BL_CURRENT_17897_1UA, + DA9052_BL_CURRENT_18339_1UA, + DA9052_BL_CURRENT_18792_1UA, + DA9052_BL_CURRENT_19256_3UA, + DA9052_BL_CURRENT_19731_9UA, + DA9052_BL_CURRENT_20219_3UA, + DA9052_BL_CURRENT_20718_7UA, + DA9052_BL_CURRENT_21230_5UA, + DA9052_BL_CURRENT_21754_8UA, + DA9052_BL_CURRENT_22292_2UA, + DA9052_BL_CURRENT_22842_8UA, + DA9052_BL_CURRENT_23407_0UA, + DA9052_BL_CURRENT_23985_2UA, + DA9052_BL_CURRENT_24577_6UA, + DA9052_BL_CURRENT_25000_0UA, +}; + +#endif /* __LINUX_MFD_DA9052_BL_H */ diff --git a/include/linux/mfd/da9052/da9052.h b/include/linux/mfd/da9052/da9052.h new file mode 100644 index 000000000000..7652d07f70d3 --- /dev/null +++ b/include/linux/mfd/da9052/da9052.h @@ -0,0 +1,206 @@ +/* + * da9052 declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_DA9052_H +#define __LINUX_MFD_DA9052_DA9052_H + +#include +#include + +#include +#include +#include + + +#define SPI 1 +#define I2C 2 + +#define DA9052_SSC_DEVICE_NAME "da9052_ssc" +#define DA9052_EH_DEVICE_NAME "da9052_eh" + +#define DA9052_IRQ S3C_EINT(9) + +/* Module specific error codes */ +#define INVALID_REGISTER 2 +#define INVALID_READ 3 +#define INVALID_PAGE 4 + +/* Defines for Volatile and Non Volatile register types */ +#define VOLATILE 0 +#define NON_VOLATILE 1 + +/* Defines for cache state */ +#define VALID 0 +#define INVALID 1 + +/* Total number of registers in DA9057 */ +#define DA9052_REG_CNT DA9052_PAGE1_REG_END + +/* Maximum number of registers that can be read/written by a singe request */ +#define MAX_READ_WRITE_CNT 16 + + +#define DA9052_SSC_SPI_DEVICE_NAME "da9052_ssc_spi" +#define PAGE_0_START 1 +#define PAGE_0_END 127 +#define PAGE_1_START 128 +#define PAGE_1_END 255 +#define ACTIVE_PAGE_0 0 +#define ACTIVE_PAGE_1 1 +#define PAGECON_0 0 +#define PAGECON_128 128 +#define RW_POL 1 + +#define DA9052_SSC_I2C_DEVICE_NAME "da9052_ssc_i2c" +#define DA9052_I2C_ADDR 0x90 +#define DA9052_SSC_I2C_PAGE_WRITE_MODE 0 +#define DA9052_SSC_I2C_REPEAT_WRITE_MODE 1 +#define DA9052_SSC_I2C_WRITE_MODE DA9052_SSC_I2C_REPEAT_WRITE_MODE + +struct da9052_ssc_msg { + unsigned char data; + unsigned char addr; +}; + +struct ssc_cache_entry{ + unsigned char val; + unsigned char type:4; + unsigned char status:4; +}; + +struct da9052_eh_nb{ + struct list_head nb_list; + unsigned char eve_type; + void (*call_back)(struct da9052_eh_nb *, unsigned int); +}; + +struct da9052_regulator_init_data { + struct regulator_init_data *init_data; +}; + +struct da9052_regulator_platform_data { + struct regulator_init_data *regulators; +}; + +struct da9052_tsi_platform_data { + u32 pen_up_interval; + u16 tsi_delay_bit_shift; + u16 tsi_skip_bit_shift; + u16 num_gpio_tsi_register; + u16 tsi_supply_voltage; + u16 tsi_ref_source; + u16 max_tsi_delay; + u16 max_tsi_skip_slot; +}; + + +struct da9052 { + struct mutex ssc_lock; + struct mutex eve_nb_lock; + struct mutex manconv_lock; + struct work_struct eh_isr_work; + struct ssc_cache_entry ssc_cache[DA9052_REG_CNT]; + int (*read) (struct da9052 *da9052, struct da9052_ssc_msg *sscmsg); + int (*write) (struct da9052 *da9052, struct da9052_ssc_msg *sscmsg); + int (*read_many) (struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int cnt); + int (*write_many)(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int cnt); + int (*register_event_notifier)(struct da9052 *da9052, + struct da9052_eh_nb *nb); + int (*unregister_event_notifier)(struct da9052 *da9052, + struct da9052_eh_nb *nb); + int num_regulators; + int connecting_device; + struct spi_device *spi_dev; + unsigned int spi_active_page; + unsigned char rw_pol; + unsigned char *spi_rx_buf; + unsigned char *spi_tx_buf; + + struct i2c_client *i2c_client; + struct device *dev; + struct i2c_adapter *adapter; + unsigned char slave_addr; +}; + + +struct da9052_platform_data { + int (*init)(struct da9052 *da9052); + int irq_high; + int irq_base; + int gpio_base; + int num_regulators; + struct da9052 *da9052; + struct regulator_init_data *regulators; + struct da9052_leds_platform_data *led_data; + struct da9052_tsi_platform_data *tsi_data; +}; + +struct da9052_ssc_ops { + int (*write)(struct da9052 *da9052, struct da9052_ssc_msg *msg); + int (*read)(struct da9052 *da9052, struct da9052_ssc_msg *msg); + int (*write_many)(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int msg_no); + int (*read_many)(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int msg_no); + int (*device_register)(struct da9052 *da9052); + void (*device_unregister)(void); +}; + +int da9052_ssc_write(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg); +int da9052_ssc_read(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg); +int da9052_ssc_write_many(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int cnt); +int da9052_ssc_read_many(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int cnt); + +int da9052_spi_write(struct da9052 *da9052, + struct da9052_ssc_msg *msg); +int da9052_spi_read(struct da9052 *da9052, + struct da9052_ssc_msg *msg); + +int da9052_spi_write_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg, + int msg_no); +int da9052_spi_read_many(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, + int msg_no); + +void da9052_ssc_exit(struct da9052 *da9052); +int da9052_ssc_init(struct da9052 *da9052); + +/* I2C specific Functions */ +int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg); +int da9052_i2c_read(struct da9052 *da9052, struct da9052_ssc_msg *msg); +int da9052_i2c_write_many(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int msg_no); +int da9052_i2c_read_many(struct da9052 *da9052, + struct da9052_ssc_msg *sscmsg, int msg_no); + +void da9052_lock(struct da9052 *da9052); +void da9052_unlock(struct da9052 *da9052); +int eh_register_nb(struct da9052 *da9052, struct da9052_eh_nb *nb); +int eh_unregister_nb(struct da9052 *da9052, struct da9052_eh_nb *nb); +int da9052_manual_read(struct da9052 *da9052, + unsigned char channel); +#endif /* __LINUX_MFD_DA9052_DA9052_H */ diff --git a/include/linux/mfd/da9052/eh.h b/include/linux/mfd/da9052/eh.h new file mode 100644 index 000000000000..c46df5d8962e --- /dev/null +++ b/include/linux/mfd/da9052/eh.h @@ -0,0 +1,77 @@ +/* + * da9052 Event Handler module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_EH_H +#define __LINUX_MFD_DA9052_EH_H + +#define DA9052_EVE_REGISTERS 4 +#define DA9052_EVE_REGISTER_SIZE 8 + +/* Define for all possible events */ +#define DCIN_DET_EVE 0 +#define VBUS_DET_EVE 1 +#define DCIN_REM_EVE 2 +#define VBUS_REM_EVE 3 +#define VDD_LOW_EVE 4 +#define ALARM_EVE 5 +#define SEQ_RDY_EVE 6 +#define COMP_1V2 7 +#define ONKEY_EVE 8 +#define ID_FLOAT_EVE 9 +#define ID_GND_EVE 10 +#define CHG_END_EVE 11 +#define TBAT_EVE 12 +#define ADC_EOM_EVE 13 +#define PEN_DOWN_EVE 14 +#define TSI_READY_EVE 15 +#define GPI0_EVE 16 +#define GPI1_EVE 17 +#define GPI2_EVE 18 +#define GPI3_EVE 19 +#define GPI4_EVE 20 +#define GPI5_EVE 21 +#define GPI6_EVE 22 +#define GPI7_EVE 23 +#define GPI8_EVE 24 +#define GPI9_EVE 25 +#define GPI10_EVE 26 +#define GPI11_EVE 27 +#define GPI12_EVE 28 +#define GPI13_EVE 29 +#define GPI14_EVE 30 +#define GPI15_EVE 31 + +/* Total number of events */ +#define EVE_CNT (GPI15_EVE+1) + +/* Error code for register/unregister functions */ +#define INVALID_NB 2 +#define INVALID_EVE 3 + +/* State for EH thread */ +#define ACTIVE 0 +#define INACTIVE 1 + +/* Status of nIRQ line */ +#define IRQ_HIGH 0 +#define IRQ_LOW 1 + +#endif /* __LINUX_MFD_DA9052_EH_H */ diff --git a/include/linux/mfd/da9052/gpio.h b/include/linux/mfd/da9052/gpio.h new file mode 100644 index 000000000000..68a24ad9d828 --- /dev/null +++ b/include/linux/mfd/da9052/gpio.h @@ -0,0 +1,253 @@ +/* + * da9052 GPIO module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_GPIO_H +#define __LINUX_MFD_DA9052_GPIO_H + +#include +#define DA9052_GPIO_DEVICE_NAME "da9052_gpio" + +#define DA9052_GPIO_INVALID_TYPE 1 +#define DA9052_GPIO_INVALID_MODE 2 +#define DA9052_GPIO_INVALID_PORTNUMBER 3 +#define DA9052_GPIO_INVALID_FUNCTION 4 + +#define DA9052_GPIO_CONFIG_ADC 1 +#define DA9052_GPIO_CONFIG_TSI 2 +#define DA9052_GPIO_CONFIG_PM 3 +#define DA9052_GPIO_CONFIG_ACC_ID_DET 4 +#define DA9052_GPIO_CONFIG_GP_FB1 5 +#define DA9052_GPIO_CONFIG_VDD_FAULT 6 +#define DA9052_GPIO_CONFIG_I2C 7 +#define DA9052_GPIO_CONFIG 8 + +/* Currently used defines for GPIO PINs */ +#define DA9052_GPIO_PIN_0 DA9052_GPIO_CONFIG_ADC +#define DA9052_GPIO_PIN_1 DA9052_GPIO_CONFIG_ADC +#define DA9052_GPIO_PIN_2 DA9052_GPIO_CONFIG_ADC + +#define DA9052_GPIO_PIN_3 DA9052_GPIO_CONFIG_TSI +#define DA9052_GPIO_PIN_4 DA9052_GPIO_CONFIG_TSI +#define DA9052_GPIO_PIN_5 DA9052_GPIO_CONFIG_TSI +#define DA9052_GPIO_PIN_6 DA9052_GPIO_CONFIG_TSI +#define DA9052_GPIO_PIN_7 DA9052_GPIO_CONFIG_TSI + +#define DA9052_GPIO_PIN_8 DA9052_GPIO_CONFIG +#define DA9052_GPIO_PIN_9 DA9052_GPIO_CONFIG +#define DA9052_GPIO_PIN_10 DA9052_GPIO_CONFIG +#define DA9052_GPIO_PIN_11 DA9052_GPIO_CONFIG + +#define DA9052_GPIO_PIN_12 DA9052_GPIO_CONFIG +#define DA9052_GPIO_PIN_13 DA9052_GPIO_CONFIG + +#define DA9052_GPIO_PIN_14 DA9052_GPIO_CONFIG +#define DA9052_GPIO_PIN_15 DA9052_GPIO_CONFIG + +enum ip_op_type { + ALTERNATE_FUNCTIONALITY = 0, + INPUT, + OUTPUT_OPENDRAIN, + OUTPUT_PUSHPULL +}; + +enum ip_type { + ACTIVE_LOW = 0, + ACTIVE_HIGH +}; + +enum op_type { + SUPPLY_VDD_IO1 = 0, + SUPPLY_VDD_IO2 +}; + + +enum op_mode { + OUTPUT_LOWLEVEL = 0, + OUTPUT_HIGHLEVEL +}; + + +enum ip_mode { + DEBOUNCING_OFF = 0, + DEBOUNCING_ON +}; + +/*DEFAULT CONFIG FOR GPIO 0*/ +#if (DA9052_GPIO_PIN_0 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO0_FUNCTION INPUT +#define DEFAULT_GPIO0_TYPE ACTIVE_LOW +#define DEFAULT_GPIO0_MODE DEBOUNCING_ON +#endif + +/*DEFAULT CONFIG FOR GPIO 1*/ +#if (DA9052_GPIO_PIN_1 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO1_FUNCTION INPUT +#define DEFAULT_GPIO1_TYPE ACTIVE_LOW +#define DEFAULT_GPIO1_MODE DEBOUNCING_ON +#endif + +/*DEFAULT CONFIG FOR GPIO 2*/ +#if (DA9052_GPIO_PIN_2 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO2_FUNCTION INPUT +#define DEFAULT_GPIO2_TYPE ACTIVE_LOW +#define DEFAULT_GPIO2_MODE DEBOUNCING_ON +#endif + +/*DEFAULT CONFIG FOR GPIO 3*/ +#if (DA9052_GPIO_PIN_3 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO3_FUNCTION INPUT +#define DEFAULT_GPIO3_TYPE ACTIVE_LOW +#define DEFAULT_GPIO3_MODE DEBOUNCING_ON +#endif + +/*DEFAULT CONFIG FOR GPIO 4*/ +#if (DA9052_GPIO_PIN_4 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO4_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO4_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO4_MODE OUTPUT_LOWLEVEL +#endif +/*DEFAULT CONFIG FOR GPIO 5*/ +#if (DA9052_GPIO_PIN_5 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO5_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO5_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO5_MODE OUTPUT_LOWLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 6*/ +#if (DA9052_GPIO_PIN_6 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO6_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO6_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO6_MODE OUTPUT_LOWLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 7*/ +#if (DA9052_GPIO_PIN_7 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO7_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO7_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO7_MODE OUTPUT_LOWLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 8*/ +#if (DA9052_GPIO_PIN_8 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO8_FUNCTION INPUT +#define DEFAULT_GPIO8_TYPE ACTIVE_LOW +#define DEFAULT_GPIO8_MODE DEBOUNCING_ON +#endif + +/*DEFAULT CONFIG FOR GPIO 9*/ +#if (DA9052_GPIO_PIN_9 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO9_FUNCTION INPUT +#define DEFAULT_GPIO9_TYPE ACTIVE_LOW +#define DEFAULT_GPIO9_MODE DEBOUNCING_ON +#endif + +#if (DA9052_GPIO_PIN_10 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO10_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO10_TYPE SUPPLY_VDD_IO2 +#define DEFAULT_GPIO10_MODE OUTPUT_HIGHLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 11 - for RTC blinking LED */ +#if (DA9052_GPIO_PIN_11 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO11_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO11_TYPE SUPPLY_VDD_IO2 +#define DEFAULT_GPIO11_MODE OUTPUT_HIGHLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 12*/ +#if (DA9052_GPIO_PIN_12 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO12_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO12_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO12_MODE OUTPUT_LOWLEVEL +#endif +/*DEFAULT CONFIG FOR GPIO 13*/ +#if (DA9052_GPIO_PIN_13 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO13_FUNCTION OUTPUT_PUSHPULL +#define DEFAULT_GPIO13_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO13_MODE OUTPUT_LOWLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 14 - for LED4 */ +#if (DA9052_GPIO_PIN_14 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO14_FUNCTION OUTPUT_OPENDRAIN +#define DEFAULT_GPIO14_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO14_MODE OUTPUT_HIGHLEVEL +#endif + +/*DEFAULT CONFIG FOR GPIO 15 - for LED5 */ +#if (DA9052_GPIO_PIN_15 == DA9052_GPIO_CONFIG) +#define DEFAULT_GPIO15_FUNCTION OUTPUT_OPENDRAIN +#define DEFAULT_GPIO15_TYPE SUPPLY_VDD_IO1 +#define DEFAULT_GPIO15_MODE OUTPUT_HIGHLEVEL +#endif + +#define DA9052_GPIO_MAX_PORTNUMBER 16 +#define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8 +#define DA9052_GPIO_SHIFT_COUNT(no) ((no)%8) +#define DA9052_GPIO_EVEN_PORT_FUNCTIONALITY 0x03 +#define DA9052_GPIO_ODD_PORT_FUNCTIONALITY 0x30 +#define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0 +#define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F +#define DA9052_GPIO_NIBBLE_SHIFT 4 +#define DA9052_GPIO_EVEN_PORT_WRITE_MODE (1 << 3) +#define DA9052_GPIO_ODD_PORT_WRITE_MODE (1 << 7) + + +struct da9052_gpio_read_write { + u8 port_number:4; + u8 read_write_value:1; +} ; + +struct da9052_gpio_multiple_read { + u8 signal_value[16]; +}; + +struct da9052_gpi_config { + enum ip_type type; + enum ip_mode mode; +}; + +struct da9052_gpo_config { + enum op_type type; + enum op_mode mode; +} ; + +union da9052_gpio_config { + struct da9052_gpi_config input; + struct da9052_gpo_config output; +}; + +struct da9052_gpio { + union da9052_gpio_config gpio_config; + enum ip_op_type gpio_function; + u8 port_number:4; +}; + +struct da9052_gpio_chip { + struct da9052_gpio gpio; + struct da9052_gpio_read_write read_write; + struct da9052 *da9052; + /* For testing*/ + struct da9052_eh_nb eh_data; + struct gpio_chip gp; +}; + +#endif /* __LINUX_MFD_DA9052_GPIO_H */ diff --git a/include/linux/mfd/da9052/led.h b/include/linux/mfd/da9052/led.h new file mode 100644 index 000000000000..b4145cd2023d --- /dev/null +++ b/include/linux/mfd/da9052/led.h @@ -0,0 +1,39 @@ +/* + * da9052 LED module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_LED_H +#define __LINUX_MFD_DA9052_LED_H + +struct da9052_led_platform_data { +#define DA9052_LED_4 4 +#define DA9052_LED_5 5 +#define DA9052_LED_MAX 2 + int id; + const char *name; + const char *default_trigger; +}; + +struct da9052_leds_platform_data { + int num_leds; + struct da9052_led_platform_data *led; +}; + +#endif /* __LINUX_MFD_DA9052_LED_H */ diff --git a/include/linux/mfd/da9052/pm.h b/include/linux/mfd/da9052/pm.h new file mode 100644 index 000000000000..9a1c4f8b4ea8 --- /dev/null +++ b/include/linux/mfd/da9052/pm.h @@ -0,0 +1,81 @@ +/* + * da9052 Power Management module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_PM_H +#define __LINUX_MFD_DA9052_PM_H + +/* PM Device name and static Major number macros */ +#define DRIVER_NAME "da9052-regulator" + +/* PM Device Macros */ +#define DA9052_LDO1 0 +#define DA9052_LDO2 1 +#define DA9052_LDO3 2 +#define DA9052_LDO4 3 +#define DA9052_LDO5 4 +#define DA9052_LDO6 5 +#define DA9052_LDO7 6 +#define DA9052_LDO8 7 +#define DA9052_LDO9 8 +#define DA9052_LDO10 9 +#define DA9052_BUCK_CORE 10 +#define DA9052_BUCK_PRO 11 +#define DA9052_BUCK_MEM 12 +#define DA9052_BUCK_PERI 13 + +/* PM Device Error Codes */ + +/* Buck Config Validation Macros */ +#define DA9052_BUCK_CORE_PRO_VOLT_UPPER 2075 +#define DA9052_BUCK_CORE_PRO_VOLT_LOWER 500 +#define DA9052_BUCK_CORE_PRO_STEP 25 +#define DA9052_BUCK_MEM_VOLT_UPPER 2500 +#define DA9052_BUCK_MEM_VOLT_LOWER 925 +#define DA9052_BUCK_MEM_STEP 25 +#if defined (CONFIG_PMIC_DA9052) +#define DA9052_BUCK_PERI_VOLT_UPPER 3600 +#define DA9052_BUCK_PERI_VOLT_LOWER 1800 +#define DA9052_BUCK_PERI_STEP_BELOW_3000 50 +#define DA9052_BUCK_PERI_STEP_ABOVE_3000 100000 +#define DA9052_BUCK_PERI_VALUES_UPTO_3000 24 +#define DA9052_BUCK_PERI_VALUES_3000 3000000 +#elif defined (CONFIG_PMIC_DA9053AA) || (CONFIG_PMIC_DA9053Bx) +#define DA9052_BUCK_PERI_VOLT_UPPER 2500 +#define DA9052_BUCK_PERI_VOLT_LOWER 925 +#define DA9052_BUCK_PERI_STEP 25 +#endif +#define DA9052_LDO1_VOLT_UPPER 1800 +#define DA9052_LDO1_VOLT_LOWER 600 +#define DA9052_LDO1_VOLT_STEP 50 +#define DA9052_LDO2_VOLT_UPPER 1800 +#define DA9052_LDO2_VOLT_LOWER 600 +#define DA9052_LDO2_VOLT_STEP 25 +#define DA9052_LDO34_VOLT_UPPER 3300 +#define DA9052_LDO34_VOLT_LOWER 1725 +#define DA9052_LDO34_VOLT_STEP 25 +#define DA9052_LDO567810_VOLT_UPPER 3600 +#define DA9052_LDO567810_VOLT_LOWER 1200 +#define DA9052_LDO567810_VOLT_STEP 50 +#define DA9052_LDO9_VOLT_STEP 50 +#define DA9052_LDO9_VOLT_LOWER 1250 +#define DA9052_LDO9_VOLT_UPPER 3650 + +#endif /* __LINUX_MFD_DA9052_PM_H */ diff --git a/include/linux/mfd/da9052/reg.h b/include/linux/mfd/da9052/reg.h new file mode 100644 index 000000000000..9daaa02e565f --- /dev/null +++ b/include/linux/mfd/da9052/reg.h @@ -0,0 +1,929 @@ +/* + * da9052 register declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_REG_H +#define __LINUX_MFD_DA9052_REG_H + +#define DA9052_PAGECON0_REG 0 +#define DA9052_STATUSA_REG 1 +#define DA9052_STATUSB_REG 2 +#define DA9052_STATUSC_REG 3 +#define DA9052_STATUSD_REG 4 +#define DA9052_EVENTA_REG 5 +#define DA9052_EVENTB_REG 6 +#define DA9052_EVENTC_REG 7 +#define DA9052_EVENTD_REG 8 +#define DA9052_FAULTLOG_REG 9 +#define DA9052_IRQMASKA_REG 10 +#define DA9052_IRQMASKB_REG 11 +#define DA9052_IRQMASKC_REG 12 +#define DA9052_IRQMASKD_REG 13 +#define DA9052_CONTROLA_REG 14 +#define DA9052_CONTROLB_REG 15 +#define DA9052_CONTROLC_REG 16 +#define DA9052_CONTROLD_REG 17 +#define DA9052_PDDIS_REG 18 +#define DA9052_INTERFACE_REG 19 +#define DA9052_RESET_REG 20 +#define DA9052_GPIO0001_REG 21 +#define DA9052_GPIO0203_REG 22 +#define DA9052_GPIO0405_REG 23 +#define DA9052_GPIO0607_REG 24 +#define DA9052_GPIO0809_REG 25 +#define DA9052_GPIO1011_REG 26 +#define DA9052_GPIO1213_REG 27 +#define DA9052_GPIO1415_REG 28 +#define DA9052_ID01_REG 29 +#define DA9052_ID23_REG 30 +#define DA9052_ID45_REG 31 +#define DA9052_ID67_REG 32 +#define DA9052_ID89_REG 33 +#define DA9052_ID1011_REG 34 +#define DA9052_ID1213_REG 35 +#define DA9052_ID1415_REG 36 +#define DA9052_ID1617_REG 37 +#define DA9052_ID1819_REG 38 +#define DA9052_ID2021_REG 39 +#define DA9052_SEQSTATUS_REG 40 +#define DA9052_SEQA_REG 41 +#define DA9052_SEQB_REG 42 +#define DA9052_SEQTIMER_REG 43 +#define DA9052_BUCKA_REG 44 +#define DA9052_BUCKB_REG 45 +#define DA9052_BUCKCORE_REG 46 +#define DA9052_BUCKPRO_REG 47 +#define DA9052_BUCKMEM_REG 48 +#define DA9052_BUCKPERI_REG 49 +#define DA9052_LDO1_REG 50 +#define DA9052_LDO2_REG 51 +#define DA9052_LDO3_REG 52 +#define DA9052_LDO4_REG 53 +#define DA9052_LDO5_REG 54 +#define DA9052_LDO6_REG 55 +#define DA9052_LDO7_REG 56 +#define DA9052_LDO8_REG 57 +#define DA9052_LDO9_REG 58 +#define DA9052_LDO10_REG 59 +#define DA9052_SUPPLY_REG 60 +#define DA9052_PULLDOWN_REG 61 +#define DA9052_CHGBUCK_REG 62 +#define DA9052_WAITCONT_REG 63 +#define DA9052_ISET_REG 64 +#define DA9052_BATCHG_REG 65 +#define DA9052_CHGCONT_REG 66 +#define DA9052_INPUTCONT_REG 67 +#define DA9052_CHGTIME_REG 68 +#define DA9052_BBATCONT_REG 69 +#define DA9052_BOOST_REG 70 +#define DA9052_LEDCONT_REG 71 +#define DA9052_LEDMIN123_REG 72 +#define DA9052_LED1CONF_REG 73 +#define DA9052_LED2CONF_REG 74 +#define DA9052_LED3CONF_REG 75 +#define DA9052_LED1CONT_REG 76 +#define DA9052_LED2CONT_REG 77 +#define DA9052_LED3CONT_REG 78 +#define DA9052_LED4CONT_REG 79 +#define DA9052_LED5CONT_REG 80 +#define DA9052_ADCMAN_REG 81 +#define DA9052_ADCCONT_REG 82 +#define DA9052_ADCRESL_REG 83 +#define DA9052_ADCRESH_REG 84 +#define DA9052_VDDRES_REG 85 +#define DA9052_VDDMON_REG 86 +#define DA9052_ICHGAV_REG 87 +#define DA9052_ICHGTHD_REG 88 +#define DA9052_ICHGEND_REG 89 +#define DA9052_TBATRES_REG 90 +#define DA9052_TBATHIGHP_REG 91 +#define DA9052_TBATHIGHIN_REG 92 +#define DA9052_TBATLOW_REG 93 +#define DA9052_TOFFSET_REG 94 +#define DA9052_ADCIN4RES_REG 95 +#define DA9052_AUTO4HIGH_REG 96 +#define DA9052_AUTO4LOW_REG 97 +#define DA9052_ADCIN5RES_REG 98 +#define DA9052_AUTO5HIGH_REG 99 +#define DA9052_AUTO5LOW_REG 100 +#define DA9052_ADCIN6RES_REG 101 +#define DA9052_AUTO6HIGH_REG 102 +#define DA9052_AUTO6LOW_REG 103 +#define DA9052_TJUNCRES_REG 104 +#define DA9052_TSICONTA_REG 105 +#define DA9052_TSICONTB_REG 106 +#define DA9052_TSIXMSB_REG 107 +#define DA9052_TSIYMSB_REG 108 +#define DA9052_TSILSB_REG 109 +#define DA9052_TSIZMSB_REG 110 +#define DA9052_COUNTS_REG 111 +#define DA9052_COUNTMI_REG 112 +#define DA9052_COUNTH_REG 113 +#define DA9052_COUNTD_REG 114 +#define DA9052_COUNTMO_REG 115 +#define DA9052_COUNTY_REG 116 +#define DA9052_ALARMMI_REG 117 +#define DA9052_ALARMH_REG 118 +#define DA9052_ALARMD_REG 119 +#define DA9052_ALARMMO_REG 120 +#define DA9052_ALARMY_REG 121 +#define DA9052_SECONDA_REG 122 +#define DA9052_SECONDB_REG 123 +#define DA9052_SECONDC_REG 124 +#define DA9052_SECONDD_REG 125 +#define DA9052_PAGECON128_REG 128 +#define DA9052_CHIPID_REG 129 +#define DA9052_CONFIGID_REG 130 +#define DA9052_OTPCONT_REG 131 +#define DA9052_OSCTRIM_REG 132 +#define DA9052_GPID0_REG 133 +#define DA9052_GPID1_REG 134 +#define DA9052_GPID2_REG 135 +#define DA9052_GPID3_REG 136 +#define DA9052_GPID4_REG 137 +#define DA9052_GPID5_REG 138 +#define DA9052_GPID6_REG 139 +#define DA9052_GPID7_REG 140 +#define DA9052_GPID8_REG 141 +#define DA9052_GPID9_REG 142 + +#define DA9052_PAGE0_REG_START (DA9052_STATUSA_REG) +#define DA9052_PAGE0_REG_END (DA9052_SECONDD_REG) + +#define DA9052_PAGE1_REG_START (DA9052_CHIPID_REG) +#define DA9052_PAGE1_REG_END (DA9052_GPID9_REG) + +/* PAGE CONFIGURATION */ + +/* Reg Page Configuration */ +#define DA9052_PAGECON0_REGPAGE (1<<7) + +/* PAGE CONFIGURATION 128 REGISTER */ +#define DA9052_PAGECON128_REGPAGE (1<<7) + +/* SYSTEM REGISTER */ + +/* STATUS REGISTER A */ +#define DA9052_STATUSA_VDATDET (1<<7) +#define DA9052_STATUSA_VBUSSEL (1<<6) +#define DA9052_STATUSA_DCINSEL (1<<5) +#define DA9052_STATUSA_VBUSDET (1<<4) +#define DA9052_STATUSA_DCINDET (1<<3) +#define DA9052_STATUSA_IDGND (1<<2) +#define DA9052_STATUSA_IDFLOAT (1<<1) +#define DA9052_STATUSA_NONKEY (1<<0) + + +/* STATUS REGISTER B */ +#define DA9052_STATUSB_COMPDET (1<<7) +#define DA9052_STATUSB_SEQUENCING (1<<6) +#define DA9052_STATUSB_GPFB2 (1<<5) +#define DA9052_STATUSB_CHGTO (1<<4) +#define DA9052_STATUSB_CHGEND (1<<3) +#define DA9052_STATUSB_CHGLIM (1<<2) +#define DA9052_STATUSB_CHGPRE (1<<1) +#define DA9052_STATUSB_CHGATT (1<<0) + + +/* STATUS REGISTER C */ +#define DA9052_STATUSC_GPI7 (1<<7) +#define DA9052_STATUSC_GPI6 (1<<6) +#define DA9052_STATUSC_GPI5 (1<<5) +#define DA9052_STATUSC_GPI4 (1<<4) +#define DA9052_STATUSC_GPI3 (1<<3) +#define DA9052_STATUSC_GPI2 (1<<2) +#define DA9052_STATUSC_GPI1 (1<<1) +#define DA9052_STATUSC_GPI0 (1<<0) + + +/* STATUS REGISTER D */ +#define DA9052_STATUSD_GPI15 (1<<7) +#define DA9052_STATUSD_GPI14 (1<<6) +#define DA9052_STATUSD_GPI13 (1<<5) +#define DA9052_STATUSD_GPI12 (1<<4) +#define DA9052_STATUSD_GPI11 (1<<3) +#define DA9052_STATUSD_GPI10 (1<<2) +#define DA9052_STATUSD_GPI9 (1<<1) +#define DA9052_STATUSD_GPI8 (1<<0) + + +/* EVENT REGISTER A */ +#define DA9052_EVENTA_ECOMP1V2 (1<<7) +#define DA9052_EVENTA_ESEQRDY (1<<6) +#define DA9052_EVENTA_EALRAM (1<<5) +#define DA9052_EVENTA_EVDDLOW (1<<4) +#define DA9052_EVENTA_EVBUSREM (1<<3) +#define DA9052_EVENTA_EDCINREM (1<<2) +#define DA9052_EVENTA_EVBUSDET (1<<1) +#define DA9052_EVENTA_EDCINDET (1<<0) + +/* EVENT REGISTER B */ +#define DA9052_EVENTB_ETSIREADY (1<<7) +#define DA9052_EVENTB_EPENDOWN (1<<6) +#define DA9052_EVENTB_EADCEOM (1<<5) +#define DA9052_EVENTB_ETBAT (1<<4) +#define DA9052_EVENTB_ECHGEND (1<<3) +#define DA9052_EVENTB_EIDGND (1<<2) +#define DA9052_EVENTB_EIDFLOAT (1<<1) +#define DA9052_EVENTB_ENONKEY (1<<0) + +/* EVENT REGISTER C */ +#define DA9052_EVENTC_EGPI7 (1<<7) +#define DA9052_EVENTC_EGPI6 (1<<6) +#define DA9052_EVENTC_EGPI5 (1<<5) +#define DA9052_EVENTC_EGPI4 (1<<4) +#define DA9052_EVENTC_EGPI3 (1<<3) +#define DA9052_EVENTC_EGPI2 (1<<2) +#define DA9052_EVENTC_EGPI1 (1<<1) +#define DA9052_EVENTC_EGPI0 (1<<0) + +/* EVENT REGISTER D */ +#define DA9052_EVENTC_EGPI15 (1<<7) +#define DA9052_EVENTC_EGPI14 (1<<6) +#define DA9052_EVENTC_EGPI13 (1<<5) +#define DA9052_EVENTC_EGPI12 (1<<4) +#define DA9052_EVENTC_EGPI11 (1<<3) +#define DA9052_EVENTC_EGPI10 (1<<2) +#define DA9052_EVENTC_EGPI9 (1<<1) +#define DA9052_EVENTC_EGPI8 (1<<0) + + +/* FAULT LOG REGISTER */ +#define DA9052_FAULTLOG_WAITSET (1<<7) +#define DA9052_FAULTLOG_NSDSET (1<<6) +#define DA9052_FAULTLOG_KEYSHUT (1<<5) +#define DA9052_FAULTLOG_TEMPOVER (1<<3) +#define DA9052_FAULTLOG_VDDSTART (1<<2) +#define DA9052_FAULTLOG_VDDFAULT (1<<1) +#define DA9052_FAULTLOG_TWDERROR (1<<0) + +/* IRQ_MASK REGISTER A */ +#define DA9052_IRQMASKA_MCOMP1V2 (1<<7) +#define DA9052_IRQMASKA_MSEQRDY (1<<6) +#define DA9052_IRQMASKA_MALRAM (1<<5) +#define DA9052_IRQMASKA_MVDDLOW (1<<4) +#define DA9052_IRQMASKA_MVBUSREM (1<<3) +#define DA9052_IRQMASKA_MDCINREM (1<<2) +#define DA9052_IRQMASKA_MVBUSVLD (1<<1) +#define DA9052_IRQMASKA_MDCINVLD (1<<0) + +/* IRQ_MASK REGISTER B */ +#define DA9052_IRQMASKB_MTSIREADY (1<<7) +#define DA9052_IRQMASKB_MPENDOWN (1<<6) +#define DA9052_IRQMASKB_MADCEOM (1<<5) +#define DA9052_IRQMASKB_MTBAT (1<<4) +#define DA9052_IRQMASKB_MCHGEND (1<<3) +#define DA9052_IRQMASKB_MIDGND (1<<2) +#define DA9052_IRQMASKB_MIDFLOAT (1<<1) +#define DA9052_IRQMASKB_MNONKEY (1<<0) + +/* IRQ_MASK REGISTER C */ +#define DA9052_IRQMASKC_MGPI7 (1<<7) +#define DA9052_IRQMASKC_MGPI6 (1<<6) +#define DA9052_IRQMASKC_MGPI5 (1<<5) +#define DA9052_IRQMASKC_MGPI4 (1<<4) +#define DA9052_IRQMASKC_MGPI3 (1<<3) +#define DA9052_IRQMASKC_MGPI2 (1<<2) +#define DA9052_IRQMASKC_MGPI1 (1<<1) +#define DA9052_IRQMASKC_MGPI0 (1<<0) + +/* IRQ_MASK REGISTER D */ +#define DA9052_IRQMASKD_MGPI15 (1<<7) +#define DA9052_IRQMASKD_MGPI14 (1<<6) +#define DA9052_IRQMASKD_MGPI13 (1<<5) +#define DA9052_IRQMASKD_MGPI12 (1<<4) +#define DA9052_IRQMASKD_MGPI11 (1<<3) +#define DA9052_IRQMASKD_MGPI10 (1<<2) +#define DA9052_IRQMASKD_MGPI9 (1<<1) +#define DA9052_IRQMASKD_MGPI8 (1<<0) + +/* CONTROL REGISTER A */ +#define DA9052_CONTROLA_GPIV (1<<7) +#define DA9052_CONTROLA_PMOTYPE (1<<5) +#define DA9052_CONTROLA_PMOV (1<<4) +#define DA9052_CONTROLA_PMIV (1<<3) +#define DA9052_CONTROLA_PMIFV (1<<3) +#define DA9052_CONTROLA_PWR1EN (1<<2) +#define DA9052_CONTROLA_PWREN (1<<1) +#define DA9052_CONTROLA_SYSEN (1<<0) + +/* CONTROL REGISTER B */ +#define DA9052_CONTROLB_SHUTDOWN (1<<7) +#define DA9052_CONTROLB_DEEPSLEEP (1<<6) +#define DA9052_CONTROLB_WRITEMODE (1<<5) +#define DA9052_CONTROLB_BBATEN (1<<4) +#define DA9052_CONTROLB_OTPREADEN (1<<3) +#define DA9052_CONTROLB_AUTOBOOT (1<<2) +#define DA9052_CONTROLB_ACTDIODE (1<<1) +#define DA9052_CONTROLB_BUCKMERGE (1<<0) + +/* CONTROL REGISTER C */ +#define DA9052_CONTROLC_BLINKDUR (1<<7) +#define DA9052_CONTROLC_BLINKFRQ (3<<5) +#define DA9052_CONTROLC_DEBOUNCING (7<<2) +#define DA9052_CONTROLC_PMFB2PIN (1<<1) +#define DA9052_CONTROLC_PMFB1PIN (1<<0) + +/* CONTROL REGISTER D */ +#define DA9052_CONTROLD_WATCHDOG (1<<7) +#define DA9052_CONTROLD_ACCDETEN (1<<6) +#define DA9052_CONTROLD_GPI1415SD (1<<5) +#define DA9052_CONTROLD_NONKEYSD (1<<4) +#define DA9052_CONTROLD_KEEPACTEN (1<<3) +#define DA9052_CONTROLD_TWDSCALE (7<<0) + +/* POWER DOWN DISABLE REGISTER */ +#define DA9052_PDDIS_PMCONTPD (1<<7) +#define DA9052_PDDIS_OUT32KPD (1<<6) +#define DA9052_PDDIS_CHGBBATPD (1<<5) +#define DA9052_PDDIS_CHGPD (1<<4) +#define DA9052_PDDIS_HS2WIREPD (1<<3) +#define DA9052_PDDIS_PMIFPD (1<<2) +#define DA9052_PDDIS_GPADCPD (1<<1) +#define DA9052_PDDIS_GPIOPD (1<<0) + +/* CONTROL REGISTER D */ +#define DA9052_INTERFACE_IFBASEADDR (7<<5) +#define DA9052_INTERFACE_NCSPOL (1<<4) +#define DA9052_INTERFACE_RWPOL (1<<3) +#define DA9052_INTERFACE_CPHA (1<<2) +#define DA9052_INTERFACE_CPOL (1<<1) +#define DA9052_INTERFACE_IFTYPE (1<<0) + +/* CONTROL REGISTER D */ +#define DA9052_RESET_RESETEVENT (3<<6) +#define DA9052_RESET_RESETTIMER (63<<0) + +/* GPIO REGISTERS */ + +/* GPIO control register for PIN 0 and 1 */ +#define DA9052_GPIO0001_GPIO1MODE (1<<7) +#define DA9052_GPIO0001_GPIO1TYPE (1<<6) +#define DA9052_GPIO0001_GPIO1PIN (3<<4) +#define DA9052_GPIO0001_GPIO0MODE (1<<3) +#define DA9052_GPIO0001_GPIO0TYPE (1<<2) +#define DA9052_GPIO0001_GPIO0PIN (3<<0) + +/* GPIO control register for PIN 2 and 3 */ +#define DA9052_GPIO0203_GPIO3MODE (1<<7) +#define DA9052_GPIO0203_GPIO3TYPE (1<<6) +#define DA9052_GPIO0203_GPIO3PIN (3<<4) +#define DA9052_GPIO0203_GPIO2MODE (1<<3) +#define DA9052_GPIO0203_GPIO2TYPE (1<<2) +#define DA9052_GPIO0203_GPIO2PIN (3<<0) + +/* GPIO control register for PIN 4 and 5 */ +#define DA9052_GPIO0405_GPIO5MODE (1<<7) +#define DA9052_GPIO0405_GPIO5TYPE (1<<6) +#define DA9052_GPIO0405_GPIO5PIN (3<<4) +#define DA9052_GPIO0405_GPIO4MODE (1<<3) +#define DA9052_GPIO0405_GPIO4TYPE (1<<2) +#define DA9052_GPIO0405_GPIO4PIN (3<<0) + +/* GPIO control register for PIN 6 and 7 */ +#define DA9052_GPIO0607_GPIO7MODE (1<<7) +#define DA9052_GPIO0607_GPIO7TYPE (1<<6) +#define DA9052_GPIO0607_GPIO7PIN (3<<4) +#define DA9052_GPIO0607_GPIO6MODE (1<<3) +#define DA9052_GPIO0607_GPIO6TYPE (1<<2) +#define DA9052_GPIO0607_GPIO6PIN (3<<0) + +/* GPIO control register for PIN 8 and 9 */ +#define DA9052_GPIO0809_GPIO9MODE (1<<7) +#define DA9052_GPIO0809_GPIO9TYPE (1<<6) +#define DA9052_GPIO0809_GPIO9PIN (3<<4) +#define DA9052_GPIO0809_GPIO8MODE (1<<3) +#define DA9052_GPIO0809_GPIO8TYPE (1<<2) +#define DA9052_GPIO0809_GPIO8PIN (3<<0) + +/* GPIO control register for PIN 10 and 11 */ +#define DA9052_GPIO1011_GPIO11MODE (1<<7) +#define DA9052_GPIO1011_GPIO11TYPE (1<<6) +#define DA9052_GPIO1011_GPIO11PIN (3<<4) +#define DA9052_GPIO1011_GPIO10MODE (1<<3) +#define DA9052_GPIO1011_GPIO10TYPE (1<<2) +#define DA9052_GPIO1011_GPIO10PIN (3<<0) + +/* GPIO control register for PIN 12 and 13 */ +#define DA9052_GPIO1213_GPIO13MODE (1<<7) +#define DA9052_GPIO1213_GPIO13TYPE (1<<6) +#define DA9052_GPIO1213_GPIO13PIN (3<<4) +#define DA9052_GPIO1213_GPIO12MODE (1<<3) +#define DA9052_GPIO1213_GPIO12TYPE (1<<2) +#define DA9052_GPIO1213_GPIO12PIN (3<<0) + +/* GPIO control register for PIN 14 and 15 */ +#define DA9052_GPIO1415_GPIO15MODE (1<<7) +#define DA9052_GPIO1415_GPIO15TYPE (1<<6) +#define DA9052_GPIO1415_GPIO15PIN (3<<4) +#define DA9052_GPIO1415_GPIO14MODE (1<<3) +#define DA9052_GPIO1415_GPIO14TYPE (1<<2) +#define DA9052_GPIO1415_GPIO14PIN (3<<0) + +/*POWER SEQUENCER REGISTER*/ + +/* SEQ control register for ID 0 and 1 */ +#define DA9052_ID01_LDO1STEP (15<<4) +#define DA9052_ID01_SYSPRE (1<<2) +#define DA9052_ID01_DEFSUPPLY (1<<1) +#define DA9052_ID01_nRESMODE (1<<0) + +/* SEQ control register for ID 2 and 3 */ +#define DA9052_ID23_LDO3STEP (15<<4) +#define DA9052_ID23_LDO2STEP (15<<0) + +/* SEQ control register for ID 4 and 5 */ +#define DA9052_ID45_LDO5STEP (15<<4) +#define DA9052_ID45_LDO4STEP (15<<0) + +/* SEQ control register for ID 6 and 7 */ +#define DA9052_ID67_LDO7STEP (15<<4) +#define DA9052_ID67_LDO6STEP (15<<0) + +/* SEQ control register for ID 8 and 9 */ +#define DA9052_ID89_LDO9STEP (15<<4) +#define DA9052_ID89_LDO8STEP (15<<0) + +/* SEQ control register for ID 10 and 11 */ +#define DA9052_ID1011_PDDISSTEP (15<<4) +#define DA9052_ID1011_LDO10STEP (15<<0) + +/* SEQ control register for ID 12 and 13 */ +#define DA9052_ID1213_VMEMSWSTEP (15<<4) +#define DA9052_ID1213_VPERISWSTEP (15<<0) + +/* SEQ control register for ID 14 and 15 */ +#define DA9052_ID1415_BUCKPROSTEP (15<<4) +#define DA9052_ID1415_BUCKCORESTEP (15<<0) + +/* SEQ control register for ID 16 and 17 */ +#define DA9052_ID1617_BUCKPERISTEP (15<<4) +#define DA9052_ID1617_BUCKMEMSTEP (15<<0) + +/* SEQ control register for ID 18 and 19 */ +#define DA9052_ID1819_GPRISE2STEP (15<<4) +#define DA9052_ID1819_GPRISE1STEP (15<<0) + +/* SEQ control register for ID 20 and 21 */ +#define DA9052_ID2021_GPFALL2STEP (15<<4) +#define DA9052_ID2021_GPFALL1STEP (15<<0) + +/* Power SEQ Status register */ +#define DA9052_SEQSTATUS_SEQPOINTER (15<<4) +#define DA9052_SEQSTATUS_WAITSTEP (15<<0) + +/* Power SEQ A register */ +#define DA9052_SEQA_POWEREND (15<<4) +#define DA9052_SEQA_SYSTEMEND (15<<0) + +/* Power SEQ B register */ +#define DA9052_SEQB_PARTDOWN (15<<4) +#define DA9052_SEQB_MAXCOUNT (15<<0) + +/* Power SEQ TIMER register */ +#define DA9052_SEQTIMER_SEQDUMMY (15<<4) +#define DA9052_SEQTIMER_SEQTIME (15<<0) + +/*POWER SUPPLY CONTROL REGISTER*/ + +/* BUCK REGISTER A */ +#define DA9052_BUCKA_BPROILIM (3<<6) +#define DA9052_BUCKA_BPROMODE (3<<4) +#define DA9052_BUCKA_BCOREILIM (3<<2) +#define DA9052_BUCKA_BCOREMODE (3<<0) + +/* BUCK REGISTER B */ +#define DA9052_BUCKB_BERIILIM (3<<6) +#define DA9052_BUCKB_BPERIMODE (3<<4) +#define DA9052_BUCKB_BMEMILIM (3<<2) +#define DA9052_BUCKB_BMEMMODE (3<<0) + +/* BUCKCORE REGISTER */ +#define DA9052_BUCKCORE_BCORECONF (1<<7) +#define DA9052_BUCKCORE_BCOREEN (1<<6) +#define DA9052_BUCKCORE_VBCORE (63<<0) + +/* BUCKPRO REGISTER */ +#define DA9052_BUCKPRO_BPROCONF (1<<7) +#define DA9052_BUCKPRO_BPROEN (1<<6) +#define DA9052_BUCKPRO_VBPRO (63<<0) + +/* BUCKMEM REGISTER */ +#define DA9052_BUCKMEM_BMEMCONF (1<<7) +#define DA9052_BUCKMEM_BMEMEN (1<<6) +#define DA9052_BUCKMEM_VBMEM (63<<0) + +/* BUCKPERI REGISTER */ +#define DA9052_BUCKPERI_BPERICONF (1<<7) +#define DA9052_BUCKPERI_BPERIEN (1<<6) +#if defined (CONFIG_PMIC_DA9052) +#define DA9052_BUCKPERI_BPERIHS (1<<5) +#define DA9052_BUCKPERI_VBPERI (31<<0) +#elif defined (CONFIG_PMIC_DA9053AA) || (CONFIG_PMIC_DA9053Bx) +#define DA9052_BUCKPERI_VBPERI (63<<0) +#endif + +/* LDO1 REGISTER */ +#define DA9052_LDO1_LDO1CONF (1<<7) +#define DA9052_LDO1_LDO1EN (1<<6) +#define DA9052_LDO1_VLDO1 (31<<0) + +/* LDO2 REGISTER */ +#define DA9052_LDO2_LDO2CONF (1<<7) +#define DA9052_LDO2_LDO2EN (1<<6) +#define DA9052_LDO2_VLDO2 (63<<0) + +/* LDO3 REGISTER */ +#define DA9052_LDO3_LDO3CONF (1<<7) +#define DA9052_LDO3_LDO3EN (1<<6) +#define DA9052_LDO3_VLDO3 (63<<0) + +/* LDO4 REGISTER */ +#define DA9052_LDO4_LDO4CONF (1<<7) +#define DA9052_LDO4_LDO4EN (1<<6) +#define DA9052_LDO4_VLDO4 (63<<0) + +/* LDO5 REGISTER */ +#define DA9052_LDO5_LDO5CONF (1<<7) +#define DA9052_LDO5_LDO5EN (1<<6) +#define DA9052_LDO5_VLDO5 (63<<0) + +/* LDO6 REGISTER */ +#define DA9052_LDO6_LDO6CONF (1<<7) +#define DA9052_LDO6_LDO6EN (1<<6) +#define DA9052_LDO6_VLDO6 (63<<0) + +/* LDO7 REGISTER */ +#define DA9052_LDO7_LDO7CONF (1<<7) +#define DA9052_LDO7_LDO7EN (1<<6) +#define DA9052_LDO7_VLDO7 (63<<0) + +/* LDO8 REGISTER */ +#define DA9052_LDO8_LDO8CONF (1<<7) +#define DA9052_LDO8_LDO8EN (1<<6) +#define DA9052_LDO8_VLDO8 (63<<0) + +/* LDO9 REGISTER */ +#define DA9052_LDO9_LDO9CONF (1<<7) +#define DA9052_LDO9_LDO9EN (1<<6) +#define DA9052_LDO9_VLDO9 (63<<0) + +/* LDO10 REGISTER */ +#define DA9052_LDO10_LDO10CONF (1<<7) +#define DA9052_LDO10_LDO10EN (1<<6) +#define DA9052_LDO10_VLDO10 (63<<0) + +/* SUPPLY REGISTER */ +#define DA9052_SUPPLY_VLOCK (1<<7) +#define DA9052_SUPPLY_VMEMSWEN (1<<6) +#define DA9052_SUPPLY_VPERISWEN (1<<5) +#define DA9052_SUPPLY_VLDO3GO (1<<4) +#define DA9052_SUPPLY_VLDO2GO (1<<3) +#define DA9052_SUPPLY_VBMEMGO (1<<2) +#define DA9052_SUPPLY_VBPROGO (1<<1) +#define DA9052_SUPPLY_VBCOREGO (1<<0) + +/* PULLDOWN REGISTER */ +#define DA9052_PULLDOWN_LDO5PDDIS (1<<5) +#define DA9052_PULLDOWN_LDO2PDDIS (1<<4) +#define DA9052_PULLDOWN_LDO1PDDIS (1<<3) +#define DA9052_PULLDOWN_MEMPDDIS (1<<2) +#define DA9052_PULLDOWN_PROPDDIS (1<<1) +#define DA9052_PULLDOWN_COREPDDIS (1<<0) + +/* BAT CHARGER REGISTER */ + +/* CHARGER BUCK REGISTER */ +#define DA9052_CHGBUCK_CHGTEMP (1<<7) +#define DA9052_CHGBUCK_CHGUSBILIM (1<<6) +#define DA9052_CHGBUCK_CHGBUCKLP (1<<5) +#define DA9052_CHGBUCK_CHGBUCKEN (1<<4) +#define DA9052_CHGBUCK_ISETBUCK (15<<0) + +/* WAIT COUNTER REGISTER */ +#define DA9052_WAITCONT_WAITDIR (1<<7) +#define DA9052_WAITCONT_RTCCLOCK (1<<6) +#define DA9052_WAITCONT_WAITMODE (1<<5) +#define DA9052_WAITCONT_EN32KOUT (1<<4) +#define DA9052_WAITCONT_DELAYTIME (15<<0) + +/* ISET CONTROL REGISTER */ +#define DA9052_ISET_ISETDCIN (15<<4) +#define DA9052_ISET_ISETVBUS (15<<0) + +/* BATTERY CHARGER CONTROL REGISTER */ +#define DA9052_BATCHG_ICHGPRE (3<<6) +#define DA9052_BATCHG_ICHGBAT (63<<0) + +/* CHARGER COUNTER REGISTER */ +#define DA9052_CHGCONT_VCHGBAT (31<<3) +#define DA9052_CHGCONT_TCTR (7<<0) + +/* INPUT CONTROL REGISTER */ +#define DA9052_INPUTCONT_TCTRMODE (1<<7) +#define DA9052_INPUTCONT_ICHGLOW (1<<5) +#define DA9052_INPUTCONT_VBUSSUSP (1<<4) +#define DA9052_INPUTCONT_DCINSUSP (1<<3) +#define DA9052_INPUTCONT_VCHGTHR (7<<0) + +/* CHARGING TIME REGISTER */ +#define DA9052_CHGTIME_CHGTIME (255<<0) + +/* BACKUP BATTERY CONTROL REGISTER */ +#define DA9052_BBATCONT_BCHARGERISET (15<<4) +#define DA9052_BBATCONT_BCHARGERVSET (15<<0) + +/* LED REGISTERS */ + +/* LED BOOST REGISTER */ +#define DA9052_BOOST_EBFAULT (1<<7) +#define DA9052_BOOST_MBFAULT (1<<6) +#define DA9052_BOOST_BOOSTFRQ (1<<5) +#define DA9052_BOOST_BOOSTILIM (1<<4) +#define DA9052_BOOST_LED3INEN (1<<3) +#define DA9052_BOOST_LED2INEN (1<<2) +#define DA9052_BOOST_LED1INEN (1<<1) +#define DA9052_BOOST_BOOSTEN (1<<0) + +/* LED COUNT REGISTER */ +#if defined (CONFIG_PMIC_DA9053Bx) +#define DA9052_LEDCONT_SELLEDMODE (1<<7) +#endif +#define DA9052_LEDCONT_LED3ICONT (1<<6) +#define DA9052_LEDCONT_LED3RAMP (1<<5) +#define DA9052_LEDCONT_LED3EN (1<<4) +#define DA9052_LEDCONT_LED2RAMP (1<<3) +#define DA9052_LEDCONT_LED2EN (1<<2) +#define DA9052_LEDCONT_LED1RAMP (1<<1) +#define DA9052_LEDCONT_LED1EN (1<<0) + +/* LEDMIN123 REGISTER */ +#define DA9052_LEDMIN123_LEDMINCURRENT (255<<0) + +/* LED1CONF REGISTER */ +#define DA9052_LED1CONF_LED1CURRENT (255<<0) + +/* LED2CONF REGISTER */ +#define DA9052_LED2CONF_LED2CURRENT (255<<0) + +/* LED3CONF REGISTER */ +#define DA9052_LED3CONF_LED3CURRENT (255<<0) + +/* LED1 COUNT REGISTER */ +#define DA9052_LED1CONT_LED1DIM (1<<7) +#define DA9052_LED1CONT_LED1PWM (127<<0) + +/* LED2 COUNT REGISTER */ +#define DA9052_LED2CONT_LED2DIM (1<<7) +#define DA9052_LED2CONT_LED2PWM (127<<0) + +/* LED3 COUNT REGISTER */ +#define DA9052_LED3CONT_LED3DIM (1<<7) +#define DA9052_LED3CONT_LED3PWM (127<<0) + +/* LED4 COUNT REGISTER */ +#define DA9052_LED4CONT_LED4DIM (1<<7) +#define DA9052_LED4CONT_LED4PWM (127<<0) + +/* LED5 COUNT REGISTER */ +#define DA9052_LED5CONT_LED5DIM (1<<7) +#define DA9052_LED5CONT_LED5PWM (127<<0) + +/* ADC REGISTERS */ + +/* ADC MAN registers */ +#define DA9052_ADCMAN_MANCONV (1<<4) +#define DA9052_ADCMAN_MUXSEL (15<<0) + +/* ADC COUNT regsisters */ +#define DA9052_ADCCONT_COMP1V2EN (1<<7) +#define DA9052_ADCCONT_ADCMODE (1<<6) +#define DA9052_ADCCONT_TBATISRCEN (1<<5) +#define DA9052_ADCCONT_AD4ISRCEN (1<<4) +#define DA9052_ADCCONT_AUTOAD6EN (1<<3) +#define DA9052_ADCCONT_AUTOAD5EN (1<<2) +#define DA9052_ADCCONT_AUTOAD4EN (1<<1) +#define DA9052_ADCCONT_AUTOVDDEN (1<<0) + +/* ADC 10 BIT MANUAL CONVERSION RESULT LOW register */ +#define DA9052_ADCRESL_ADCRESLSB (3<<0) + +/* ADC 10 BIT MANUAL CONVERSION RESULT HIGH register */ +#define DA9052_ADCRESH_ADCRESMSB (255<<0) + +/* VDD RES regsister*/ +#define DA9052_VDDRES_VDDOUTRES (255<<0) + +/* VDD MON regsister*/ +#define DA9052_VDDMON_VDDOUTMON (255<<0) + +/* ICHG_AV regsister*/ +#define DA9052_ICHGAV_ICHGAV (255<<0) + +/* ICHG_THD regsister*/ +#define DA9052_ICHGTHD_ICHGTHD (255<<0) + +/* ICHG_END regsister*/ +#define DA9052_ICHGEND_ICHGEND (255<<0) + +/* TBAT_RES regsister*/ +#define DA9052_TBATRES_TBATRES (255<<0) + +/* TBAT_HIGHP regsister*/ +#define DA9052_TBATHIGHP_TBATHIGHP (255<<0) + +/* TBAT_HIGHN regsister*/ +#define DA9052_TBATHIGHN_TBATHIGHN (255<<0) + +/* TBAT_LOW regsister*/ +#define DA9052_TBATLOW_TBATLOW (255<<0) + +/* T_OFFSET regsister*/ +#define DA9052_TOFFSET_TOFFSET (255<<0) + +/* ADCIN4_RES regsister*/ +#define DA9052_ADCIN4RES_ADCIN4RES (255<<0) + +/* ADCIN4_HIGH regsister*/ +#define DA9052_AUTO4HIGH_AUTO4HIGH (255<<0) + +/* ADCIN4_LOW regsister*/ +#define DA9052_AUTO4LOW_AUTO4LOW (255<<0) + +/* ADCIN5_RES regsister*/ +#define DA9052_ADCIN5RES_ADCIN5RES (255<<0) + +/* ADCIN5_HIGH regsister*/ +#define DA9052_AUTO5HIGH_AUTOHIGH (255<<0) + +/* ADCIN5_LOW regsister*/ +#define DA9052_AUTO5LOW_AUTO5LOW (255<<0) + +/* ADCIN6_RES regsister*/ +#define DA9052_ADCIN6RES_ADCIN6RES (255<<0) + +/* ADCIN6_HIGH regsister*/ +#define DA9052_AUTO6HIGH_AUTO6HIGH (255<<0) + +/* ADCIN6_LOW regsister*/ +#define DA9052_AUTO6LOW_AUTO6LOW (255<<0) + +/* TJUNC_RES regsister*/ +#define DA9052_TJUNCRES_TJUNCRES (255<<0) + +/* TSI REGISTER */ + +/* TSI Control Register A */ +#define DA9052_TSICONTA_TSIDELAY (3<<6) +#define DA9052_TSICONTA_TSISKIP (7<<3) +#define DA9052_TSICONTA_TSIMODE (1<<2) +#define DA9052_TSICONTA_PENDETEN (1<<1) +#define DA9052_TSICONTA_AUTOTSIEN (1<<0) + +/* TSI Control Register B */ +#define DA9052_TSICONTB_ADCREF (1<<7) +#define DA9052_TSICONTB_TSIMAN (1<<6) +#define DA9052_TSICONTB_TSIMUX (3<<4) +#define DA9052_TSICONTB_TSISEL3 (1<<3) +#define DA9052_TSICONTB_TSISEL2 (1<<2) +#define DA9052_TSICONTB_TSISEL1 (1<<1) +#define DA9052_TSICONTB_TSISEL0 (1<<0) + +/* TSI X Co-ordinate MSB Result register */ +#define DA9052_TSIXMSB_TSIXM (255<<0) + +/* TSI Y Co-ordinate MSB Result register */ +#define DA9052_TSIYMSB_TSIYM (255<<0) + +/* TSI Co-ordinate LSB Result register */ +#define DA9052_TSILSB_PENDOWN (1<<6) +#define DA9052_TSILSB_TSIZL (3<<4) +#define DA9052_TSILSB_TSIYL (3<<2) +#define DA9052_TSILSB_TSIXL (3<<0) + +/* TSI Z Measurement MSB Result register */ +#define DA9052_TSIZMSB_TSIZM (255<<0) + +/* RTC REGISTER */ + +/* RTC TIMER SECONDS REGISTER */ +#define DA9052_COUNTS_MONITOR (1<<6) +#define DA9052_COUNTS_COUNTSEC (63<<0) + +/* RTC TIMER MINUTES REGISTER */ +#define DA9052_COUNTMI_COUNTMIN (63<<0) + +/* RTC TIMER HOUR REGISTER */ +#define DA9052_COUNTH_COUNTHOUR (31<<0) + +/* RTC TIMER DAYS REGISTER */ +#define DA9052_COUNTD_COUNTDAY (31<<0) + +/* RTC TIMER MONTHS REGISTER */ +#define DA9052_COUNTMO_COUNTMONTH (15<<0) + +/* RTC TIMER YEARS REGISTER */ +#define DA9052_COUNTY_COUNTYEAR (63<<0) + +/* RTC ALARM MINUTES REGISTER */ +#define DA9052_ALARMMI_TICKTYPE (1<<7) +#define DA9052_ALARMMI_ALARMTYPE (1<<6) +#define DA9052_ALARMMI_ALARMMIN (63<<0) + +/* RTC ALARM HOURS REGISTER */ +#define DA9052_ALARMH_ALARMHOUR (31<<0) + +/* RTC ALARM DAYS REGISTER */ +#define DA9052_ALARMD_ALARMDAY (31<<0) + +/* RTC ALARM MONTHS REGISTER */ +#define DA9052_ALARMMO_ALARMMONTH (15<<0) + +/* RTC ALARM YEARS REGISTER */ +#define DA9052_ALARMY_TICKON (1<<7) +#define DA9052_ALARMY_ALARMON (1<<6) +#define DA9052_ALARMY_ALARMYEAR (63<<0) + +/* RTC SECONDS REGISTER A*/ +#define DA9052_SECONDA_SECONDSA (255<<0) + +/* RTC SECONDS REGISTER B*/ +#define DA9052_SECONDB_SECONDSB (255<<0) + +/* RTC SECONDS REGISTER C*/ +#define DA9052_SECONDC_SECONDSC (255<<0) + +/* RTC SECONDS REGISTER D*/ +#define DA9052_SECONDD_SECONDSD (255<<0) + +/* OTP REGISTER */ + +/* CHIP IDENTIFICATION REGISTER */ +#define DA9052_CHIPID_MRC (15<<4) +#define DA9052_CHIPID_TRC (15<<0) + +/* CONFIGURATION IDENTIFICATION REGISTER */ +#define DA9052_CONFIGID_CUSTOMERID (31<<3) +#define DA9052_CONFIGID_CONFID (7<<0) + +/* OTP CONTROL REGISTER */ +#define DA9052_OTPCONT_GPWRITEDIS (1<<7) +#define DA9052_OTPCONT_OTPCONFLOCK (1<<6) +#define DA9052_OTPCONT_OTPGPLOCK (1<<5) +#define DA9052_OTPCONT_OTPCONFG (1<<3) +#define DA9052_OTPCONT_OTPGP (1<<2) +#define DA9052_OTPCONT_OTPRP (1<<1) +#define DA9052_OTPCONT_OTPTRANSFER (1<<0) + +/* RTC OSCILLATOR TRIM REGISTER */ +#define DA9052_OSCTRIM_TRIM32K (255<<0) + +/* GP ID REGISTER 0 */ +#define DA9052_GPID0_GP0 (255<<0) + +/* GP ID REGISTER 1 */ +#define DA9052_GPID1_GP1 (255<<0) + +/* GP ID REGISTER 2 */ +#define DA9052_GPID2_GP2 (255<<0) + +/* GP ID REGISTER 3 */ +#define DA9052_GPID3_GP3 (255<<0) + +/* GP ID REGISTER 4 */ +#define DA9052_GPID4_GP4 (255<<0) + +/* GP ID REGISTER 5 */ +#define DA9052_GPID5_GP5 (255<<0) + +/* GP ID REGISTER 6 */ +#define DA9052_GPID6_GP6 (255<<0) + +/* GP ID REGISTER 7 */ +#define DA9052_GPID7_GP7 (255<<0) + +/* GP ID REGISTER 8 */ +#define DA9052_GPID8_GP8 (255<<0) + +/* GP ID REGISTER 9 */ +#define DA9052_GPID9_GP9 (255<<0) + +#endif +/* __LINUX_MFD_DA9052_REG_H */ diff --git a/include/linux/mfd/da9052/rtc.h b/include/linux/mfd/da9052/rtc.h new file mode 100644 index 000000000000..c7fc87dd7eb2 --- /dev/null +++ b/include/linux/mfd/da9052/rtc.h @@ -0,0 +1,322 @@ +/* + * da9052 RTC module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_RTC_H +#define __LINUX_MFD_DA9052_RTC_H + +#define DA9052_RTC_DEVICE_NAME "da9052_rtc" + +/* Limit values */ +#define DA9052_RTC_SECONDS_LIMIT 59 +#define DA9052_RTC_MINUTES_LIMIT 59 +#define DA9052_RTC_HOURS_LIMIT 23 +#define DA9052_RTC_DAYS_LIMIT 31 +#define DA9052_RTC_MONTHS_LIMIT 12 +#define DA9052_RTC_YEARS_LIMIT 63 + +/* Months */ +#define FEBRUARY 2 +#define APRIL 4 +#define JUNE 6 +#define SEPTEMBER 9 +#define NOVEMBER 11 + +/* BYTE shifts */ +#define DA9052_RTC_FOURTH_BYTE 24 +#define DA9052_RTC_THIRD_BYTE 16 +#define DA9052_RTC_SECOND_BYTE 8 +#define DA9052_RTC_FIRST_BYTE 0 + +/* Oscillator trim values */ +#define DA9052_RTC_OSC_FRQ_0_0ppm 0 +#define DA9052_RTC_OSC_FRQ_1_9ppm 1 +#define DA9052_RTC_OSC_FRQ_3_8ppm 2 +#define DA9052_RTC_OSC_FRQ_5_7ppm 3 +#define DA9052_RTC_OSC_FRQ_7_6ppm 4 +#define DA9052_RTC_OSC_FRQ_9_5ppm 5 +#define DA9052_RTC_OSC_FRQ_11_4ppm 6 +#define DA9052_RTC_OSC_FRQ_13_3ppm 7 +#define DA9052_RTC_OSC_FRQ_15_2ppm 8 +#define DA9052_RTC_OSC_FRQ_17_1ppm 9 +#define DA9052_RTC_OSC_FRQ_19_0ppm 10 +#define DA9052_RTC_OSC_FRQ_20_9ppm 11 +#define DA9052_RTC_OSC_FRQ_22_8ppm 12 +#define DA9052_RTC_OSC_FRQ_24_7ppm 13 +#define DA9052_RTC_OSC_FRQ_26_7ppm 14 +#define DA9052_RTC_OSC_FRQ_28_6ppm 15 +#define DA9052_RTC_OSC_FRQ_30_5ppm 16 +#define DA9052_RTC_OSC_FRQ_32_4ppm 17 +#define DA9052_RTC_OSC_FRQ_34_3ppm 18 +#define DA9052_RTC_OSC_FRQ_36_2ppm 19 +#define DA9052_RTC_OSC_FRQ_38_1ppm 20 +#define DA9052_RTC_OSC_FRQ_40_0ppm 21 +#define DA9052_RTC_OSC_FRQ_41_9ppm 22 +#define DA9052_RTC_OSC_FRQ_43_8ppm 23 +#define DA9052_RTC_OSC_FRQ_45_7ppm 24 +#define DA9052_RTC_OSC_FRQ_47_6ppm 25 +#define DA9052_RTC_OSC_FRQ_49_5ppm 26 +#define DA9052_RTC_OSC_FRQ_51_4ppm 27 +#define DA9052_RTC_OSC_FRQ_53_4ppm 28 +#define DA9052_RTC_OSC_FRQ_55_3ppm 29 +#define DA9052_RTC_OSC_FRQ_57_2ppm 30 +#define DA9052_RTC_OSC_FRQ_59_1ppm 31 +#define DA9052_RTC_OSC_FRQ_61_0ppm 32 +#define DA9052_RTC_OSC_FRQ_62_9ppm 33 +#define DA9052_RTC_OSC_FRQ_64_8ppm 34 +#define DA9052_RTC_OSC_FRQ_66_7ppm 35 +#define DA9052_RTC_OSC_FRQ_68_6ppm 36 +#define DA9052_RTC_OSC_FRQ_70_5ppm 37 +#define DA9052_RTC_OSC_FRQ_72_4ppm 38 +#define DA9052_RTC_OSC_FRQ_74_3ppm 39 +#define DA9052_RTC_OSC_FRQ_76_2ppm 40 +#define DA9052_RTC_OSC_FRQ_78_2ppm 41 +#define DA9052_RTC_OSC_FRQ_80_1ppm 42 +#define DA9052_RTC_OSC_FRQ_82_0ppm 43 +#define DA9052_RTC_OSC_FRQ_83_9ppm 44 +#define DA9052_RTC_OSC_FRQ_85_8ppm 45 +#define DA9052_RTC_OSC_FRQ_87_7ppm 46 +#define DA9052_RTC_OSC_FRQ_89_6ppm 47 +#define DA9052_RTC_OSC_FRQ_91_5ppm 48 +#define DA9052_RTC_OSC_FRQ_93_4ppm 49 +#define DA9052_RTC_OSC_FRQ_95_3ppm 50 +#define DA9052_RTC_OSC_FRQ_97_2ppm 51 +#define DA9052_RTC_OSC_FRQ_99_1ppm 52 +#define DA9052_RTC_OSC_FRQ_101_0ppm 53 +#define DA9052_RTC_OSC_FRQ_102_9ppm 54 +#define DA9052_RTC_OSC_FRQ_104_9ppm 55 +#define DA9052_RTC_OSC_FRQ_106_8ppm 56 +#define DA9052_RTC_OSC_FRQ_108_7ppm 57 +#define DA9052_RTC_OSC_FRQ_110_6ppm 58 +#define DA9052_RTC_OSC_FRQ_112_5ppm 59 +#define DA9052_RTC_OSC_FRQ_114_4ppm 60 +#define DA9052_RTC_OSC_FRQ_116_3ppm 61 +#define DA9052_RTC_OSC_FRQ_118_2ppm 62 +#define DA9052_RTC_OSC_FRQ_120_1ppm 63 +#define DA9052_RTC_OSC_FRQ_122_0ppm 64 +#define DA9052_RTC_OSC_FRQ_123_9ppm 65 +#define DA9052_RTC_OSC_FRQ_125_8ppm 66 +#define DA9052_RTC_OSC_FRQ_127_7ppm 67 +#define DA9052_RTC_OSC_FRQ_129_6ppm 68 +#define DA9052_RTC_OSC_FRQ_131_6ppm 69 +#define DA9052_RTC_OSC_FRQ_133_5ppm 70 +#define DA9052_RTC_OSC_FRQ_135_4ppm 71 +#define DA9052_RTC_OSC_FRQ_137_3ppm 72 +#define DA9052_RTC_OSC_FRQ_139_2ppm 73 +#define DA9052_RTC_OSC_FRQ_141_1ppm 74 +#define DA9052_RTC_OSC_FRQ_143_0ppm 75 +#define DA9052_RTC_OSC_FRQ_144_9ppm 76 +#define DA9052_RTC_OSC_FRQ_146_8ppm 77 +#define DA9052_RTC_OSC_FRQ_148_7ppm 78 +#define DA9052_RTC_OSC_FRQ_150_6ppm 79 +#define DA9052_RTC_OSC_FRQ_152_5ppm 80 +#define DA9052_RTC_OSC_FRQ_154_4ppm 81 +#define DA9052_RTC_OSC_FRQ_156_4ppm 82 +#define DA9052_RTC_OSC_FRQ_158_3ppm 83 +#define DA9052_RTC_OSC_FRQ_160_2ppm 84 +#define DA9052_RTC_OSC_FRQ_162_1ppm 85 +#define DA9052_RTC_OSC_FRQ_164_0ppm 86 +#define DA9052_RTC_OSC_FRQ_165_9ppm 87 +#define DA9052_RTC_OSC_FRQ_167_8ppm 88 +#define DA9052_RTC_OSC_FRQ_169_7ppm 89 +#define DA9052_RTC_OSC_FRQ_171_6ppm 90 +#define DA9052_RTC_OSC_FRQ_173_5ppm 91 +#define DA9052_RTC_OSC_FRQ_175_4ppm 92 +#define DA9052_RTC_OSC_FRQ_177_3ppm 93 +#define DA9052_RTC_OSC_FRQ_179_2ppm 94 +#define DA9052_RTC_OSC_FRQ_181_1ppm 95 +#define DA9052_RTC_OSC_FRQ_183_1ppm 96 +#define DA9052_RTC_OSC_FRQ_185_0ppm 97 +#define DA9052_RTC_OSC_FRQ_186_9ppm 98 +#define DA9052_RTC_OSC_FRQ_188_8ppm 99 +#define DA9052_RTC_OSC_FRQ_190_7ppm 100 +#define DA9052_RTC_OSC_FRQ_192_6ppm 101 +#define DA9052_RTC_OSC_FRQ_194_5ppm 102 +#define DA9052_RTC_OSC_FRQ_196_4ppm 103 +#define DA9052_RTC_OSC_FRQ_198_3ppm 104 +#define DA9052_RTC_OSC_FRQ_200_2ppm 105 +#define DA9052_RTC_OSC_FRQ_202_1ppm 106 +#define DA9052_RTC_OSC_FRQ_204_0ppm 107 +#define DA9052_RTC_OSC_FRQ_205_9ppm 108 +#define DA9052_RTC_OSC_FRQ_207_9ppm 109 +#define DA9052_RTC_OSC_FRQ_209_8ppm 110 +#define DA9052_RTC_OSC_FRQ_211_7ppm 111 +#define DA9052_RTC_OSC_FRQ_213_6ppm 112 +#define DA9052_RTC_OSC_FRQ_215_5ppm 113 +#define DA9052_RTC_OSC_FRQ_217_4ppm 114 +#define DA9052_RTC_OSC_FRQ_219_3ppm 115 +#define DA9052_RTC_OSC_FRQ_221_2ppm 116 +#define DA9052_RTC_OSC_FRQ_223_1ppm 117 +#define DA9052_RTC_OSC_FRQ_225_0ppm 118 +#define DA9052_RTC_OSC_FRQ_226_9ppm 119 +#define DA9052_RTC_OSC_FRQ_228_8ppm 120 +#define DA9052_RTC_OSC_FRQ_230_7ppm 121 +#define DA9052_RTC_OSC_FRQ_232_6ppm 122 +#define DA9052_RTC_OSC_FRQ_234_6ppm 123 +#define DA9052_RTC_OSC_FRQ_236_5ppm 124 +#define DA9052_RTC_OSC_FRQ_238_4ppm 125 +#define DA9052_RTC_OSC_FRQ_240_3ppm 126 +#define DA9052_RTC_OSC_FRQ_242_2ppm 127 +#define DA9052_RTC_OSC_FRQ_MINUS_244_1ppm 128 +#define DA9052_RTC_OSC_FRQ_MINUS_242_2ppm 129 +#define DA9052_RTC_OSC_FRQ_MINUS_240_3ppm 130 +#define DA9052_RTC_OSC_FRQ_MINUS_238_4ppm 131 +#define DA9052_RTC_OSC_FRQ_MINUS_236_5ppm 132 +#define DA9052_RTC_OSC_FRQ_MINUS_234_6ppm 133 +#define DA9052_RTC_OSC_FRQ_MINUS_232_6ppm 134 +#define DA9052_RTC_OSC_FRQ_MINUS_230_7ppm 135 +#define DA9052_RTC_OSC_FRQ_MINUS_228_8ppm 136 +#define DA9052_RTC_OSC_FRQ_MINUS_226_9ppm 137 +#define DA9052_RTC_OSC_FRQ_MINUS_225_0ppm 138 +#define DA9052_RTC_OSC_FRQ_MINUS_223_1ppm 139 +#define DA9052_RTC_OSC_FRQ_MINUS_221_2ppm 140 +#define DA9052_RTC_OSC_FRQ_MINUS_219_3ppm 141 +#define DA9052_RTC_OSC_FRQ_MINUS_217_4ppm 142 +#define DA9052_RTC_OSC_FRQ_MINUS_215_5ppm 143 +#define DA9052_RTC_OSC_FRQ_MINUS_213_6ppm 144 +#define DA9052_RTC_OSC_FRQ_MINUS_211_7ppm 145 +#define DA9052_RTC_OSC_FRQ_MINUS_209_8ppm 146 +#define DA9052_RTC_OSC_FRQ_MINUS_207_9ppm 147 +#define DA9052_RTC_OSC_FRQ_MINUS_205_9ppm 148 +#define DA9052_RTC_OSC_FRQ_MINUS_204_0ppm 149 +#define DA9052_RTC_OSC_FRQ_MINUS_202_1ppm 150 +#define DA9052_RTC_OSC_FRQ_MINUS_200_2ppm 151 +#define DA9052_RTC_OSC_FRQ_MINUS_198_3ppm 152 +#define DA9052_RTC_OSC_FRQ_MINUS_196_4ppm 153 +#define DA9052_RTC_OSC_FRQ_MINUS_194_5ppm 154 +#define DA9052_RTC_OSC_FRQ_MINUS_192_6ppm 155 +#define DA9052_RTC_OSC_FRQ_MINUS_190_7ppm 156 +#define DA9052_RTC_OSC_FRQ_MINUS_188_8ppm 157 +#define DA9052_RTC_OSC_FRQ_MINUS_186_9ppm 158 +#define DA9052_RTC_OSC_FRQ_MINUS_185_0ppm 159 +#define DA9052_RTC_OSC_FRQ_MINUS_183_1ppm 160 +#define DA9052_RTC_OSC_FRQ_MINUS_181_1ppm 161 +#define DA9052_RTC_OSC_FRQ_MINUS_179_2ppm 162 +#define DA9052_RTC_OSC_FRQ_MINUS_177_3ppm 163 +#define DA9052_RTC_OSC_FRQ_MINUS_175_4ppm 164 +#define DA9052_RTC_OSC_FRQ_MINUS_173_5ppm 165 +#define DA9052_RTC_OSC_FRQ_MINUS_171_6ppm 166 +#define DA9052_RTC_OSC_FRQ_MINUS_169_7ppm 167 +#define DA9052_RTC_OSC_FRQ_MINUS_167_8ppm 168 +#define DA9052_RTC_OSC_FRQ_MINUS_165_9ppm 169 +#define DA9052_RTC_OSC_FRQ_MINUS_164_0ppm 170 +#define DA9052_RTC_OSC_FRQ_MINUS_162_1ppm 171 +#define DA9052_RTC_OSC_FRQ_MINUS_160_2ppm 172 +#define DA9052_RTC_OSC_FRQ_MINUS_158_3ppm 173 +#define DA9052_RTC_OSC_FRQ_MINUS_156_4ppm 174 +#define DA9052_RTC_OSC_FRQ_MINUS_154_4ppm 175 +#define DA9052_RTC_OSC_FRQ_MINUS_152_5ppm 176 +#define DA9052_RTC_OSC_FRQ_MINUS_150_6ppm 177 +#define DA9052_RTC_OSC_FRQ_MINUS_148_7ppm 178 +#define DA9052_RTC_OSC_FRQ_MINUS_146_8ppm 179 +#define DA9052_RTC_OSC_FRQ_MINUS_144_9ppm 180 +#define DA9052_RTC_OSC_FRQ_MINUS_143_0ppm 181 +#define DA9052_RTC_OSC_FRQ_MINUS_141_1ppm 182 +#define DA9052_RTC_OSC_FRQ_MINUS_139_2ppm 183 +#define DA9052_RTC_OSC_FRQ_MINUS_137_3ppm 184 +#define DA9052_RTC_OSC_FRQ_MINUS_135_4ppm 185 +#define DA9052_RTC_OSC_FRQ_MINUS_133_5ppm 186 +#define DA9052_RTC_OSC_FRQ_MINUS_131_6ppm 187 +#define DA9052_RTC_OSC_FRQ_MINUS_129_6ppm 188 +#define DA9052_RTC_OSC_FRQ_MINUS_127_7ppm 189 +#define DA9052_RTC_OSC_FRQ_MINUS_125_8ppm 190 +#define DA9052_RTC_OSC_FRQ_MINUS_123_9ppm 191 +#define DA9052_RTC_OSC_FRQ_MINUS_122_0ppm 192 +#define DA9052_RTC_OSC_FRQ_MINUS_120_1ppm 193 +#define DA9052_RTC_OSC_FRQ_MINUS_118_2ppm 194 +#define DA9052_RTC_OSC_FRQ_MINUS_116_3ppm 195 +#define DA9052_RTC_OSC_FRQ_MINUS_114_4ppm 196 +#define DA9052_RTC_OSC_FRQ_MINUS_112_5ppm 197 +#define DA9052_RTC_OSC_FRQ_MINUS_110_6ppm 198 +#define DA9052_RTC_OSC_FRQ_MINUS_108_7ppm 199 +#define DA9052_RTC_OSC_FRQ_MINUS_106_8ppm 200 +#define DA9052_RTC_OSC_FRQ_MINUS_104_9ppm 201 +#define DA9052_RTC_OSC_FRQ_MINUS_102_9ppm 202 +#define DA9052_RTC_OSC_FRQ_MINUS_101_0ppm 203 +#define DA9052_RTC_OSC_FRQ_MINUS_99_1ppm 204 +#define DA9052_RTC_OSC_FRQ_MINUS_97_2ppm 205 +#define DA9052_RTC_OSC_FRQ_MINUS_95_3ppm 206 +#define DA9052_RTC_OSC_FRQ_MINUS_93_4ppm 207 +#define DA9052_RTC_OSC_FRQ_MINUS_91_5ppm 208 +#define DA9052_RTC_OSC_FRQ_MINUS_89_6ppm 209 +#define DA9052_RTC_OSC_FRQ_MINUS_87_7ppm 210 +#define DA9052_RTC_OSC_FRQ_MINUS_85_8ppm 211 +#define DA9052_RTC_OSC_FRQ_MINUS_83_9ppm 212 +#define DA9052_RTC_OSC_FRQ_MINUS_82_0ppm 213 +#define DA9052_RTC_OSC_FRQ_MINUS_80_1ppm 214 +#define DA9052_RTC_OSC_FRQ_MINUS_78_2ppm 215 +#define DA9052_RTC_OSC_FRQ_MINUS_76_2ppm 216 +#define DA9052_RTC_OSC_FRQ_MINUS_74_3ppm 217 +#define DA9052_RTC_OSC_FRQ_MINUS_72_4ppm 218 +#define DA9052_RTC_OSC_FRQ_MINUS_70_5ppm 219 +#define DA9052_RTC_OSC_FRQ_MINUS_68_6ppm 220 +#define DA9052_RTC_OSC_FRQ_MINUS_66_7ppm 221 +#define DA9052_RTC_OSC_FRQ_MINUS_64_8ppm 222 +#define DA9052_RTC_OSC_FRQ_MINUS_62_9ppm 223 +#define DA9052_RTC_OSC_FRQ_MINUS_61_0ppm 224 +#define DA9052_RTC_OSC_FRQ_MINUS_59_1ppm 225 +#define DA9052_RTC_OSC_FRQ_MINUS_57_2ppm 226 +#define DA9052_RTC_OSC_FRQ_MINUS_55_3ppm 227 +#define DA9052_RTC_OSC_FRQ_MINUS_53_4ppm 228 +#define DA9052_RTC_OSC_FRQ_MINUS_51_4ppm 229 +#define DA9052_RTC_OSC_FRQ_MINUS_49_5ppm 230 +#define DA9052_RTC_OSC_FRQ_MINUS_47_6ppm 231 +#define DA9052_RTC_OSC_FRQ_MINUS_45_7ppm 232 +#define DA9052_RTC_OSC_FRQ_MINUS_43_8ppm 233 +#define DA9052_RTC_OSC_FRQ_MINUS_41_9ppm 234 +#define DA9052_RTC_OSC_FRQ_MINUS_40_0ppm 235 +#define DA9052_RTC_OSC_FRQ_MINUS_38_1ppm 236 +#define DA9052_RTC_OSC_FRQ_MINUS_36_2ppm 237 +#define DA9052_RTC_OSC_FRQ_MINUS_34_3ppm 238 +#define DA9052_RTC_OSC_FRQ_MINUS_32_4ppm 239 +#define DA9052_RTC_OSC_FRQ_MINUS_30_5ppm 240 +#define DA9052_RTC_OSC_FRQ_MINUS_28_6ppm 241 +#define DA9052_RTC_OSC_FRQ_MINUS_26_7ppm 242 +#define DA9052_RTC_OSC_FRQ_MINUS_24_7ppm 243 +#define DA9052_RTC_OSC_FRQ_MINUS_22_8ppm 244 +#define DA9052_RTC_OSC_FRQ_MINUS_20_9ppm 245 +#define DA9052_RTC_OSC_FRQ_MINUS_19_0ppm 246 +#define DA9052_RTC_OSC_FRQ_MINUS_17_1ppm 247 +#define DA9052_RTC_OSC_FRQ_MINUS_15_2ppm 248 +#define DA9052_RTC_OSC_FRQ_MINUS_13_3ppm 249 +#define DA9052_RTC_OSC_FRQ_MINUS_11_4ppm 250 +#define DA9052_RTC_OSC_FRQ_MINUS_9_5ppm 251 +#define DA9052_RTC_OSC_FRQ_MINUS_7_6ppm 252 +#define DA9052_RTC_OSC_FRQ_MINUS_5_7ppm 253 +#define DA9052_RTC_OSC_FRQ_MINUS_3_8ppm 254 +#define DA9052_RTC_OSC_FRQ_MINUS_1_9ppm 255 + +/* RTC error codes */ +#define DA9052_RTC_INVALID_SECONDS 3 +#define DA9052_RTC_INVALID_MINUTES 4 +#define DA9052_RTC_INVALID_HOURS 5 +#define DA9052_RTC_INVALID_DAYS 6 +#define DA9052_RTC_INVALID_MONTHS 7 +#define DA9052_RTC_INVALID_YEARS 8 +#define DA9052_RTC_INVALID_EVENT 9 +#define DA9052_RTC_INVALID_IOCTL 10 +#define DA9052_RTC_INVALID_SETTING 11 +#define DA9052_RTC_EVENT_ALREADY_REGISTERED 12 +#define DA9052_RTC_EVENT_UNREGISTERED 13 +#define DA9052_RTC_EVENT_REGISTRATION_FAILED 14 +#define DA9052_RTC_EVENT_UNREGISTRATION_FAILED 15 + +#endif +/* __LINUX_MFD_DA9052_RTC_H */ \ No newline at end of file diff --git a/include/linux/mfd/da9052/tsi.h b/include/linux/mfd/da9052/tsi.h new file mode 100644 index 000000000000..b0f5c2449e4f --- /dev/null +++ b/include/linux/mfd/da9052/tsi.h @@ -0,0 +1,427 @@ +/* + * da9052 TSI module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_TSI_H +#define __LINUX_MFD_DA9052_TSI_H + +#include +#include +#include +#include + +#define DA9052_TSI_DEVICE_NAME "da9052-tsi" +#define DA9052_TSI_INPUT_DEV DA9052_TSI_DEVICE_NAME + +#define TSI_VERSION 0x0101 +#define DA9052_VENDOR_ID 0x15B6 +#define DA9052_PRODUCT_ID 0x9052 + +#define TSI_INPUT_DEVICE_OFF 0 +#define NUM_INPUT_DEVS 1 + +#define DA9052_DISPLAY_X_MAX 0x3FF +#define DA9052_DISPLAY_Y_MAX 0x3FF +#define DA9052_TOUCH_PRESSURE_MAX 0x3FF + +#define DA9052_TCA_AUTO_TSI_ENABLE (1<<0) +#define DA9052_TCA_PEN_DET_ENABLE (1<<1) +#define DA9052_TCA_TSI_XP_MODE_ENABLE (1<<2) + +#define DA9052_TCA_TSI_DELAY_0SLOTS (0<<6) +#define DA9052_TCA_TSI_DELAY_2SLOTS (2<<6) +#define DA9052_TCA_TSI_DELAY_4SLOTS (3<<6) + +#define DA9052_TCA_TSI_SEL_XPLUS (1<<0) +#define DA9052_TCA_TSI_SEL_XMINUS (1<<1) +#define DA9052_TCA_TSI_SEL_YPLUS (1<<2) +#define DA9052_TCA_TSI_SEL_YMINUS (1<<3) + +#define DA9052_TCA_TSI_MUX_XPLUS_ROUTED_ADCIN7 (0<<4) +#define DA9052_TCA_TSI_MUX_YPLUS_ROUTED_ADCIN7 (1<<4) +#define DA9052_TCA_TSI_MUX_XMINUS_ROUTED_ADCIN7 (2<<4) +#define DA9052_TCA_TSI_MUX_YMINUS_ROUTED_ADCIN7 (3<<4) + +#define DA9052_TCA_TSI_MAN_ENABLE (1<<6) +#define DA9052_TCA_TSI_SET_TSIREF (0<<7) +#define DA9052_TCA_TSI_SET_XY_REF (1<<7) + +#define DA9052_EVETN_B_E_PEN_DOWN (1<<6) +#define DA9052_EVENT_B_E_TSI_READY (1<<7) + +#define DA9052_IRQMASK_B_PENDOWN_MASK (1<<6) +#define DA9052_IRQMASK_B_TSI_READY_MASK (1<<7) + +#define X_LSB_SHIFT (0) +#define Y_LSB_SHIFT (2) +#define Z_LSB_SHIFT (4) +#define PEN_DET_SHIFT (6) +#define X_MSB_SHIFT (2) +#define Y_MSB_SHIFT (2) +#define Z_MSB_SHIFT (2) +#define X_LSB_MASK (11 << X_LSB_SHIFT) +#define Y_LSB_MASK (11 << Y_LSB_SHIFT) +#define Z_LSB_MASK (11 << Z_LSB_SHIFT) +#define PEN_DET_MASK (11 << PEN_DET_SHIFT) + +#define TSI_FIFO_SIZE 16 + +#define INVALID_LDO9_VOLT_VALUE 17 + +#define set_bits(value, mask) (value | mask) +#define clear_bits(value, mask) (value & ~(mask)) + +#define SUCCESS 0 +#define FAILURE 1 + +#define SET 1 +#define RESET 0 +#define CLEAR 0 + +#define ENABLE 1 +#define DISABLE 0 + +#define TRUE 1 +#define FALSE 0 + +#define incr_with_wrap_reg_fifo(x) \ + if(++x >= TSI_REG_DATA_BUF_SIZE) \ + x = 0 + +#define incr_with_wrap(x) \ + if(++x >= TSI_FIFO_SIZE) \ + x = 0 + +#undef DA9052_DEBUG +#if DA9052_TSI_DEBUG +#define DA9052_DEBUG( fmt, args... ) printk( KERN_CRIT "" fmt, ##args ) +#else +#define DA9052_DEBUG( fmt, args... ) +#endif + +enum ADC_MODE { + + ECONOMY_MODE = 0, + FAST_MODE = 1 +}; + +enum TSI_DELAY { + TSI_DELAY_0SLOTS = 0, + TSI_DELAY_1SLOTS = 1, + TSI_DELAY_2SLOTS = 2, + TSI_DELAY_4SLOTS = 3 +}; + +enum TSI_SLOT_SKIP{ + TSI_SKIP_0SLOTS = 0, + TSI_SKIP_2SLOTS = 1, + TSI_SKIP_5SLOTS = 2, + TSI_SKIP_10SLOTS = 3, + TSI_SKIP_30SLOTS = 4, + TSI_SKIP_80SLOTS = 5, + TSI_SKIP_130SLOTS = 6, + TSI_SKIP_330SLOTS = 7 +}; + + +enum TSI_MUX_SEL +{ + TSI_MUX_XPLUS = 0, + TSI_MUX_YPLUS = 1, + TSI_MUX_XMINUS = 2, + TSI_MUX_YMINUS = 3 +}; + + +enum TSI_IRQ{ + TSI_PEN_DWN, + TSI_DATA_RDY +}; + + +enum TSI_COORDINATE{ + X_COORDINATE, + Y_COORDINATE, + Z_COORDINATE +}; + +enum TSI_MEASURE_SEQ{ + XYZP_MODE, + XP_MODE +}; + +enum TSI_STATE { + TSI_AUTO_MODE, + TSI_MANUAL_COORD_X, + TSI_MANUAL_COORD_Y, + TSI_MANUAL_COORD_Z, + TSI_MANUAL_SET, + TSI_IDLE +}; + +union da9052_tsi_cont_reg { + u8 da9052_tsi_cont_a; + struct{ + u8 auto_tsi_en:1; + u8 pen_det_en:1; + u8 tsi_mode:1; + u8 tsi_skip:3; + u8 tsi_delay:2; + }tsi_cont_a; +}; + +union da9052_tsi_man_cont_reg { + u8 da9052_tsi_cont_b; + struct{ + u8 tsi_sel_0:1; + u8 tsi_sel_1:1; + u8 tsi_sel_2:1; + u8 tsi_sel_3:1; + u8 tsi_mux:2; + u8 tsi_man:1; + u8 tsi_adc_ref:1; + }tsi_cont_b; +}; + +struct da9052_tsi_conf { + union da9052_tsi_cont_reg auto_cont; + union da9052_tsi_man_cont_reg man_cont; + u8 tsi_adc_sample_intervel:1; + enum TSI_STATE state; + u8 ldo9_en:1; + u8 ldo9_conf:1; + u8 tsi_ready_irq_mask:1; + u8 tsi_pendown_irq_mask:1; +}; + + +struct da9052_tsi_reg { + u8 x_msb; + u8 y_msb; + u8 z_msb; + u8 lsb; + }; + + +struct da9052_tsi_reg_fifo { + struct semaphore lock; + s32 head; + s32 tail; + struct da9052_tsi_reg data[TSI_REG_DATA_BUF_SIZE]; +}; + +struct da9052_tsi_info { + struct da9052_tsi_conf tsi_conf; + struct input_dev *input_devs[NUM_INPUT_DEVS]; + struct calib_cfg_t *tsi_calib; + u32 tsi_data_poll_interval; + u32 tsi_penup_count; + u32 tsi_zero_data_cnt; + u8 pen_dwn_event; + u8 tsi_rdy_event; + u8 pd_reg_status; + u8 datardy_reg_status; +}; + +struct da9052_tsi { + struct da9052_tsi_reg tsi_fifo[TSI_FIFO_SIZE]; + struct mutex tsi_fifo_lock; + u8 tsi_sampling; + u8 tsi_state; + u32 tsi_fifo_start; + u32 tsi_fifo_end; +}; + + struct da9052_ts_priv { + struct da9052 *da9052; + struct da9052_eh_nb pd_nb; + struct da9052_eh_nb datardy_nb; + + struct tsi_thread_type tsi_reg_proc_thread; + struct tsi_thread_type tsi_raw_proc_thread; + + struct da9052_tsi_platform_data *tsi_pdata; + + struct da9052_tsi_reg_fifo tsi_reg_fifo; + struct da9052_tsi_raw_fifo tsi_raw_fifo; + + u32 tsi_reg_data_poll_interval; + u32 tsi_raw_data_poll_interval; + + u8 early_data_flag; + u8 debounce_over; + u8 win_reference_valid; + + int os_data_cnt; + int raw_data_cnt; +}; + +static inline u8 mask_pendwn_irq(u8 val) +{ + return (val |= DA9052_IRQMASKB_MPENDOWN); +} + +static inline u8 unmask_pendwn_irq(u8 val) +{ + return (val &= ~DA9052_IRQMASKB_MPENDOWN); +} + +static inline u8 mask_tsi_rdy_irq(u8 val) +{ + return (val |=DA9052_IRQMASKB_MTSIREADY); +} + +static inline u8 unmask_tsi_rdy_irq(u8 val) +{ + return (val &= ~DA9052_IRQMASKB_MTSIREADY); +} + +static inline u8 enable_ldo9(u8 val) +{ + return (val |=DA9052_LDO9_LDO9EN); +} + +static inline u8 disable_ldo9(u8 val) +{ + return (val &= ~DA9052_LDO9_LDO9EN); +} + +static inline u8 set_auto_tsi_en(u8 val) +{ + return (val |=DA9052_TSICONTA_AUTOTSIEN); +} + +static inline u8 reset_auto_tsi_en(u8 val) +{ + return (val &=~DA9052_TSICONTA_AUTOTSIEN); +} + +static inline u8 enable_pen_detect(u8 val) +{ + return (val |=DA9052_TSICONTA_PENDETEN); +} + +static inline u8 disable_pen_detect(u8 val) +{ + return (val &=~DA9052_TSICONTA_PENDETEN); +} + +static inline u8 enable_xyzp_mode(u8 val) +{ + return (val &= ~DA9052_TSICONTA_TSIMODE); +} + +static inline u8 enable_xp_mode(u8 val) +{ + return(val |= DA9052_TSICONTA_TSIMODE); +} + +static inline u8 enable_tsi_manual_mode(u8 val) +{ + return(val |= DA9052_TSICONTB_TSIMAN); +} + +static inline u8 disable_tsi_manual_mode(u8 val) +{ + return(val &= ~DA9052_TSICONTB_TSIMAN); +} + +static inline u8 tsi_sel_xplus_close(u8 val) +{ + return(val |= DA9052_TSICONTB_TSISEL0); +} + +static inline u8 tsi_sel_xplus_open(u8 val) +{ + return(val &= ~DA9052_TSICONTB_TSISEL0); +} + +static inline u8 tsi_sel_xminus_close(u8 val) +{ + return(val |= DA9052_TSICONTB_TSISEL1); +} + +static inline u8 tsi_sel_xminus_open(u8 val) +{ + return(val &= ~DA9052_TSICONTB_TSISEL1); +} + +static inline u8 tsi_sel_yplus_close(u8 val) +{ + return(val |= DA9052_TSICONTB_TSISEL2); +} + +static inline u8 tsi_sel_yplus_open(u8 val) +{ + return(val &= ~DA9052_TSICONTB_TSISEL2); +} + +static inline u8 tsi_sel_yminus_close(u8 val) +{ + return(val |= DA9052_TSICONTB_TSISEL3); +} + +static inline u8 tsi_sel_yminus_open(u8 val) +{ + return(val &= ~DA9052_TSICONTB_TSISEL3); +} + +static inline u8 adc_mode_economy_mode(u8 val) +{ + return(val &= ~DA9052_ADCCONT_ADCMODE); +} + +static inline u8 adc_mode_fast_mode(u8 val) +{ + return(val |= DA9052_ADCCONT_ADCMODE); +} +int da9052_tsi_get_calib_display_point(struct da9052_tsi_data *display); + +struct da9052_ldo_config { + u16 ldo_volt; + u8 ldo_num; + u8 ldo_conf:1; + u8 ldo_pd:1; +}; + +static inline u8 ldo9_mV_to_reg(u16 value) +{ + return ((value - DA9052_LDO9_VOLT_LOWER)/DA9052_LDO9_VOLT_STEP); +} + +static inline u8 validate_ldo9_mV(u16 value) +{ + if ((value >= DA9052_LDO9_VOLT_LOWER) && \ + (value <= DA9052_LDO9_VOLT_UPPER)) + return + (((value - DA9052_LDO9_VOLT_LOWER) % DA9052_LDO9_VOLT_STEP > 0) ? -1 : 0); + return FAILURE; +} + +s32 da9052_tsi_raw_proc_thread (void *ptr); +void __init da9052_init_tsi_fifos (struct da9052_ts_priv *priv); +void clean_tsi_fifos(struct da9052_ts_priv *priv); +u32 get_reg_data_cnt (struct da9052_ts_priv *priv); +u32 get_reg_free_space_cnt(struct da9052_ts_priv *priv); +void da9052_tsi_process_reg_data(struct da9052_ts_priv *priv); +void da9052_tsi_pen_down_handler(struct da9052_eh_nb *eh_data, u32 event); +void da9052_tsi_data_ready_handler(struct da9052_eh_nb *eh_data, u32 event); + +#endif /* __LINUX_MFD_DA9052_TSI_H */ diff --git a/include/linux/mfd/da9052/tsi_calibrate.h b/include/linux/mfd/da9052/tsi_calibrate.h new file mode 100644 index 000000000000..a4b42ea079a9 --- /dev/null +++ b/include/linux/mfd/da9052/tsi_calibrate.h @@ -0,0 +1,47 @@ +/* + * da9052 TSI calibration module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_TSI_CALIBRATE_H +#define __LINUX_MFD_DA9052_TSI_CALIBRATE_H + +#include + +struct Calib_xform_matrix_t { + s32 An; + s32 Bn; + s32 Cn; + s32 Dn; + s32 En; + s32 Fn; + s32 Divider; +} ; + + +struct calib_cfg_t { + u8 calibrate_flag; +} ; + +ssize_t da9052_tsi_set_calib_matrix(struct da9052_tsi_data *displayPtr, + struct da9052_tsi_data *screenPtr); +u8 configure_tsi_calib(struct calib_cfg_t *tsi_calib); +struct calib_cfg_t *get_calib_config(void); +#endif /* __LINUX_MFD_DA9052_TSI_CALIBRATE_H */ + diff --git a/include/linux/mfd/da9052/tsi_cfg.h b/include/linux/mfd/da9052/tsi_cfg.h new file mode 100644 index 000000000000..729d10316283 --- /dev/null +++ b/include/linux/mfd/da9052/tsi_cfg.h @@ -0,0 +1,132 @@ +/* + * da9052 TSI configuration module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_TSI_CFG_H +#define __LINUX_MFD_DA9052_TSI_CFG_H + +#define DA9052_TSI_DEBUG 0 + +#define AUTO_MODE 0 +#define IDLE 1 +#define DEFAULT_TSI_STATE AUTO_MODE + +#define TSI_SLOT_SKIP_VALUE 0 + +#define TSI_DELAY_VALUE 3 + +#define TSI_MODE_VALUE 0 + +#define ENABLE_AVERAGE_FILTER 1 + +#define DEFAULT_AVERAGE_FILTER_SIZE 3 + +#define ENABLE_WINDOW_FILTER 1 + +#define TSI_X_WINDOW_SIZE 50 +#define TSI_Y_WINDOW_SIZE 50 + +#define SAMPLE_CNT_FOR_WIN_REF 3 + +#define TSI_ECONOMY_MODE 0 +#define TSI_FAST_MODE 1 +#define DEFAULT_TSI_SAMPLING_MODE TSI_FAST_MODE + +#define TSI_USE_CALIBRATION 1 + +#define DA9052_TSI_CALIB_AN 1 +#define DA9052_TSI_CALIB_BN 0 +#define DA9052_TSI_CALIB_CN 0 +#define DA9052_TSI_CALIB_DN 0 +#define DA9052_TSI_CALIB_EN 1 +#define DA9052_TSI_CALIB_FN 0 +#define DA9052_TSI_CALIB_DIVIDER 1 + +#define TS_X_MIN (0) +#define TS_X_MAX (1023) +#define TS_Y_MIN (0) +#define TS_Y_MAX (1023) + +#define DISPLAY_X_MIN (0) +#define DISPLAY_X_MAX (1023) +#define DISPLAY_Y_MIN (0) +#define DISPLAY_Y_MAX (1023) + +#define ENABLE_TSI_DEBOUNCE 0 + +#define TSI_DEBOUNCE_DATA_CNT 3 + + +#define RELEASE +#define DA9052_TSI_RAW_DATA_PROFILING 0 +#define DA9052_TSI_WIN_FLT_DATA_PROFILING 0 +#define DA9052_TSI_AVG_FLT_DATA_PROFILING 0 +#define DA9052_TSI_CALIB_DATA_PROFILING 0 +#define DA9052_TSI_OS_DATA_PROFILING 1 +#define DA9052_TSI_PRINT_DEBOUNCED_DATA 0 +#define DA9052_TSI_PRINT_PREVIOUS_DATA 0 + + +#if ENABLE_AVERAGE_FILTER +#define TSI_AVERAGE_FILTER_SIZE DEFAULT_AVERAGE_FILTER_SIZE +#else +#define TSI_AVERAGE_FILTER_SIZE 1 +#endif + +#define TSI_FAST_MODE_SAMPLE_CNT 1000 +#define TSI_ECO_MODE_SAMPLE_CNT 100 + +#define TSI_POLL_SAMPLE_CNT 10 + +#define TSI_FAST_MODE_REG_DATA_PROCESSING_INTERVAL \ + ((1000 / TSI_FAST_MODE_SAMPLE_CNT)* TSI_POLL_SAMPLE_CNT) +#define TSI_ECO_MODE_REG_DATA_PROCESSING_INTERVAL \ + ((1000 / TSI_ECO_MODE_SAMPLE_CNT)* TSI_POLL_SAMPLE_CNT) + +#if DEFAULT_TSI_SAMPLING_MODE +#define DEFAULT_REG_DATA_PROCESSING_INTERVAL \ + TSI_FAST_MODE_REG_DATA_PROCESSING_INTERVAL +#else +#define DEFAULT_REG_DATA_PROCESSING_INTERVAL \ + TSI_ECO_MODE_REG_DATA_PROCESSING_INTERVAL +#endif + +#define TSI_REG_DATA_BUF_SIZE (2 * TSI_POLL_SAMPLE_CNT) + +#define TSI_FAST_MODE_RAW_DATA_PROCESSING_INTERVAL \ + ((1000 / TSI_FAST_MODE_SAMPLE_CNT) * (TSI_AVERAGE_FILTER_SIZE)) +#define TSI_ECO_MODE_RAW_DATA_PROCESSING_INTERVAL \ + ((1000 / TSI_ECO_MODE_SAMPLE_CNT) * (TSI_AVERAGE_FILTER_SIZE)) + + +#if DEFAULT_TSI_SAMPLING_MODE +#define DEFAULT_RAW_DATA_PROCESSING_INTERVAL \ + TSI_FAST_MODE_RAW_DATA_PROCESSING_INTERVAL +#else +#define DEFAULT_RAW_DATA_PROCESSING_INTERVAL \ + TSI_ECO_MODE_RAW_DATA_PROCESSING_INTERVAL +#endif + + +#define TSI_RAW_DATA_BUF_SIZE \ + (TSI_REG_DATA_BUF_SIZE * \ + ( (TSI_AVERAGE_FILTER_SIZE / TSI_POLL_SAMPLE_CNT) + 1)) + +#endif /* __LINUX_MFD_DA9052_TSI_CFG_H */ diff --git a/include/linux/mfd/da9052/tsi_filter.h b/include/linux/mfd/da9052/tsi_filter.h new file mode 100644 index 000000000000..0439a08fa64d --- /dev/null +++ b/include/linux/mfd/da9052/tsi_filter.h @@ -0,0 +1,56 @@ +/* + * da9052 TSI filter module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_TSI_FILTER_H +#define __LINUX_MFD_DA9052_TSI_FILTER_H + +#include + +struct da9052_tsi_data { + s16 x; + s16 y; + s16 z; +}; + +struct da9052_tsi_raw_fifo { + struct semaphore lock; + s32 head; + s32 tail; + struct da9052_tsi_data data[TSI_RAW_DATA_BUF_SIZE]; +}; + +struct tsi_thread_type { + u8 pid; + u8 state; + struct completion notifier; + struct task_struct *thread_task; +} ; + +/* State for TSI thread */ +#define ACTIVE 0 +#define INACTIVE 1 + + +extern u32 da9052_tsi_get_input_dev(u8 off); + +ssize_t da9052_tsi_get_calib_display_point(struct da9052_tsi_data *displayPtr); + +#endif /* __LINUX_MFD_DA9052_TSI_FILTER_H */ diff --git a/include/linux/mfd/da9052/wdt.h b/include/linux/mfd/da9052/wdt.h new file mode 100644 index 000000000000..6a77bfc5fd9b --- /dev/null +++ b/include/linux/mfd/da9052/wdt.h @@ -0,0 +1,83 @@ +/* + * da9052 SM (watchdog) module declarations. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_WDT_H +#define __LINUX_MFD_DA9052_WDT_H + +#include + +/* To enable debug output for your module, set this to 1 */ +#define DA9052_SM_DEBUG 0 + +/* Error codes */ +#define BUS_ERR 2 +#define INIT_FAIL 3 +#define SM_OPEN_FAIL 4 +#define NO_IOCTL_CMD 5 +#define INVALID_SCALING_VALUE 6 +#define STROBING_FILTER_ERROR 7 +#define TIMER_DELETE_ERR 8 +#define STROBING_MODE_ERROR 9 + +/* IOCTL Switch */ +/* For strobe watchdog function */ +#define DA9052_SM_IOCTL_STROBE_WDT 1 + +/* For setting watchdog timer time */ +#define DA9052_SM_IOCTL_SET_WDT 2 + +/* For enabling/disabling strobing filter */ +#define DA9052_SM_IOCTL_SET_STROBING_FILTER 3 + +/* For enabling/disabling strobing filter */ +#define DA9052_SM_IOCTL_SET_STROBING_MODE 4 + +/* Watchdog time scaling TWDMAX scaling macros */ +#define DA9052_WDT_DISABLE 0 +#define DA9052_SCALE_1X 1 +#define DA9052_SCALE_2X 2 +#define DA9052_SCALE_4X 3 +#define DA9052_SCALE_8X 4 +#define DA9052_SCALE_16X 5 +#define DA9052_SCALE_32X 6 +#define DA9052_SCALE_64X 7 + +#define DA9052_STROBE_WIN_FILTER_PER 80 +#define DA9052_X1_WINDOW ((1 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) +#define DA9052_X2_WINDOW ((2 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) +#define DA9052_X4_WINDOW ((4 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) +#define DA9052_X8_WINDOW ((8 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) +#define DA9052_X16_WINDOW ((16 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) +#define DA9052_X32_WINDOW ((32 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) +#define DA9052_X64_WINDOW ((64 * 2048 * DA9052_STROBE_WIN_FILTER_PER)/100) + +#define DA9052_STROBE_AUTO 1 +#define DA9052_STROBE_MANUAL 0 + +#define DA9052_SM_STROBE_CONF DISABLE + +#define DA9052_ADC_TWDMIN_TIME 500 + +void start_strobing(struct work_struct *work); +/* Create a handler for the scheduling start_strobing function */ +DECLARE_WORK(strobing_action, start_strobing); + +#endif /* __LINUX_MFD_DA9052_WDT_H */ diff --git a/include/linux/regulator/da9052-regulator.h b/include/linux/regulator/da9052-regulator.h new file mode 100644 index 000000000000..58e6f0bafef6 --- /dev/null +++ b/include/linux/regulator/da9052-regulator.h @@ -0,0 +1,15 @@ +/* This file is there to support additional attributes in + sysfs - changestate and setvoltage +*/ +#ifndef _DA9052_REGULATOR_H +#define _DA9052_REGULATOR_H + +int da9052_ldo_buck_enable(struct regulator_dev *rdev); +int da9052_ldo_buck_disable(struct regulator_dev *rdev); + + +int da9052_ldo_buck_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV); +int da9052_ldo_buck_get_voltage(struct regulator_dev *rdev); + +#endif