From 004c15a68076f5bdc343bed92efed81087cecbfb Mon Sep 17 00:00:00 2001 From: Bill Huang Date: Sun, 19 Aug 2012 18:07:55 -0700 Subject: mfd: dt: tps6586x: Add power off control Add DT property "ti,system-power-controller" telling whether or not this pmic is in charge of controlling the system power, so the power off routine can be hooked up to system call "pm_power_off". Based on the work by: Dan Willemsen Signed-off-by: Bill Huang Tested-by: Stephen Warren Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/regulator/tps6586x.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/tps6586x.txt b/Documentation/devicetree/bindings/regulator/tps6586x.txt index d156e1b5db12..03dfa4e0aa75 100644 --- a/Documentation/devicetree/bindings/regulator/tps6586x.txt +++ b/Documentation/devicetree/bindings/regulator/tps6586x.txt @@ -18,6 +18,10 @@ Required properties: - vinldo678-supply: The input supply for the LDO6, LDO7 and LDO8 - vinldo9-supply: The input supply for the LDO9 +Optional properties: +- ti,system-power-controller: Telling whether or not this pmic is controlling + the system power. + Each regulator is defined using the standard binding for regulators. Example: @@ -30,6 +34,8 @@ Example: #gpio-cells = <2>; gpio-controller; + ti,system-power-controller; + sm0-supply = <&some_reg>; sm1-supply = <&some_reg>; sm2-supply = <&some_reg>; -- cgit v1.2.3-59-g8ed1b From b079fa72069ba7f754ba8bdf737335abdb971b67 Mon Sep 17 00:00:00 2001 From: Bill Huang Date: Sun, 19 Aug 2012 18:07:56 -0700 Subject: mfd: dt: tps65910: Add power off control Add DT property "ti,system-power-controller" telling whether or not this pmic is in charge of controlling the system power, so the power off routine can be hooked up to system call "pm_power_off". Based on the work by: Dan Willemsen Signed-off-by: Bill Huang Tested-by: Stephen Warren Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/tps65910.txt | 4 ++++ drivers/mfd/tps65910.c | 22 ++++++++++++++++++++++ include/linux/mfd/tps65910.h | 3 +++ 3 files changed, 29 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt index db03599ae4dc..2e3304888ffc 100644 --- a/Documentation/devicetree/bindings/mfd/tps65910.txt +++ b/Documentation/devicetree/bindings/mfd/tps65910.txt @@ -59,6 +59,8 @@ Optional properties: in TPS6591X datasheet) - ti,en-gpio-sleep: enable sleep control for gpios There should be 9 entries here, one for each gpio. +- ti,system-power-controller: Telling whether or not this pmic is controlling + the system power. Regulator Optional properties: - ti,regulator-ext-sleep-control: enable external sleep @@ -79,6 +81,8 @@ Example: #interrupt-cells = <2>; interrupt-controller; + ti,system-power-controller; + ti,vmbch-threshold = 0; ti,vmbch2-threshold = 0; ti,en-ck32k-xtal; diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index d3ce4d569deb..ca902943cfa9 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -198,6 +198,8 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, board_info->irq = client->irq; board_info->irq_base = -1; + board_info->pm_off = of_property_read_bool(np, + "ti,system-power-controller"); return board_info; } @@ -210,6 +212,21 @@ struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, } #endif +static struct i2c_client *tps65910_i2c_client; +static void tps65910_power_off(void) +{ + struct tps65910 *tps65910; + + tps65910 = dev_get_drvdata(&tps65910_i2c_client->dev); + + if (tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_PWR_OFF_MASK) < 0) + return; + + tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_ON_MASK); +} + static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -267,6 +284,11 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, tps65910_ck32k_init(tps65910, pmic_plat_data); tps65910_sleepinit(tps65910, pmic_plat_data); + if (pmic_plat_data->pm_off && !pm_power_off) { + tps65910_i2c_client = i2c; + pm_power_off = tps65910_power_off; + } + return ret; } diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 9bf8767818b4..ac772b36a1b1 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -366,6 +366,8 @@ /*Register DEVCTRL (0x80) register.RegisterDescription */ +#define DEVCTRL_PWR_OFF_MASK 0x80 +#define DEVCTRL_PWR_OFF_SHIFT 7 #define DEVCTRL_RTC_PWDN_MASK 0x40 #define DEVCTRL_RTC_PWDN_SHIFT 6 #define DEVCTRL_CK32K_CTRL_MASK 0x20 @@ -809,6 +811,7 @@ struct tps65910_board { int vmbch2_threshold; bool en_ck32k_xtal; bool en_dev_slp; + bool pm_off; struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; unsigned long regulator_ext_sleep_control[TPS65910_NUM_REGS]; -- cgit v1.2.3-59-g8ed1b From a91d2f8d6e4c0fd3d6e1a2b5609eb93b114effb6 Mon Sep 17 00:00:00 2001 From: Gyungoh Yoo Date: Wed, 15 Aug 2012 10:28:46 -0600 Subject: mfd: Add MAX8907 core driver The MAX8907 is an I2C-based power-management IC containing voltage regulators, a reset controller, a real-time clock, and a touch-screen controller. The original driver was written by: * Gyungoh Yoo Various fixes and enhancements by: * Jin Park * Tom Cherry * Prashant Gaikwad * Dan Willemsen * Laxman Dewangan During upstreaming, I (swarren): * Converted to regmap. * Converted to regmap-irq. * Allowed probing from device tree. * Renamed from max8907c->max8907, since the driver covers at least the C and B revisions. * General cleanup. Signed-off-by: Gyungoh Yoo Signed-off-by: Stephen Warren Reviewed-by: Mark Brown #v3 Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/regulator/max8907.txt | 71 +++++ drivers/mfd/Kconfig | 12 + drivers/mfd/Makefile | 1 + drivers/mfd/max8907.c | 331 +++++++++++++++++++++ include/linux/mfd/max8907.h | 250 ++++++++++++++++ 5 files changed, 665 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/max8907.txt create mode 100644 drivers/mfd/max8907.c create mode 100644 include/linux/mfd/max8907.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/max8907.txt b/Documentation/devicetree/bindings/regulator/max8907.txt new file mode 100644 index 000000000000..4fd847f0afc0 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8907.txt @@ -0,0 +1,71 @@ +MAX8907 regulator + +Required properties: +- compatible: "maxim,max8907" +- reg: I2C slave address +- interrupts: The interrupt output of the controller +- mbatt-supply: The input supply for MBATT, BBAT, SDBY, VRTC. +- in-v1-supply: The input supply for SD1. +- in-v2-supply: The input supply for SD2. +- in-v3-supply: The input supply for SD3. +- in1-supply: The input supply for LDO1. +... +- in20-supply: The input supply for LDO20. +- regulators: A node that houses a sub-node for each regulator within the + device. Each sub-node is identified using the regulator-compatible + property, with valid values listed below. The content of each sub-node + is defined by the standard binding for regulators; see regulator.txt. + +Valid regulator-compatible values are: + + sd1, sd2, sd3, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8, ldo9, ldo10, + ldo11, ldo12, ldo13, ldo14, ldo15, ldo16, ldo17, ldo18, ldo19, ldo20, out5v, + out33v, bbat, sdby, vrtc. + +Example: + + max8907@3c { + compatible = "maxim,max8907"; + reg = <0x3c>; + interrupts = <0 86 0x4>; + + mbatt-supply = <&some_reg>; + in-v1-supply = <&mbatt_reg>; + ... + in1-supply = <&mbatt_reg>; + ... + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + mbatt_reg: regulator@0 { + reg = <0>; + regulator-compatible = "mbatt"; + regulator-name = "vbat_pmu"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + regulator@1 { + reg = <1>; + regulator-compatible = "sd1"; + regulator-name = "nvvdd_sv1,vdd_cpu_pmu"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + regulator@2 { + reg = <2>; + regulator-compatible = "sd2"; + regulator-name = "nvvdd_sv2,vdd_core"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; +... + }; + }; + }; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d1facef28a60..856ec00ab787 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -476,6 +476,18 @@ config MFD_MAX77693 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX8907 + tristate "Maxim Semiconductor MAX8907 PMIC Support" + select MFD_CORE + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to support for Maxim Semiconductor MAX8907. This is + a Power Management IC. This driver provides common support for + accessing the device; additional drivers must be enabled in order + to use the functionality of the device. + config MFD_MAX8925 bool "Maxim Semiconductor MAX8925 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 79dd22d1dc3d..2a4108d98442 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o +obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c new file mode 100644 index 000000000000..6497c98e030d --- /dev/null +++ b/drivers/mfd/max8907.c @@ -0,0 +1,331 @@ +/* + * max8907.c - mfd driver for MAX8907 + * + * Copyright (C) 2010 Gyungoh Yoo + * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mfd_cell max8907_cells[] = { + { .name = "max8907-regulator", }, + { .name = "max8907-rtc", }, +}; + +static bool max8907_gen_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_ON_OFF_IRQ1: + case MAX8907_REG_ON_OFF_STAT: + case MAX8907_REG_ON_OFF_IRQ2: + case MAX8907_REG_CHG_IRQ1: + case MAX8907_REG_CHG_IRQ2: + case MAX8907_REG_CHG_STAT: + return true; + default: + return false; + } +} + +static bool max8907_gen_is_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_ON_OFF_IRQ1: + case MAX8907_REG_ON_OFF_IRQ2: + case MAX8907_REG_CHG_IRQ1: + case MAX8907_REG_CHG_IRQ2: + return true; + default: + return false; + } +} + +static bool max8907_gen_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return !max8907_gen_is_volatile_reg(dev, reg); +} + +static const struct regmap_config max8907_regmap_gen_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = max8907_gen_is_volatile_reg, + .precious_reg = max8907_gen_is_precious_reg, + .writeable_reg = max8907_gen_is_writeable_reg, + .max_register = MAX8907_REG_LDO20VOUT, + .cache_type = REGCACHE_RBTREE, +}; + +static bool max8907_rtc_is_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg <= MAX8907_REG_RTC_YEAR2) + return true; + + switch (reg) { + case MAX8907_REG_RTC_STATUS: + case MAX8907_REG_RTC_IRQ: + return true; + default: + return false; + } +} + +static bool max8907_rtc_is_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_RTC_IRQ: + return true; + default: + return false; + } +} + +static bool max8907_rtc_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_RTC_STATUS: + case MAX8907_REG_RTC_IRQ: + return false; + default: + return true; + } +} + +static const struct regmap_config max8907_regmap_rtc_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = max8907_rtc_is_volatile_reg, + .precious_reg = max8907_rtc_is_precious_reg, + .writeable_reg = max8907_rtc_is_writeable_reg, + .max_register = MAX8907_REG_MPL_CNTL, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_irq max8907_chg_irqs[] = { + { .reg_offset = 0, .mask = 1 << 0, }, + { .reg_offset = 0, .mask = 1 << 1, }, + { .reg_offset = 0, .mask = 1 << 2, }, + { .reg_offset = 1, .mask = 1 << 0, }, + { .reg_offset = 1, .mask = 1 << 1, }, + { .reg_offset = 1, .mask = 1 << 2, }, + { .reg_offset = 1, .mask = 1 << 3, }, + { .reg_offset = 1, .mask = 1 << 4, }, + { .reg_offset = 1, .mask = 1 << 5, }, + { .reg_offset = 1, .mask = 1 << 6, }, + { .reg_offset = 1, .mask = 1 << 7, }, +}; + +static const struct regmap_irq_chip max8907_chg_irq_chip = { + .name = "max8907 chg", + .status_base = MAX8907_REG_CHG_IRQ1, + .mask_base = MAX8907_REG_CHG_IRQ1_MASK, + .wake_base = MAX8907_REG_CHG_IRQ1_MASK, + .irq_reg_stride = MAX8907_REG_CHG_IRQ2 - MAX8907_REG_CHG_IRQ1, + .num_regs = 2, + .irqs = max8907_chg_irqs, + .num_irqs = ARRAY_SIZE(max8907_chg_irqs), +}; + +static const struct regmap_irq max8907_on_off_irqs[] = { + { .reg_offset = 0, .mask = 1 << 0, }, + { .reg_offset = 0, .mask = 1 << 1, }, + { .reg_offset = 0, .mask = 1 << 2, }, + { .reg_offset = 0, .mask = 1 << 3, }, + { .reg_offset = 0, .mask = 1 << 4, }, + { .reg_offset = 0, .mask = 1 << 5, }, + { .reg_offset = 0, .mask = 1 << 6, }, + { .reg_offset = 0, .mask = 1 << 7, }, + { .reg_offset = 1, .mask = 1 << 0, }, + { .reg_offset = 1, .mask = 1 << 1, }, +}; + +static const struct regmap_irq_chip max8907_on_off_irq_chip = { + .name = "max8907 on_off", + .status_base = MAX8907_REG_ON_OFF_IRQ1, + .mask_base = MAX8907_REG_ON_OFF_IRQ1_MASK, + .irq_reg_stride = MAX8907_REG_ON_OFF_IRQ2 - MAX8907_REG_ON_OFF_IRQ1, + .num_regs = 2, + .irqs = max8907_on_off_irqs, + .num_irqs = ARRAY_SIZE(max8907_on_off_irqs), +}; + +static const struct regmap_irq max8907_rtc_irqs[] = { + { .reg_offset = 0, .mask = 1 << 2, }, + { .reg_offset = 0, .mask = 1 << 3, }, +}; + +static const struct regmap_irq_chip max8907_rtc_irq_chip = { + .name = "max8907 rtc", + .status_base = MAX8907_REG_RTC_IRQ, + .mask_base = MAX8907_REG_RTC_IRQ_MASK, + .num_regs = 1, + .irqs = max8907_rtc_irqs, + .num_irqs = ARRAY_SIZE(max8907_rtc_irqs), +}; + +static __devinit int max8907_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max8907 *max8907; + int ret; + + max8907 = devm_kzalloc(&i2c->dev, sizeof(struct max8907), GFP_KERNEL); + if (!max8907) { + ret = -ENOMEM; + goto err_alloc_drvdata; + } + + max8907->dev = &i2c->dev; + dev_set_drvdata(max8907->dev, max8907); + + max8907->i2c_gen = i2c; + i2c_set_clientdata(i2c, max8907); + max8907->regmap_gen = devm_regmap_init_i2c(i2c, + &max8907_regmap_gen_config); + if (IS_ERR(max8907->regmap_gen)) { + ret = PTR_ERR(max8907->regmap_gen); + dev_err(&i2c->dev, "gen regmap init failed: %d\n", ret); + goto err_regmap_gen; + } + + max8907->i2c_rtc = i2c_new_dummy(i2c->adapter, MAX8907_RTC_I2C_ADDR); + if (!max8907->i2c_rtc) { + ret = -ENOMEM; + goto err_dummy_rtc; + } + i2c_set_clientdata(max8907->i2c_rtc, max8907); + max8907->regmap_rtc = devm_regmap_init_i2c(max8907->i2c_rtc, + &max8907_regmap_rtc_config); + if (IS_ERR(max8907->regmap_rtc)) { + ret = PTR_ERR(max8907->regmap_rtc); + dev_err(&i2c->dev, "rtc regmap init failed: %d\n", ret); + goto err_regmap_rtc; + } + + irq_set_status_flags(max8907->i2c_gen->irq, IRQ_NOAUTOEN); + + ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq, + IRQF_ONESHOT | IRQF_SHARED, -1, + &max8907_chg_irq_chip, + &max8907->irqc_chg); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add chg irq chip: %d\n", ret); + goto err_irqc_chg; + } + ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq, + IRQF_ONESHOT | IRQF_SHARED, -1, + &max8907_on_off_irq_chip, + &max8907->irqc_on_off); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add on off irq chip: %d\n", ret); + goto err_irqc_on_off; + } + ret = regmap_add_irq_chip(max8907->regmap_rtc, max8907->i2c_gen->irq, + IRQF_ONESHOT | IRQF_SHARED, -1, + &max8907_rtc_irq_chip, + &max8907->irqc_rtc); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add rtc irq chip: %d\n", ret); + goto err_irqc_rtc; + } + + enable_irq(max8907->i2c_gen->irq); + + ret = mfd_add_devices(max8907->dev, -1, max8907_cells, + ARRAY_SIZE(max8907_cells), NULL, 0, NULL); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add MFD devices %d\n", ret); + goto err_add_devices; + } + + return 0; + +err_add_devices: + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc); +err_irqc_rtc: + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off); +err_irqc_on_off: + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg); +err_irqc_chg: +err_regmap_rtc: + i2c_unregister_device(max8907->i2c_rtc); +err_dummy_rtc: +err_regmap_gen: +err_alloc_drvdata: + return ret; +} + +static __devexit int max8907_i2c_remove(struct i2c_client *i2c) +{ + struct max8907 *max8907 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max8907->dev); + + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc); + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off); + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg); + + i2c_unregister_device(max8907->i2c_rtc); + + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id max8907_of_match[] = { + { .compatible = "maxim,max8907" }, + { }, +}; +MODULE_DEVICE_TABLE(of, max8907_of_match); +#endif + +static const struct i2c_device_id max8907_i2c_id[] = { + {"max8907", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max8907_i2c_id); + +static struct i2c_driver max8907_i2c_driver = { + .driver = { + .name = "max8907", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max8907_of_match), + }, + .probe = max8907_i2c_probe, + .remove = max8907_i2c_remove, + .id_table = max8907_i2c_id, +}; + +static int __init max8907_i2c_init(void) +{ + int ret = -ENODEV; + + ret = i2c_add_driver(&max8907_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); + + return ret; +} +subsys_initcall(max8907_i2c_init); + +static void __exit max8907_i2c_exit(void) +{ + i2c_del_driver(&max8907_i2c_driver); +} +module_exit(max8907_i2c_exit); + +MODULE_DESCRIPTION("MAX8907 multi-function core driver"); +MODULE_AUTHOR("Gyungoh Yoo "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/max8907.h b/include/linux/mfd/max8907.h new file mode 100644 index 000000000000..283531fde4e0 --- /dev/null +++ b/include/linux/mfd/max8907.h @@ -0,0 +1,250 @@ +/* + * Functions to access MAX8907 power management chip. + * + * Copyright (C) 2010 Gyungoh Yoo + * Copyright (C) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MFD_MAX8907_H +#define __LINUX_MFD_MAX8907_H + +#include +#include + +#define MAX8907_GEN_I2C_ADDR (0x78 >> 1) +#define MAX8907_ADC_I2C_ADDR (0x8e >> 1) +#define MAX8907_RTC_I2C_ADDR (0xd0 >> 1) + +/* MAX8907 register map */ +#define MAX8907_REG_SYSENSEL 0x00 +#define MAX8907_REG_ON_OFF_IRQ1 0x01 +#define MAX8907_REG_ON_OFF_IRQ1_MASK 0x02 +#define MAX8907_REG_ON_OFF_STAT 0x03 +#define MAX8907_REG_SDCTL1 0x04 +#define MAX8907_REG_SDSEQCNT1 0x05 +#define MAX8907_REG_SDV1 0x06 +#define MAX8907_REG_SDCTL2 0x07 +#define MAX8907_REG_SDSEQCNT2 0x08 +#define MAX8907_REG_SDV2 0x09 +#define MAX8907_REG_SDCTL3 0x0A +#define MAX8907_REG_SDSEQCNT3 0x0B +#define MAX8907_REG_SDV3 0x0C +#define MAX8907_REG_ON_OFF_IRQ2 0x0D +#define MAX8907_REG_ON_OFF_IRQ2_MASK 0x0E +#define MAX8907_REG_RESET_CNFG 0x0F +#define MAX8907_REG_LDOCTL16 0x10 +#define MAX8907_REG_LDOSEQCNT16 0x11 +#define MAX8907_REG_LDO16VOUT 0x12 +#define MAX8907_REG_SDBYSEQCNT 0x13 +#define MAX8907_REG_LDOCTL17 0x14 +#define MAX8907_REG_LDOSEQCNT17 0x15 +#define MAX8907_REG_LDO17VOUT 0x16 +#define MAX8907_REG_LDOCTL1 0x18 +#define MAX8907_REG_LDOSEQCNT1 0x19 +#define MAX8907_REG_LDO1VOUT 0x1A +#define MAX8907_REG_LDOCTL2 0x1C +#define MAX8907_REG_LDOSEQCNT2 0x1D +#define MAX8907_REG_LDO2VOUT 0x1E +#define MAX8907_REG_LDOCTL3 0x20 +#define MAX8907_REG_LDOSEQCNT3 0x21 +#define MAX8907_REG_LDO3VOUT 0x22 +#define MAX8907_REG_LDOCTL4 0x24 +#define MAX8907_REG_LDOSEQCNT4 0x25 +#define MAX8907_REG_LDO4VOUT 0x26 +#define MAX8907_REG_LDOCTL5 0x28 +#define MAX8907_REG_LDOSEQCNT5 0x29 +#define MAX8907_REG_LDO5VOUT 0x2A +#define MAX8907_REG_LDOCTL6 0x2C +#define MAX8907_REG_LDOSEQCNT6 0x2D +#define MAX8907_REG_LDO6VOUT 0x2E +#define MAX8907_REG_LDOCTL7 0x30 +#define MAX8907_REG_LDOSEQCNT7 0x31 +#define MAX8907_REG_LDO7VOUT 0x32 +#define MAX8907_REG_LDOCTL8 0x34 +#define MAX8907_REG_LDOSEQCNT8 0x35 +#define MAX8907_REG_LDO8VOUT 0x36 +#define MAX8907_REG_LDOCTL9 0x38 +#define MAX8907_REG_LDOSEQCNT9 0x39 +#define MAX8907_REG_LDO9VOUT 0x3A +#define MAX8907_REG_LDOCTL10 0x3C +#define MAX8907_REG_LDOSEQCNT10 0x3D +#define MAX8907_REG_LDO10VOUT 0x3E +#define MAX8907_REG_LDOCTL11 0x40 +#define MAX8907_REG_LDOSEQCNT11 0x41 +#define MAX8907_REG_LDO11VOUT 0x42 +#define MAX8907_REG_LDOCTL12 0x44 +#define MAX8907_REG_LDOSEQCNT12 0x45 +#define MAX8907_REG_LDO12VOUT 0x46 +#define MAX8907_REG_LDOCTL13 0x48 +#define MAX8907_REG_LDOSEQCNT13 0x49 +#define MAX8907_REG_LDO13VOUT 0x4A +#define MAX8907_REG_LDOCTL14 0x4C +#define MAX8907_REG_LDOSEQCNT14 0x4D +#define MAX8907_REG_LDO14VOUT 0x4E +#define MAX8907_REG_LDOCTL15 0x50 +#define MAX8907_REG_LDOSEQCNT15 0x51 +#define MAX8907_REG_LDO15VOUT 0x52 +#define MAX8907_REG_OUT5VEN 0x54 +#define MAX8907_REG_OUT5VSEQ 0x55 +#define MAX8907_REG_OUT33VEN 0x58 +#define MAX8907_REG_OUT33VSEQ 0x59 +#define MAX8907_REG_LDOCTL19 0x5C +#define MAX8907_REG_LDOSEQCNT19 0x5D +#define MAX8907_REG_LDO19VOUT 0x5E +#define MAX8907_REG_LBCNFG 0x60 +#define MAX8907_REG_SEQ1CNFG 0x64 +#define MAX8907_REG_SEQ2CNFG 0x65 +#define MAX8907_REG_SEQ3CNFG 0x66 +#define MAX8907_REG_SEQ4CNFG 0x67 +#define MAX8907_REG_SEQ5CNFG 0x68 +#define MAX8907_REG_SEQ6CNFG 0x69 +#define MAX8907_REG_SEQ7CNFG 0x6A +#define MAX8907_REG_LDOCTL18 0x72 +#define MAX8907_REG_LDOSEQCNT18 0x73 +#define MAX8907_REG_LDO18VOUT 0x74 +#define MAX8907_REG_BBAT_CNFG 0x78 +#define MAX8907_REG_CHG_CNTL1 0x7C +#define MAX8907_REG_CHG_CNTL2 0x7D +#define MAX8907_REG_CHG_IRQ1 0x7E +#define MAX8907_REG_CHG_IRQ2 0x7F +#define MAX8907_REG_CHG_IRQ1_MASK 0x80 +#define MAX8907_REG_CHG_IRQ2_MASK 0x81 +#define MAX8907_REG_CHG_STAT 0x82 +#define MAX8907_REG_WLED_MODE_CNTL 0x84 +#define MAX8907_REG_ILED_CNTL 0x84 +#define MAX8907_REG_II1RR 0x8E +#define MAX8907_REG_II2RR 0x8F +#define MAX8907_REG_LDOCTL20 0x9C +#define MAX8907_REG_LDOSEQCNT20 0x9D +#define MAX8907_REG_LDO20VOUT 0x9E + +/* RTC register map */ +#define MAX8907_REG_RTC_SEC 0x00 +#define MAX8907_REG_RTC_MIN 0x01 +#define MAX8907_REG_RTC_HOURS 0x02 +#define MAX8907_REG_RTC_WEEKDAY 0x03 +#define MAX8907_REG_RTC_DATE 0x04 +#define MAX8907_REG_RTC_MONTH 0x05 +#define MAX8907_REG_RTC_YEAR1 0x06 +#define MAX8907_REG_RTC_YEAR2 0x07 +#define MAX8907_REG_ALARM0_SEC 0x08 +#define MAX8907_REG_ALARM0_MIN 0x09 +#define MAX8907_REG_ALARM0_HOURS 0x0A +#define MAX8907_REG_ALARM0_WEEKDAY 0x0B +#define MAX8907_REG_ALARM0_DATE 0x0C +#define MAX8907_REG_ALARM0_MONTH 0x0D +#define MAX8907_REG_ALARM0_YEAR1 0x0E +#define MAX8907_REG_ALARM0_YEAR2 0x0F +#define MAX8907_REG_ALARM1_SEC 0x10 +#define MAX8907_REG_ALARM1_MIN 0x11 +#define MAX8907_REG_ALARM1_HOURS 0x12 +#define MAX8907_REG_ALARM1_WEEKDAY 0x13 +#define MAX8907_REG_ALARM1_DATE 0x14 +#define MAX8907_REG_ALARM1_MONTH 0x15 +#define MAX8907_REG_ALARM1_YEAR1 0x16 +#define MAX8907_REG_ALARM1_YEAR2 0x17 +#define MAX8907_REG_ALARM0_CNTL 0x18 +#define MAX8907_REG_ALARM1_CNTL 0x19 +#define MAX8907_REG_RTC_STATUS 0x1A +#define MAX8907_REG_RTC_CNTL 0x1B +#define MAX8907_REG_RTC_IRQ 0x1C +#define MAX8907_REG_RTC_IRQ_MASK 0x1D +#define MAX8907_REG_MPL_CNTL 0x1E + +/* ADC and Touch Screen Controller register map */ +#define MAX8907_CTL 0 +#define MAX8907_SEQCNT 1 +#define MAX8907_VOUT 2 + +/* mask bit fields */ +#define MAX8907_MASK_LDO_SEQ 0x1C +#define MAX8907_MASK_LDO_EN 0x01 +#define MAX8907_MASK_VBBATTCV 0x03 +#define MAX8907_MASK_OUT5V_VINEN 0x10 +#define MAX8907_MASK_OUT5V_ENSRC 0x0E +#define MAX8907_MASK_OUT5V_EN 0x01 + +/* Regulator IDs */ +#define MAX8907_MBATT 0 +#define MAX8907_SD1 1 +#define MAX8907_SD2 2 +#define MAX8907_SD3 3 +#define MAX8907_LDO1 4 +#define MAX8907_LDO2 5 +#define MAX8907_LDO3 6 +#define MAX8907_LDO4 7 +#define MAX8907_LDO5 8 +#define MAX8907_LDO6 9 +#define MAX8907_LDO7 10 +#define MAX8907_LDO8 11 +#define MAX8907_LDO9 12 +#define MAX8907_LDO10 13 +#define MAX8907_LDO11 14 +#define MAX8907_LDO12 15 +#define MAX8907_LDO13 16 +#define MAX8907_LDO14 17 +#define MAX8907_LDO15 18 +#define MAX8907_LDO16 19 +#define MAX8907_LDO17 20 +#define MAX8907_LDO18 21 +#define MAX8907_LDO19 22 +#define MAX8907_LDO20 23 +#define MAX8907_OUT5V 24 +#define MAX8907_OUT33V 25 +#define MAX8907_BBAT 26 +#define MAX8907_SDBY 27 +#define MAX8907_VRTC 28 +#define MAX8907_NUM_REGULATORS (MAX8907_VRTC + 1) + +/* IRQ definitions */ +enum { + MAX8907_IRQ_VCHG_DC_OVP = 0, + MAX8907_IRQ_VCHG_DC_F, + MAX8907_IRQ_VCHG_DC_R, + MAX8907_IRQ_VCHG_THM_OK_R, + MAX8907_IRQ_VCHG_THM_OK_F, + MAX8907_IRQ_VCHG_MBATTLOW_F, + MAX8907_IRQ_VCHG_MBATTLOW_R, + MAX8907_IRQ_VCHG_RST, + MAX8907_IRQ_VCHG_DONE, + MAX8907_IRQ_VCHG_TOPOFF, + MAX8907_IRQ_VCHG_TMR_FAULT, + + MAX8907_IRQ_GPM_RSTIN = 0, + MAX8907_IRQ_GPM_MPL, + MAX8907_IRQ_GPM_SW_3SEC, + MAX8907_IRQ_GPM_EXTON_F, + MAX8907_IRQ_GPM_EXTON_R, + MAX8907_IRQ_GPM_SW_1SEC, + MAX8907_IRQ_GPM_SW_F, + MAX8907_IRQ_GPM_SW_R, + MAX8907_IRQ_GPM_SYSCKEN_F, + MAX8907_IRQ_GPM_SYSCKEN_R, + + MAX8907_IRQ_RTC_ALARM1 = 0, + MAX8907_IRQ_RTC_ALARM0, +}; + +struct max8907_platform_data { + struct regulator_init_data *init_data[MAX8907_NUM_REGULATORS]; +}; + +struct regmap_irq_chips_data; + +struct max8907 { + struct device *dev; + struct mutex irq_lock; + struct i2c_client *i2c_gen; + struct i2c_client *i2c_rtc; + struct regmap *regmap_gen; + struct regmap *regmap_rtc; + struct regmap_irq_chip_data *irqc_chg; + struct regmap_irq_chip_data *irqc_on_off; + struct regmap_irq_chip_data *irqc_rtc; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 87d687301f380729ec320619f100f3ba39f3693d Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 5 Sep 2012 10:57:13 +0800 Subject: mfd: Add syscon driver based on regmap Add regmap based syscon driver. This is usually used for access misc bits in registers which does not belong to a specific module, for example, IMX IOMUXC GPR and ANATOP. With this driver, client can use generic regmap API to access registers which are registered into syscon. Reviewed-by: Mark Brown Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/syscon.txt | 20 +++ drivers/mfd/Kconfig | 8 ++ drivers/mfd/Makefile | 1 + drivers/mfd/syscon.c | 176 +++++++++++++++++++++++ include/linux/mfd/syscon.h | 23 +++ 5 files changed, 228 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/syscon.txt create mode 100644 drivers/mfd/syscon.c create mode 100644 include/linux/mfd/syscon.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/syscon.txt b/Documentation/devicetree/bindings/mfd/syscon.txt new file mode 100644 index 000000000000..fe8150bb3248 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/syscon.txt @@ -0,0 +1,20 @@ +* System Controller Registers R/W driver + +System controller node represents a register region containing a set +of miscellaneous registers. The registers are not cohesive enough to +represent as any specific type of device. The typical use-case is for +some other node's driver, or platform-specific code, to acquire a +reference to the syscon node (e.g. by phandle, node path, or search +using a specific compatible value), interrogate the node (or associated +OS driver) to determine the location of the registers, and access the +registers directly. + +Required properties: +- compatible: Should contain "syscon". +- reg: the register region can be accessed from syscon + +Examples: +gpr: iomuxc-gpr@020e0000 { + compatible = "fsl,imx6q-iomuxc-gpr", "syscon"; + reg = <0x020e0000 0x38>; +}; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 856ec00ab787..909d84b1f5c1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1004,6 +1004,14 @@ config MFD_ANATOP MFD controller. This controller embeds regulator and thermal devices for Freescale i.MX platforms. +config MFD_SYSCON + bool "System Controller Register R/W Based on Regmap" + depends on OF + select REGMAP_MMIO + help + Select this option to enable accessing system control registers + via regmap. + config MFD_PALMAS bool "Support for the TI Palmas series chips" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2a4108d98442..07b66aff30b4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -132,4 +132,5 @@ obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o +obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c new file mode 100644 index 000000000000..65fe609026cc --- /dev/null +++ b/drivers/mfd/syscon.c @@ -0,0 +1,176 @@ +/* + * System Control Driver + * + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Dong Aisheng + * + * 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 + +static struct platform_driver syscon_driver; + +struct syscon { + struct device *dev; + void __iomem *base; + struct regmap *regmap; +}; + +static int syscon_match(struct device *dev, void *data) +{ + struct syscon *syscon = dev_get_drvdata(dev); + struct device_node *dn = data; + + return (syscon->dev->of_node == dn) ? 1 : 0; +} + +struct regmap *syscon_node_to_regmap(struct device_node *np) +{ + struct syscon *syscon; + struct device *dev; + + dev = driver_find_device(&syscon_driver.driver, NULL, np, + syscon_match); + if (!dev) + return ERR_PTR(-EPROBE_DEFER); + + syscon = dev_get_drvdata(dev); + + return syscon->regmap; +} +EXPORT_SYMBOL_GPL(syscon_node_to_regmap); + +struct regmap *syscon_regmap_lookup_by_compatible(const char *s) +{ + struct device_node *syscon_np; + struct regmap *regmap; + + syscon_np = of_find_compatible_node(NULL, NULL, s); + if (!syscon_np) + return ERR_PTR(-ENODEV); + + regmap = syscon_node_to_regmap(syscon_np); + of_node_put(syscon_np); + + return regmap; +} +EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); + +struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, + const char *property) +{ + struct device_node *syscon_np; + struct regmap *regmap; + + syscon_np = of_parse_phandle(np, property, 0); + if (!syscon_np) + return ERR_PTR(-ENODEV); + + regmap = syscon_node_to_regmap(syscon_np); + of_node_put(syscon_np); + + return regmap; +} +EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); + +static const struct of_device_id of_syscon_match[] = { + { .compatible = "syscon", }, + { }, +}; + +static struct regmap_config syscon_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int __devinit syscon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct syscon *syscon; + struct resource res; + int ret; + + if (!np) + return -ENOENT; + + syscon = devm_kzalloc(dev, sizeof(struct syscon), + GFP_KERNEL); + if (!syscon) + return -ENOMEM; + + syscon->base = of_iomap(np, 0); + if (!syscon->base) + return -EADDRNOTAVAIL; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + + syscon_regmap_config.max_register = res.end - res.start - 3; + syscon->regmap = devm_regmap_init_mmio(dev, syscon->base, + &syscon_regmap_config); + if (IS_ERR(syscon->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(syscon->regmap); + } + + syscon->dev = dev; + platform_set_drvdata(pdev, syscon); + + dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n", + res.start, res.end); + + return 0; +} + +static int __devexit syscon_remove(struct platform_device *pdev) +{ + struct syscon *syscon; + + syscon = platform_get_drvdata(pdev); + iounmap(syscon->base); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver syscon_driver = { + .driver = { + .name = "syscon", + .owner = THIS_MODULE, + .of_match_table = of_syscon_match, + }, + .probe = syscon_probe, + .remove = __devexit_p(syscon_remove), +}; + +static int __init syscon_init(void) +{ + return platform_driver_register(&syscon_driver); +} +postcore_initcall(syscon_init); + +static void __exit syscon_exit(void) +{ + platform_driver_unregister(&syscon_driver); +} +module_exit(syscon_exit); + +MODULE_AUTHOR("Dong Aisheng "); +MODULE_DESCRIPTION("System Control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h new file mode 100644 index 000000000000..6aeb6b8da64d --- /dev/null +++ b/include/linux/mfd/syscon.h @@ -0,0 +1,23 @@ +/* + * System Control Driver + * + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Dong Aisheng + * + * 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. + */ + +#ifndef __LINUX_MFD_SYSCON_H__ +#define __LINUX_MFD_SYSCON_H__ + +extern struct regmap *syscon_node_to_regmap(struct device_node *np); +extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); +extern struct regmap *syscon_regmap_lookup_by_phandle( + struct device_node *np, + const char *property); +#endif /* __LINUX_MFD_SYSCON_H__ */ -- cgit v1.2.3-59-g8ed1b From 019a7e6b7b312b17cd74b45d09d4ec17486c4088 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 10 Sep 2012 13:46:25 +0300 Subject: mfd: twl4030-audio: Add DT support Support for loading the twl4030 audio module via devicetree. Sub devices for codec and vibra will be created as mfd devices once the core MFD driver is loaded when the kernel is booted with a DT blob. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/mfd/twl4030-audio.txt | 46 ++++++++++++++++++ drivers/mfd/twl4030-audio.c | 54 +++++++++++++++++++--- 2 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/twl4030-audio.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/twl4030-audio.txt b/Documentation/devicetree/bindings/mfd/twl4030-audio.txt new file mode 100644 index 000000000000..414d2ae0adf6 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/twl4030-audio.txt @@ -0,0 +1,46 @@ +Texas Instruments TWL family (twl4030) audio module + +The audio module inside the TWL family consist of an audio codec and a vibra +driver. + +Required properties: +- compatible : must be "ti,twl4030-audio" + +Optional properties, nodes: + +Audio functionality: +- codec { }: Need to be present if the audio functionality is used. Within this + section the following options can be used: +- ti,digimic_delay: Delay need after enabling the digimic to reduce artifacts + from the start of the recorded sample (in ms) +-ti,ramp_delay_value: HS ramp delay configuration to reduce pop noise +-ti,hs_extmute: Use external mute for HS pop reduction +-ti,hs_extmute_gpio: Use external GPIO to control the external mute +-ti,offset_cncl_path: Offset cancellation path selection, refer to TRM for the + valid values. + +Vibra functionality +- ti,enable-vibra: Need to be set to <1> if the vibra functionality is used. if + missing or it is 0, the vibra functionality is disabled. + +Example: +&i2c1 { + clock-frequency = <2600000>; + + twl: twl@48 { + reg = <0x48>; + interrupts = <7>; /* SYS_NIRQ cascaded to intc */ + interrupt-parent = <&intc>; + + twl_audio: audio { + compatible = "ti,twl4030-audio"; + + ti,enable-vibra = <1>; + + codec { + ti,ramp_delay_value = <3>; + }; + + }; + }; +}; diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index 31605fa02ca1..5c11acf9e0fd 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -156,15 +158,42 @@ unsigned int twl4030_audio_get_mclk(void) } EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); +static bool twl4030_audio_has_codec(struct twl4030_audio_data *pdata, + struct device_node *node) +{ + if (pdata && pdata->codec) + return true; + + if (of_find_node_by_name(node, "codec")) + return true; + + return false; +} + +static bool twl4030_audio_has_vibra(struct twl4030_audio_data *pdata, + struct device_node *node) +{ + int vibra; + + if (pdata && pdata->vibra) + return true; + + if (!of_property_read_u32(node, "ti,enable-vibra", &vibra) && vibra) + return true; + + return false; +} + static int __devinit twl4030_audio_probe(struct platform_device *pdev) { struct twl4030_audio *audio; struct twl4030_audio_data *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; struct mfd_cell *cell = NULL; int ret, childs = 0; u8 val; - if (!pdata) { + if (!pdata && !node) { dev_err(&pdev->dev, "Platform data is missing\n"); return -EINVAL; } @@ -202,18 +231,22 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev) audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; - if (pdata->codec) { + if (twl4030_audio_has_codec(pdata, node)) { cell = &audio->cells[childs]; cell->name = "twl4030-codec"; - cell->platform_data = pdata->codec; - cell->pdata_size = sizeof(*pdata->codec); + if (pdata) { + cell->platform_data = pdata->codec; + cell->pdata_size = sizeof(*pdata->codec); + } childs++; } - if (pdata->vibra) { + if (twl4030_audio_has_vibra(pdata, node)) { cell = &audio->cells[childs]; cell->name = "twl4030-vibra"; - cell->platform_data = pdata->vibra; - cell->pdata_size = sizeof(*pdata->vibra); + if (pdata) { + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); + } childs++; } @@ -245,10 +278,17 @@ static int __devexit twl4030_audio_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id twl4030_audio_of_match[] = { + {.compatible = "ti,twl4030-audio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl4030_audio_of_match); + static struct platform_driver twl4030_audio_driver = { .driver = { .owner = THIS_MODULE, .name = "twl4030-audio", + .of_match_table = twl4030_audio_of_match, }, .probe = twl4030_audio_probe, .remove = __devexit_p(twl4030_audio_remove), -- cgit v1.2.3-59-g8ed1b From 9582fdcb6fcf1b596a83b161a2ea886272d2a62f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 18 Sep 2012 16:51:19 -0600 Subject: mfd: max8907: Add power off control Add DT property "maxim,system-power-controller" to indicate whether the PMIC is in charge of controlling the system power. If this is set, the driver will provide the pm_power_off() function. Signed-off-by: Stephen Warren Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/regulator/max8907.txt | 6 ++++++ drivers/mfd/max8907.c | 20 ++++++++++++++++++++ include/linux/mfd/max8907.h | 2 ++ 3 files changed, 28 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/max8907.txt b/Documentation/devicetree/bindings/regulator/max8907.txt index 4fd847f0afc0..51e683b2bf47 100644 --- a/Documentation/devicetree/bindings/regulator/max8907.txt +++ b/Documentation/devicetree/bindings/regulator/max8907.txt @@ -16,6 +16,10 @@ Required properties: property, with valid values listed below. The content of each sub-node is defined by the standard binding for regulators; see regulator.txt. +Optional properties: +- maxim,system-power-controller: Boolean property indicating that the PMIC + controls the overall system power. + Valid regulator-compatible values are: sd1, sd2, sd3, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8, ldo9, ldo10, @@ -29,6 +33,8 @@ Example: reg = <0x3c>; interrupts = <0 86 0x4>; + maxim,system-power-controller; + mbatt-supply = <&some_reg>; in-v1-supply = <&mbatt_reg>; ... diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c index 6497c98e030d..2303641dd052 100644 --- a/drivers/mfd/max8907.c +++ b/drivers/mfd/max8907.c @@ -176,11 +176,26 @@ static const struct regmap_irq_chip max8907_rtc_irq_chip = { .num_irqs = ARRAY_SIZE(max8907_rtc_irqs), }; +struct max8907 *max8907_pm_off; +static void max8907_power_off(void) +{ + regmap_update_bits(max8907_pm_off->regmap_gen, MAX8907_REG_RESET_CNFG, + MAX8907_MASK_POWER_OFF, MAX8907_MASK_POWER_OFF); +} + static __devinit int max8907_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max8907 *max8907; int ret; + struct max8907_platform_data *pdata = dev_get_platdata(&i2c->dev); + bool pm_off = false; + + if (pdata) + pm_off = pdata->pm_off; + else if (i2c->dev.of_node) + pm_off = of_property_read_bool(i2c->dev.of_node, + "maxim,system-power-controller"); max8907 = devm_kzalloc(&i2c->dev, sizeof(struct max8907), GFP_KERNEL); if (!max8907) { @@ -251,6 +266,11 @@ static __devinit int max8907_i2c_probe(struct i2c_client *i2c, goto err_add_devices; } + if (pm_off && !pm_power_off) { + max8907_pm_off = max8907; + pm_power_off = max8907_power_off; + } + return 0; err_add_devices: diff --git a/include/linux/mfd/max8907.h b/include/linux/mfd/max8907.h index 283531fde4e0..b06f7a6a1e80 100644 --- a/include/linux/mfd/max8907.h +++ b/include/linux/mfd/max8907.h @@ -167,6 +167,7 @@ #define MAX8907_MASK_OUT5V_VINEN 0x10 #define MAX8907_MASK_OUT5V_ENSRC 0x0E #define MAX8907_MASK_OUT5V_EN 0x01 +#define MAX8907_MASK_POWER_OFF 0x40 /* Regulator IDs */ #define MAX8907_MBATT 0 @@ -231,6 +232,7 @@ enum { struct max8907_platform_data { struct regulator_init_data *init_data[MAX8907_NUM_REGULATORS]; + bool pm_off; }; struct regmap_irq_chips_data; -- cgit v1.2.3-59-g8ed1b From 5cbe786a6e32e80149f7b29def50b2bf563f6628 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 16 Aug 2012 15:13:14 +0300 Subject: mfd: twl6040: Add twl6040-gpio child Add needed platform data structure and code to be able to load the GPO child of twl6040. Update the devicetree binding documentation at the same time. Signed-off-by: Sergio Aguirre Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/twl6040.txt | 9 ++++++--- drivers/mfd/twl6040-core.c | 15 +++++++++++++++ include/linux/mfd/twl6040.h | 9 ++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/twl6040.txt b/Documentation/devicetree/bindings/mfd/twl6040.txt index c855240f3a0e..0f5dd709d752 100644 --- a/Documentation/devicetree/bindings/mfd/twl6040.txt +++ b/Documentation/devicetree/bindings/mfd/twl6040.txt @@ -1,7 +1,7 @@ Texas Instruments TWL6040 family -The TWL6040s are 8-channel high quality low-power audio codecs providing audio -and vibra functionality on OMAP4+ platforms. +The TWL6040s are 8-channel high quality low-power audio codecs providing audio, +vibra and GPO functionality on OMAP4+ platforms. They are connected ot the host processor via i2c for commands, McPDM for audio data and commands. @@ -10,6 +10,8 @@ Required properties: - reg: must be 0x4b for i2c address - interrupts: twl6040 has one interrupt line connecteded to the main SoC - interrupt-parent: The parent interrupt controller +- gpio-controller: +- #gpio-cells = <1>: twl6040 provides GPO lines. - twl6040,audpwron-gpio: Power on GPIO line for the twl6040 - vio-supply: Regulator for the twl6040 VIO supply @@ -37,7 +39,6 @@ Example: &i2c1 { twl6040: twl@4b { compatible = "ti,twl6040"; - reg = <0x4b>; interrupts = <0 119 4>; interrupt-parent = <&gic>; @@ -60,3 +61,5 @@ Example: }; }; }; + +/include/ "twl6040.dtsi" diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 348e1de8e7c1..3f2a1cf02fc0 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -631,6 +631,21 @@ static int __devinit twl6040_probe(struct i2c_client *client, children++; } + /* + * Enable the GPO driver in the following cases: + * DT booted kernel or legacy boot with valid gpo platform_data + */ + if (!pdata || (pdata && pdata->gpo)) { + cell = &twl6040->cells[children]; + cell->name = "twl6040-gpo"; + + if (pdata) { + cell->platform_data = pdata->gpo; + cell->pdata_size = sizeof(*pdata->gpo); + } + children++; + } + ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, NULL, 0, NULL); if (ret) diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 269b7067d360..8991532d4391 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -158,7 +158,7 @@ #define TWL6040_VIBROCDET 0x20 #define TWL6040_TSHUTDET 0x40 -#define TWL6040_CELLS 2 +#define TWL6040_CELLS 3 #define TWL6040_REV_ES1_0 0x00 #define TWL6040_REV_ES1_1 0x01 /* Rev ES1.1 and ES1.2 */ @@ -176,6 +176,8 @@ #define TWL6040_SYSCLK_SEL_LPPLL 0 #define TWL6040_SYSCLK_SEL_HPPLL 1 +#define TWL6040_GPO_MAX 3 + struct twl6040_codec_data { u16 hs_left_step; u16 hs_right_step; @@ -192,12 +194,17 @@ struct twl6040_vibra_data { int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */ }; +struct twl6040_gpo_data { + int gpio_base; +}; + struct twl6040_platform_data { int audpwron_gpio; /* audio power-on gpio */ unsigned int irq_base; struct twl6040_codec_data *codec; struct twl6040_vibra_data *vibra; + struct twl6040_gpo_data *gpo; }; struct regmap; -- cgit v1.2.3-59-g8ed1b From 396f70be54f026a6601012a73db87dc75c6d8f1d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 24 Sep 2012 13:25:03 -0600 Subject: mfd: max8907: Remove regulator-compatible from DT docs Commit "regulator: deprecate regulator-compatible DT property" deprecated the use of the regulator-compatible DT property. Update the DT example in the MAX8907 binding documentation to reflect this. Signed-off-by: Stephen Warren Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/regulator/max8907.txt | 24 ++++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/max8907.txt b/Documentation/devicetree/bindings/regulator/max8907.txt index 51e683b2bf47..371eccd1cd68 100644 --- a/Documentation/devicetree/bindings/regulator/max8907.txt +++ b/Documentation/devicetree/bindings/regulator/max8907.txt @@ -12,15 +12,16 @@ Required properties: ... - in20-supply: The input supply for LDO20. - regulators: A node that houses a sub-node for each regulator within the - device. Each sub-node is identified using the regulator-compatible - property, with valid values listed below. The content of each sub-node - is defined by the standard binding for regulators; see regulator.txt. + device. Each sub-node is identified using the node's name (or the deprecated + regulator-compatible property if present), with valid values listed below. + The content of each sub-node is defined by the standard binding for + regulators; see regulator.txt. Optional properties: - maxim,system-power-controller: Boolean property indicating that the PMIC controls the overall system power. -Valid regulator-compatible values are: +The valid names for regulators are: sd1, sd2, sd3, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8, ldo9, ldo10, ldo11, ldo12, ldo13, ldo14, ldo15, ldo16, ldo17, ldo18, ldo19, ldo20, out5v, @@ -42,30 +43,21 @@ Example: ... regulators { - #address-cells = <1>; - #size-cells = <0>; - - mbatt_reg: regulator@0 { - reg = <0>; - regulator-compatible = "mbatt"; + mbatt_reg: mbatt { regulator-name = "vbat_pmu"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-always-on; }; - regulator@1 { - reg = <1>; - regulator-compatible = "sd1"; + sd1 { regulator-name = "nvvdd_sv1,vdd_cpu_pmu"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; }; - regulator@2 { - reg = <2>; - regulator-compatible = "sd2"; + sd2 { regulator-name = "nvvdd_sv2,vdd_core"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; -- cgit v1.2.3-59-g8ed1b From 2e57d56747e601b3e0ff6697e524025d0504d161 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Fri, 21 Sep 2012 18:06:52 +0800 Subject: mfd: 88pm860x: Device tree support Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/88pm860x.txt | 85 ++++++++++++++++++++++ .../devicetree/bindings/regulator/88pm860x.txt | 30 ++++++++ .../bindings/video/backlight/88pm860x.txt | 15 ++++ drivers/input/touchscreen/88pm860x-ts.c | 46 ++++++++---- drivers/leds/leds-88pm860x.c | 33 ++++++++- drivers/mfd/88pm860x-core.c | 62 +++++++++++++--- drivers/regulator/88pm8607.c | 35 ++++++++- drivers/rtc/rtc-88pm860x.c | 43 ++++++++--- drivers/video/backlight/88pm860x_bl.c | 39 +++++++++- include/linux/mfd/88pm860x.h | 4 +- 10 files changed, 351 insertions(+), 41 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/88pm860x.txt create mode 100644 Documentation/devicetree/bindings/regulator/88pm860x.txt create mode 100644 Documentation/devicetree/bindings/video/backlight/88pm860x.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/88pm860x.txt b/Documentation/devicetree/bindings/mfd/88pm860x.txt new file mode 100644 index 000000000000..63f3ee33759c --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/88pm860x.txt @@ -0,0 +1,85 @@ +* Marvell 88PM860x Power Management IC + +Required parent device properties: +- compatible : "marvell,88pm860x" +- reg : the I2C slave address for the 88pm860x chip +- interrupts : IRQ line for the 88pm860x chip +- interrupt-controller: describes the 88pm860x as an interrupt controller (has its own domain) +- #interrupt-cells : should be 1. + - The cell is the 88pm860x local IRQ number + +Optional parent device properties: +- marvell,88pm860x-irq-read-clr: inicates whether interrupt status is cleared by read +- marvell,88pm860x-slave-addr: 88pm860x are two chips solution. stores the I2C address + of one chip, and this property stores the I2C address of + another chip. + +88pm860x consists of a large and varied group of sub-devices: + +Device Supply Names Description +------ ------------ ----------- +88pm860x-onkey : : On key +88pm860x-rtc : : RTC +88pm8607 : : Regulators +88pm860x-backlight : : Backlight +88pm860x-led : : Led +88pm860x-touch : : Touchscreen + +Example: + + pmic: 88pm860x@34 { + compatible = "marvell,88pm860x"; + reg = <0x34>; + interrupts = <4>; + interrupt-parent = <&intc>; + interrupt-controller; + #interrupt-cells = <1>; + + marvell,88pm860x-irq-read-clr; + marvell,88pm860x-slave-addr = <0x11>; + + regulators { + BUCK1 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1500000>; + regulator-boot-on; + regulator-always-on; + }; + LDO1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <2800000>; + regulator-boot-on; + regulator-always-on; + }; + }; + rtc { + marvell,88pm860x-vrtc = <1>; + }; + touch { + marvell,88pm860x-gpadc-prebias = <1>; + marvell,88pm860x-gpadc-slot-cycle = <1>; + marvell,88pm860x-tsi-prebias = <6>; + marvell,88pm860x-pen-prebias = <16>; + marvell,88pm860x-pen-prechg = <2>; + marvell,88pm860x-resistor-X = <300>; + }; + backlights { + backlight-0 { + marvell,88pm860x-iset = <4>; + marvell,88pm860x-pwm = <3>; + }; + backlight-2 { + }; + }; + leds { + led0-red { + marvell,88pm860x-iset = <12>; + }; + led0-green { + marvell,88pm860x-iset = <12>; + }; + led0-blue { + marvell,88pm860x-iset = <12>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/regulator/88pm860x.txt b/Documentation/devicetree/bindings/regulator/88pm860x.txt new file mode 100644 index 000000000000..1267b3e1a2cc --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/88pm860x.txt @@ -0,0 +1,30 @@ +Marvell 88PM860x regulator + +Required properties: +- compatible: "marvell,88pm860x" +- reg: I2C slave address +- regulators: A node that houses a sub-node for each regulator within the + device. Each sub-node is identified using the regulator-compatible + property, with valid values listed below. + +Example: + + pmic: 88pm860x@34 { + compatible = "marvell,88pm860x"; + reg = <0x34>; + + regulators { + BUCK1 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1500000>; + regulator-boot-on; + regulator-always-on; + }; + BUCK3 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/video/backlight/88pm860x.txt b/Documentation/devicetree/bindings/video/backlight/88pm860x.txt new file mode 100644 index 000000000000..261df2799315 --- /dev/null +++ b/Documentation/devicetree/bindings/video/backlight/88pm860x.txt @@ -0,0 +1,15 @@ +88pm860x-backlight bindings + +Optional properties: + - marvell,88pm860x-iset: Current supplies on backlight device. + - marvell,88pm860x-pwm: PWM frequency on backlight device. + +Example: + + backlights { + backlight-0 { + marvell,88pm860x-iset = <4>; + marvell,88pm860x-pwm = <3>; + }; + backlight-2 { + }; diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index 05f30b73c3c3..4f81e6eb74b6 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -10,6 +10,7 @@ */ #include #include +#include #include #include #include @@ -113,14 +114,31 @@ 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, + int *res_x) +{ + struct device_node *np = pdev->dev.parent->of_node; + 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; + } + of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x); + return 0; +} +#else +#define pm860x_touch_dt_init(x, y) (-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; + int irq, ret, res_x = 0; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -128,15 +146,13 @@ 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, &res_x)) { + if (pdata) + res_x = pdata->res_x; + else { + dev_err(&pdev->dev, "failed to get platform data\n"); + return -EINVAL; + } } touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); @@ -159,8 +175,8 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev) 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->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 70232b1756f9..b7e8cc0957fc 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -123,6 +124,33 @@ static void pm860x_led_set(struct led_classdev *cdev, schedule_work(&data->work); } +#ifdef CONFIG_OF +static int pm860x_led_dt_init(struct platform_device *pdev, + struct pm860x_led *data) +{ + struct device_node *nproot = pdev->dev.parent->of_node, *np; + int iset = 0; + if (!nproot) + return -ENODEV; + nproot = of_find_node_by_name(nproot, "leds"); + if (!nproot) { + dev_err(&pdev->dev, "failed to find leds node\n"); + return -ENODEV; + } + for_each_child_of_node(nproot, np) { + if (!of_node_cmp(np->name, data->name)) { + of_property_read_u32(np, "marvell,88pm860x-iset", + &iset); + data->iset = PM8606_LED_CURRENT(iset); + break; + } + } + return 0; +} +#else +#define pm860x_led_dt_init(x, y) (-1) +#endif + static int pm860x_led_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -179,8 +207,9 @@ static int pm860x_led_probe(struct platform_device *pdev) data->chip = chip; data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; data->port = pdev->id; - if (pdata && pdata->iset) - data->iset = pdata->iset; + if (pm860x_led_dt_init(pdev, data)) + if (pdata) + data->iset = pdata->iset; data->current_brightness = 0; data->cdev.name = data->name; diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 5b56fe8250b4..fdaf6861ce95 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -1112,12 +1114,6 @@ static void __devexit pm860x_device_exit(struct pm860x_chip *chip) mfd_remove_devices(chip->dev); } -static const struct i2c_device_id pm860x_id_table[] = { - { "88PM860x", 0 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, pm860x_id_table); - static int verify_addr(struct i2c_client *i2c) { unsigned short addr_8607[] = {0x30, 0x34}; @@ -1144,21 +1140,52 @@ static struct regmap_config pm860x_regmap_config = { .val_bits = 8, }; +static int __devinit pm860x_dt_init(struct device_node *np, + struct device *dev, + struct pm860x_platform_data *pdata) +{ + int ret; + + if (of_get_property(np, "marvell,88pm860x-irq-read-clr", NULL)) + pdata->irq_mode = 1; + ret = of_property_read_u32(np, "marvell,88pm860x-slave-addr", + &pdata->companion_addr); + if (ret) { + dev_err(dev, "Not found \"marvell,88pm860x-slave-addr\" " + "property\n"); + pdata->companion_addr = 0; + } + return 0; +} + static int __devinit pm860x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pm860x_platform_data *pdata = client->dev.platform_data; + struct device_node *node = client->dev.of_node; struct pm860x_chip *chip; int ret; - if (!pdata) { + if (node && !pdata) { + /* parse DT to get platform data */ + pdata = devm_kzalloc(&client->dev, + sizeof(struct pm860x_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + ret = pm860x_dt_init(node, &client->dev, pdata); + if (ret) + goto err; + } else if (!pdata) { pr_info("No platform data in %s!\n", __func__); return -EINVAL; } chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; + if (chip == NULL) { + ret = -ENOMEM; + goto err; + } chip->id = verify_addr(client); chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config); @@ -1198,6 +1225,10 @@ static int __devinit pm860x_probe(struct i2c_client *client, pm860x_device_init(chip, pdata); return 0; +err: + if (node) + devm_kfree(&client->dev, pdata); + return ret; } static int __devexit pm860x_remove(struct i2c_client *client) @@ -1238,11 +1269,24 @@ static int pm860x_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume); +static const struct i2c_device_id pm860x_id_table[] = { + { "88PM860x", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pm860x_id_table); + +static const struct of_device_id pm860x_dt_ids[] = { + { .compatible = "marvell,88pm860x", }, + {}, +}; +MODULE_DEVICE_TABLE(of, pm860x_dt_ids); + static struct i2c_driver pm860x_driver = { .driver = { .name = "88PM860x", .owner = THIS_MODULE, .pm = &pm860x_pm_ops, + .of_match_table = of_match_ptr(pm860x_dt_ids), }, .probe = pm860x_probe, .remove = __devexit_p(pm860x_remove), diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index f96fbe38ff6f..1c5ab0172ea2 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -364,6 +366,34 @@ static struct pm8607_regulator_info pm8606_regulator_info[] = { PM8606_PREG(PREREGULATORB, 5), }; +#ifdef CONFIG_OF +static int pm8607_regulator_dt_init(struct platform_device *pdev, + struct pm8607_regulator_info *info, + struct regulator_config *config) +{ + struct device_node *nproot, *np; + nproot = pdev->dev.parent->of_node; + if (!nproot) + return -ENODEV; + nproot = of_find_node_by_name(nproot, "regulators"); + if (!nproot) { + dev_err(&pdev->dev, "failed to find regulators node\n"); + return -ENODEV; + } + for_each_child_of_node(nproot, np) { + if (!of_node_cmp(np->name, info->desc.name)) { + config->init_data = + of_get_regulator_init_data(&pdev->dev, np); + config->of_node = np; + break; + } + } + return 0; +} +#else +#define pm8607_regulator_dt_init(x, y, z) (-1) +#endif + static int __devinit pm8607_regulator_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -402,9 +432,12 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) info->slope_double = 1; config.dev = &pdev->dev; - config.init_data = pdata; config.driver_data = info; + if (pm8607_regulator_dt_init(pdev, info, &config)) + if (pdata) + config.init_data = pdata; + if (chip->id == CHIP_PM8607) config.regmap = chip->regmap; else diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index feddefc42109..de9e854b326a 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -284,6 +285,28 @@ out: } #endif +#ifdef CONFIG_OF +static int __devinit pm860x_rtc_dt_init(struct platform_device *pdev, + struct pm860x_rtc_info *info) +{ + struct device_node *np = pdev->dev.parent->of_node; + int ret; + if (!np) + return -ENODEV; + np = of_find_node_by_name(np, "rtc"); + if (!np) { + dev_err(&pdev->dev, "failed to find rtc node\n"); + return -ENODEV; + } + ret = of_property_read_u32(np, "marvell,88pm860x-vrtc", &info->vrtc); + if (ret) + info->vrtc = 0; + return 0; +} +#else +#define pm860x_rtc_dt_init(x, y) (-1) +#endif + static int __devinit pm860x_rtc_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -294,8 +317,6 @@ static int __devinit pm860x_rtc_probe(struct platform_device *pdev) int ret; pdata = pdev->dev.platform_data; - if (pdata == NULL) - dev_warn(&pdev->dev, "No platform data!\n"); info = kzalloc(sizeof(struct pm860x_rtc_info), GFP_KERNEL); if (!info) @@ -345,9 +366,11 @@ static int __devinit pm860x_rtc_probe(struct platform_device *pdev) } } rtc_tm_to_time(&tm, &ticks); - if (pdata && pdata->sync) { - pdata->sync(ticks); - info->sync = pdata->sync; + if (pm860x_rtc_dt_init(pdev, info)) { + if (pdata && pdata->sync) { + pdata->sync(ticks); + info->sync = pdata->sync; + } } info->rtc_dev = rtc_device_register("88pm860x-rtc", &pdev->dev, @@ -366,10 +389,12 @@ static int __devinit pm860x_rtc_probe(struct platform_device *pdev) #ifdef VRTC_CALIBRATION /* <00> -- 2.7V, <01> -- 2.9V, <10> -- 3.1V, <11> -- 3.3V */ - if (pdata && pdata->vrtc) - info->vrtc = pdata->vrtc & 0x3; - else - info->vrtc = 1; + if (pm860x_rtc_dt_init(pdev, info)) { + if (pdata && pdata->vrtc) + info->vrtc = pdata->vrtc & 0x3; + else + info->vrtc = 1; + } pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, MEAS2_VRTC); /* calibrate VRTC */ diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index 965161cacefa..b7ec34c57f46 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -159,6 +160,36 @@ static const struct backlight_ops pm860x_backlight_ops = { .get_brightness = pm860x_backlight_get_brightness, }; +#ifdef CONFIG_OF +static int pm860x_backlight_dt_init(struct platform_device *pdev, + struct pm860x_backlight_data *data, + char *name) +{ + struct device_node *nproot = pdev->dev.parent->of_node, *np; + int iset = 0; + if (!nproot) + return -ENODEV; + nproot = of_find_node_by_name(nproot, "backlights"); + if (!nproot) { + dev_err(&pdev->dev, "failed to find backlights node\n"); + return -ENODEV; + } + for_each_child_of_node(nproot, np) { + if (!of_node_cmp(np->name, name)) { + of_property_read_u32(np, "marvell,88pm860x-iset", + &iset); + data->iset = PM8606_WLED_CURRENT(iset); + of_property_read_u32(np, "marvell,88pm860x-pwm", + &data->pwm); + break; + } + } + return 0; +} +#else +#define pm860x_backlight_dt_init(x, y, z) (-1) +#endif + static int pm860x_backlight_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -203,9 +234,11 @@ static int pm860x_backlight_probe(struct platform_device *pdev) data->i2c = (chip->id == CHIP_PM8606) ? chip->client \ : chip->companion; data->current_brightness = MAX_BRIGHTNESS; - if (pdata) { - data->pwm = pdata->pwm; - data->iset = pdata->iset; + if (pm860x_backlight_dt_init(pdev, data, name)) { + if (pdata) { + data->pwm = pdata->pwm; + data->iset = pdata->iset; + } } memset(&props, 0, sizeof(struct backlight_properties)); diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index d515e5c438f0..ef3e6b701179 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -306,7 +306,7 @@ struct pm860x_chip { struct regmap *regmap_companion; int buck3_double; /* DVC ramp slope double */ - unsigned short companion_addr; + int companion_addr; unsigned short osc_vote; int id; int irq_mode; @@ -376,7 +376,7 @@ struct pm860x_platform_data { struct regulator_init_data *ldo_vibrator; struct regulator_init_data *ldo14; - unsigned short companion_addr; /* I2C address of companion chip */ + int companion_addr; /* I2C address of companion chip */ int i2c_port; /* Controlled by GI2C or PI2C */ int irq_mode; /* Clear interrupt by read/write(0/1) */ int irq_base; /* IRQ base number of 88pm860x */ -- cgit v1.2.3-59-g8ed1b