diff options
Diffstat (limited to 'drivers')
62 files changed, 5008 insertions, 1549 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8382dc832929..aa73ef3233b8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -409,6 +409,13 @@ config GPIO_TWL4030 Say yes here to access the GPIO signals of various multi-function power management chips from Texas Instruments. +config GPIO_TWL6040 + tristate "TWL6040 GPO" + depends on TWL6040_CORE + help + Say yes here to access the GPO signals of twl6040 + audio chip from Texas Instruments. + config GPIO_WM831X tristate "WM831x GPIOs" depends on MFD_WM831X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 0ffaa8423e87..b2c109d1303d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o +obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index b7c06517403d..d4d617966696 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -49,6 +49,10 @@ static const u8 ichx_regs[3][3] = { {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ }; +static const u8 ichx_reglen[3] = { + 0x30, 0x10, 0x10, +}; + #define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start) #define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start) @@ -75,6 +79,7 @@ static struct { struct resource *pm_base; /* Power Mangagment IO base */ struct ichx_desc *desc; /* Pointer to chipset-specific description */ u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */ + u8 use_gpio; /* Which GPIO groups are usable */ } ichx_priv; static int modparam_gpiobase = -1; /* dynamic */ @@ -123,8 +128,16 @@ static int ichx_read_bit(int reg, unsigned nr) return data & (1 << bit) ? 1 : 0; } +static int ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) +{ + return (ichx_priv.use_gpio & (1 << (nr / 32))) ? 0 : -ENXIO; +} + static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) { + if (!ichx_gpio_check_available(gpio, nr)) + return -ENXIO; + /* * Try setting pin as an input and verify it worked since many pins * are output-only. @@ -138,6 +151,9 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, int val) { + if (!ichx_gpio_check_available(gpio, nr)) + return -ENXIO; + /* Set GPIO output value. */ ichx_write_bit(GPIO_LVL, nr, val, 0); @@ -153,6 +169,9 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) { + if (!ichx_gpio_check_available(chip, nr)) + return -ENXIO; + return ichx_read_bit(GPIO_LVL, nr); } @@ -161,6 +180,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) unsigned long flags; u32 data; + if (!ichx_gpio_check_available(chip, nr)) + return -ENXIO; + /* * GPI 0 - 15 need to be read from the power management registers on * a ICH6/3100 bridge. @@ -291,6 +313,46 @@ static struct ichx_desc intel5_desc = { .ngpio = 76, }; +static int __devinit ichx_gpio_request_regions(struct resource *res_base, + const char *name, u8 use_gpio) +{ + int i; + + if (!res_base || !res_base->start || !res_base->end) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { + if (!(use_gpio & (1 << i))) + continue; + if (!request_region(res_base->start + ichx_regs[0][i], + ichx_reglen[i], name)) + goto request_err; + } + return 0; + +request_err: + /* Clean up: release already requested regions, if any */ + for (i--; i >= 0; i--) { + if (!(use_gpio & (1 << i))) + continue; + release_region(res_base->start + ichx_regs[0][i], + ichx_reglen[i]); + } + return -EBUSY; +} + +static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { + if (!(use_gpio & (1 << i))) + continue; + release_region(res_base->start + ichx_regs[0][i], + ichx_reglen[i]); + } +} + static int __devinit ichx_gpio_probe(struct platform_device *pdev) { struct resource *res_base, *res_pm; @@ -329,12 +391,11 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev) } res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); - if (!res_base || !res_base->start || !res_base->end) - return -ENODEV; - - if (!request_region(res_base->start, resource_size(res_base), - pdev->name)) - return -EBUSY; + ichx_priv.use_gpio = ich_info->use_gpio; + err = ichx_gpio_request_regions(res_base, pdev->name, + ichx_priv.use_gpio); + if (err) + return err; ichx_priv.gpio_base = res_base; @@ -374,8 +435,7 @@ init: return 0; add_err: - release_region(ichx_priv.gpio_base->start, - resource_size(ichx_priv.gpio_base)); + ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio); if (ichx_priv.pm_base) release_region(ichx_priv.pm_base->start, resource_size(ichx_priv.pm_base)); @@ -393,8 +453,7 @@ static int __devexit ichx_gpio_remove(struct platform_device *pdev) return err; } - release_region(ichx_priv.gpio_base->start, - resource_size(ichx_priv.gpio_base)); + ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio); if (ichx_priv.pm_base) release_region(ichx_priv.pm_base->start, resource_size(ichx_priv.pm_base)); diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c new file mode 100644 index 000000000000..dd58e8b25043 --- /dev/null +++ b/drivers/gpio/gpio-twl6040.c @@ -0,0 +1,137 @@ +/* + * Access to GPOs on TWL6040 chip + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Authors: + * Sergio Aguirre <saaguirre@ti.com> + * Peter Ujfalusi <peter.ujfalusi@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kthread.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/of.h> + +#include <linux/mfd/twl6040.h> + +static struct gpio_chip twl6040gpo_chip; + +static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) +{ + struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent); + int ret = 0; + + ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); + if (ret < 0) + return ret; + + return (ret >> offset) & 1; +} + +static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + /* This only drives GPOs, and can't change direction */ + return 0; +} + +static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent); + int ret; + u8 gpoctl; + + ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); + if (ret < 0) + return; + + if (value) + gpoctl = ret | (1 << offset); + else + gpoctl = ret & ~(1 << offset); + + twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); +} + +static struct gpio_chip twl6040gpo_chip = { + .label = "twl6040", + .owner = THIS_MODULE, + .get = twl6040gpo_get, + .direction_output = twl6040gpo_direction_out, + .set = twl6040gpo_set, + .can_sleep = 1, +}; + +/*----------------------------------------------------------------------*/ + +static int __devinit gpo_twl6040_probe(struct platform_device *pdev) +{ + struct twl6040_gpo_data *pdata = pdev->dev.platform_data; + struct device *twl6040_core_dev = pdev->dev.parent; + struct twl6040 *twl6040 = dev_get_drvdata(twl6040_core_dev); + int ret; + + if (pdata) + twl6040gpo_chip.base = pdata->gpio_base; + else + twl6040gpo_chip.base = -1; + + if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0) + twl6040gpo_chip.ngpio = 3; /* twl6040 have 3 GPO */ + else + twl6040gpo_chip.ngpio = 1; /* twl6041 have 1 GPO */ + + twl6040gpo_chip.dev = &pdev->dev; +#ifdef CONFIG_OF_GPIO + twl6040gpo_chip.of_node = twl6040_core_dev->of_node; +#endif + + ret = gpiochip_add(&twl6040gpo_chip); + if (ret < 0) { + dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); + twl6040gpo_chip.ngpio = 0; + } + + return ret; +} + +static int __devexit gpo_twl6040_remove(struct platform_device *pdev) +{ + return gpiochip_remove(&twl6040gpo_chip); +} + +/* Note: this hardware lives inside an I2C-based multi-function device. */ +MODULE_ALIAS("platform:twl6040-gpo"); + +static struct platform_driver gpo_twl6040_driver = { + .driver = { + .name = "twl6040-gpo", + .owner = THIS_MODULE, + }, + .probe = gpo_twl6040_probe, + .remove = gpo_twl6040_remove, +}; + +module_platform_driver(gpo_twl6040_driver); + +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_DESCRIPTION("GPO interface for TWL6040"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index fc0ed9b43424..2194a3c7236a 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/jiffies.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/workqueue.h> #include <linux/i2c/twl.h> #include <linux/mfd/twl4030-audio.h> @@ -194,13 +195,26 @@ static int twl4030_vibra_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, twl4030_vibra_suspend, twl4030_vibra_resume); +static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, + struct device_node *node) +{ + if (pdata && pdata->coexist) + return true; + + if (of_find_node_by_name(node, "codec")) + return true; + + return false; +} + static int __devinit twl4030_vibra_probe(struct platform_device *pdev) { struct twl4030_vibra_data *pdata = pdev->dev.platform_data; + struct device_node *twl4030_core_node = pdev->dev.parent->of_node; struct vibra_info *info; int ret; - if (!pdata) { + if (!pdata && !twl4030_core_node) { dev_dbg(&pdev->dev, "platform_data not available\n"); return -EINVAL; } @@ -210,7 +224,7 @@ static int __devinit twl4030_vibra_probe(struct platform_device *pdev) return -ENOMEM; info->dev = &pdev->dev; - info->coexist = pdata->coexist; + info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); INIT_WORK(&info->play_work, vibra_play_work); info->input_dev = input_allocate_device(); diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index 05f30b73c3c3..326218dbd6e6 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -10,6 +10,7 @@ */ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/input.h> @@ -113,14 +114,69 @@ static void pm860x_touch_close(struct input_dev *dev) pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0); } +#ifdef CONFIG_OF +static int __devinit pm860x_touch_dt_init(struct platform_device *pdev, + struct pm860x_chip *chip, + int *res_x) +{ + struct device_node *np = pdev->dev.parent->of_node; + struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ + : chip->companion; + int data, n, ret; + if (!np) + return -ENODEV; + np = of_find_node_by_name(np, "touch"); + if (!np) { + dev_err(&pdev->dev, "Can't find touch node\n"); + return -EINVAL; + } + /* set GPADC MISC1 register */ + data = 0; + if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n)) + data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK; + if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n)) + data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK; + if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n)) + data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK; + if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n)) + data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK; + if (data) { + ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); + if (ret < 0) + return -EINVAL; + } + /* set tsi prebias time */ + if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) { + ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); + if (ret < 0) + return -EINVAL; + } + /* set prebias & prechg time of pen detect */ + data = 0; + if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n)) + data |= n & PM8607_PD_PREBIAS_MASK; + if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n)) + data |= n & PM8607_PD_PRECHG_MASK; + if (data) { + ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); + if (ret < 0) + return -EINVAL; + } + of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x); + return 0; +} +#else +#define pm860x_touch_dt_init(x, y, z) (-1) +#endif + static int __devinit pm860x_touch_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct pm860x_platform_data *pm860x_pdata = \ - pdev->dev.parent->platform_data; - struct pm860x_touch_pdata *pdata = NULL; + struct pm860x_touch_pdata *pdata = pdev->dev.platform_data; struct pm860x_touch *touch; - int irq, ret; + struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ + : chip->companion; + int irq, ret, res_x = 0, data = 0; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -128,16 +184,55 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev) return -EINVAL; } - if (!pm860x_pdata) { - dev_err(&pdev->dev, "platform data is missing\n"); - return -EINVAL; - } - - pdata = pm860x_pdata->touch; - if (!pdata) { - dev_err(&pdev->dev, "touchscreen data is missing\n"); - return -EINVAL; + if (pm860x_touch_dt_init(pdev, chip, &res_x)) { + if (pdata) { + /* set GPADC MISC1 register */ + data = 0; + data |= (pdata->gpadc_prebias << 1) + & PM8607_GPADC_PREBIAS_MASK; + data |= (pdata->slot_cycle << 3) + & PM8607_GPADC_SLOT_CYCLE_MASK; + data |= (pdata->off_scale << 5) + & PM8607_GPADC_OFF_SCALE_MASK; + data |= (pdata->sw_cal << 7) + & PM8607_GPADC_SW_CAL_MASK; + if (data) { + ret = pm860x_reg_write(i2c, + PM8607_GPADC_MISC1, data); + if (ret < 0) + return -EINVAL; + } + /* set tsi prebias time */ + if (pdata->tsi_prebias) { + data = pdata->tsi_prebias; + ret = pm860x_reg_write(i2c, + PM8607_TSI_PREBIAS, data); + if (ret < 0) + return -EINVAL; + } + /* set prebias & prechg time of pen detect */ + data = 0; + data |= pdata->pen_prebias + & PM8607_PD_PREBIAS_MASK; + data |= (pdata->pen_prechg << 5) + & PM8607_PD_PRECHG_MASK; + if (data) { + ret = pm860x_reg_write(i2c, + PM8607_PD_PREBIAS, data); + if (ret < 0) + return -EINVAL; + } + res_x = pdata->res_x; + } else { + dev_err(&pdev->dev, "failed to get platform data\n"); + return -EINVAL; + } } + /* enable GPADC */ + ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN, + PM8607_GPADC_EN); + if (ret) + return ret; touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); if (touch == NULL) @@ -158,9 +253,9 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev) touch->idev->open = pm860x_touch_open; touch->idev->close = pm860x_touch_close; touch->chip = chip; - touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; - touch->irq = irq + chip->irq_base; - touch->res_x = pdata->res_x; + touch->i2c = i2c; + touch->irq = irq; + touch->res_x = res_x; input_set_drvdata(touch->idev, touch); ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler, diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 61897cfeeda6..b7e8cc0957fc 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/leds.h> @@ -20,18 +21,12 @@ #include <linux/mfd/88pm860x.h> #include <linux/module.h> -#define LED_PWM_SHIFT (3) #define LED_PWM_MASK (0x1F) #define LED_CURRENT_MASK (0x07 << 5) -#define LED_BLINK_ON_MASK (0x07) #define LED_BLINK_MASK (0x7F) -#define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66) -#define LED_BLINK_ON_MIN LED_BLINK_ON(0) -#define LED_BLINK_ON_MAX LED_BLINK_ON(0x7) #define LED_ON_CONTINUOUS (0x0F << 3) -#define LED_TO_ON(x) ((x - 66) / 66) #define LED1_BLINK_EN (1 << 1) #define LED2_BLINK_EN (1 << 2) @@ -49,85 +44,25 @@ struct pm860x_led { unsigned char brightness; unsigned char current_brightness; - int blink_data; - int blink_time; - int blink_on; - int blink_off; + int reg_control; + int reg_blink; + int blink_mask; }; -/* return offset of color register */ -static inline int __led_off(int port) -{ - int ret = -EINVAL; - - switch (port) { - case PM8606_LED1_RED: - case PM8606_LED1_GREEN: - case PM8606_LED1_BLUE: - ret = port - PM8606_LED1_RED + PM8606_RGB1B; - break; - case PM8606_LED2_RED: - case PM8606_LED2_GREEN: - case PM8606_LED2_BLUE: - ret = port - PM8606_LED2_RED + PM8606_RGB2B; - break; - } - return ret; -} - -/* return offset of blink register */ -static inline int __blink_off(int port) -{ - int ret = -EINVAL; - - switch (port) { - case PM8606_LED1_RED: - case PM8606_LED1_GREEN: - case PM8606_LED1_BLUE: - ret = PM8606_RGB1A; - break; - case PM8606_LED2_RED: - case PM8606_LED2_GREEN: - case PM8606_LED2_BLUE: - ret = PM8606_RGB2A; - break; - } - return ret; -} - -static inline int __blink_ctl_mask(int port) -{ - int ret = -EINVAL; - - switch (port) { - case PM8606_LED1_RED: - case PM8606_LED1_GREEN: - case PM8606_LED1_BLUE: - ret = LED1_BLINK_EN; - break; - case PM8606_LED2_RED: - case PM8606_LED2_GREEN: - case PM8606_LED2_BLUE: - ret = LED2_BLINK_EN; - break; - } - return ret; -} - static int led_power_set(struct pm860x_chip *chip, int port, int on) { int ret = -EINVAL; switch (port) { - case PM8606_LED1_RED: - case PM8606_LED1_GREEN: - case PM8606_LED1_BLUE: + case 0: + case 1: + case 2: ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) : pm8606_osc_disable(chip, RGB1_ENABLE); break; - case PM8606_LED2_RED: - case PM8606_LED2_GREEN: - case PM8606_LED2_BLUE: + case 3: + case 4: + case 5: ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) : |