This patchset adds to the support for the Palmas series of PMIC chips.
Some of the patches have previously been submitted individually.
The DT bindings doc has been added first due to comments that it was missing.
Patches based on linux-next-20130319
Changes since v8
The .compatible = "palmas-charger-xxx" has been removed from the DT
for all palmas source files as the charger driver is not yet present.
[PATCH 01/12] mfd: DT bindings... - Added in missing descriptions for
properties and added in properties that were missing
[PATCH 08/12] gpio: Palmas.... - Added
palmas_gpio_(read|write|update) gpio parameter renamed to offset
palmas_gpio_write renamed to palmas_gpio_clear_and_set
as it no longer takes a data value instead it always sets the
offset bit and clears all other bits -
this is how it was being used by the callers
[PATCH 09/12] leds: Add support... - Combined patches for
drivers/leds/leds-palmas.c, drivers/leds/Kconfig and drivers/leds/Makefile.
palmas_dt_to_pdata - added bounds checking for DT values and
removed use of child node.
palmas_led.on_time and palmas_led.period both changed to enums.
palmas_get_crtl_and_period added which contains factored code
from palmas_leds_work for maintainability.
[PATCH 10/12] clk: Add a clock... - clock names taken from DT if provided
palmas-regulator not in patchset - patch Now applied by Mark Brown
[PATCH 12/12] regulator: palmas-charger-pmic removed from DT compatible list
.../devicetree/bindings/clock/palmas-clk.txt | 27 +
.../devicetree/bindings/gpio/gpio-palmas.txt | 35 ++
.../devicetree/bindings/input/palmas-pwrbutton.txt | 26 +
.../devicetree/bindings/leds/leds-palmas.txt | 36 ++
Documentation/devicetree/bindings/mfd/palmas.txt | 81 +++
.../devicetree/bindings/regulator/palmas-pmic.txt | 168 ++++++
.../devicetree/bindings/rtc/palmas-rtc.txt | 21 +
.../devicetree/bindings/watchdog/palmas-wdt.txt | 21 +
drivers/clk/Kconfig | 8 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-palmas.c | 275 +++++++++
drivers/gpio/gpio-palmas.c | 90 +++-
drivers/leds/Kconfig | 9 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-palmas.c | 590 ++++++++++++++++++++
drivers/mfd/palmas.c | 102 +++-
drivers/regulator/palmas-regulator.c | 1 -
drivers/watchdog/Kconfig | 8 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/palmas_wdt.c | 169 ++++++
include/linux/mfd/palmas.h | 139 +++++-
21 files changed, 1768 insertions(+), 41 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/palmas-clk.txt
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-palmas.txt
create mode 100644 Documentation/devicetree/bindings/input/palmas-pwrbutton.txt
create mode 100644 Documentation/devicetree/bindings/leds/leds-palmas.txt
create mode 100644 Documentation/devicetree/bindings/mfd/palmas.txt
create mode 100644 Documentation/devicetree/bindings/regulator/palmas-pmic.txt
create mode 100644 Documentation/devicetree/bindings/rtc/palmas-rtc.txt
create mode 100644 Documentation/devicetree/bindings/watchdog/palmas-wdt.txt
create mode 100644 drivers/clk/clk-palmas.c
create mode 100644 drivers/leds/leds-palmas.c
create mode 100644 drivers/watchdog/palmas_wdt.c
From: J Keerthy <[email protected]>
Enable DT support for palmas gpio.
Signed-off-by: J Keerthy <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
Acked-by: Linus Walleij <[email protected]>
---
drivers/gpio/gpio-palmas.c | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index 636648c..04da57a 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -208,9 +208,22 @@ static int palmas_gpio_remove(struct platform_device *pdev)
return gpiochip_remove(&palmas_gpio->gpio_chip);
}
+static struct of_device_id of_palmas_match_tbl[] = {
+ { .compatible = "ti,palmas-gpio", },
+ { .compatible = "ti,palmas-charger-gpio", },
+ { .compatible = "ti,twl6035-gpio", },
+ { .compatible = "ti,twl6036-gpio", },
+ { .compatible = "ti,twl6037-gpio", },
+ { .compatible = "ti,tps65913-gpio", },
+ { .compatible = "ti,tps65914-gpio", },
+ { .compatible = "ti,tps80036-gpio", },
+ { /* end */ }
+};
+
static struct platform_driver palmas_gpio_driver = {
.driver.name = "palmas-gpio",
.driver.owner = THIS_MODULE,
+ .driver.of_match_table = of_palmas_match_tbl,
.probe = palmas_gpio_probe,
.remove = palmas_gpio_remove,
};
--
1.7.0.4
Signed-off-by: Ian Lartey <[email protected]>
---
drivers/regulator/palmas-regulator.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index a9b558e..6b2d829 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -803,7 +803,6 @@ static int palmas_remove(struct platform_device *pdev)
static struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas-pmic", },
- { .compatible = "ti,palmas-charger-pmic", },
{ .compatible = "ti,twl6035-pmic", },
{ .compatible = "ti,twl6036-pmic", },
{ .compatible = "ti,twl6037-pmic", },
--
1.7.0.4
Palmas charger has 16 GPIOs
add palmas_gpio_[read|write|update] api to take account
second bank of GPIOs
Signed-off-by: Ian Lartey <[email protected]>
Signed-off-by: Graeme Gregory <[email protected]>
---
drivers/gpio/gpio-palmas.c | 77 ++++++++++++++++++++++++++++++++++++--------
include/linux/mfd/palmas.h | 12 ++++++-
2 files changed, 74 insertions(+), 15 deletions(-)
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index e3a4e56..636648c 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -1,7 +1,7 @@
/*
- * TI Palma series PMIC's GPIO driver.
+ * TI Palmas series PMIC's GPIO driver.
*
- * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2012, NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <[email protected]>
*
@@ -31,6 +31,36 @@ struct palmas_gpio {
struct palmas *palmas;
};
+static int palmas_gpio_read(struct palmas *palmas, unsigned int reg,
+ int gpio, unsigned int *dest)
+{
+ /* registers for second bank are identical and offset by 0x9 */
+ if (gpio > 7)
+ reg += PALMAS_GPIO_DATA_IN2;
+
+ return palmas_read(palmas, PALMAS_GPIO_BASE, reg, dest);
+}
+
+static int palmas_gpio_write(struct palmas *palmas, unsigned int reg,
+ int gpio, unsigned int data)
+{
+ /* registers for second bank are identical and offset by 0x9 */
+ if (gpio > 7)
+ reg += PALMAS_GPIO_DATA_IN2;
+
+ return palmas_write(palmas, PALMAS_GPIO_BASE, reg, data);
+}
+
+static int palmas_gpio_update_bits(struct palmas *palmas, unsigned int reg,
+ int gpio, unsigned int mask, unsigned int data)
+{
+ /* registers for second bank are identical and offset by 0x9 */
+ if (gpio > 7)
+ reg += PALMAS_GPIO_DATA_IN2;
+
+ return palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, mask, data);
+}
+
static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct palmas_gpio, gpio_chip);
@@ -43,12 +73,12 @@ static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)
unsigned int val;
int ret;
- ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_IN, &val);
+ ret = palmas_gpio_read(palmas, PALMAS_GPIO_DATA_IN, offset, &val);
if (ret < 0) {
dev_err(gc->dev, "GPIO_DATA_IN read failed, err = %d\n", ret);
return ret;
}
- return !!(val & BIT(offset));
+ return !!(val & BIT(offset % 8));
}
static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
@@ -59,11 +89,11 @@ static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
int ret;
if (value)
- ret = palmas_write(palmas, PALMAS_GPIO_BASE,
- PALMAS_GPIO_SET_DATA_OUT, BIT(offset));
+ ret = palmas_gpio_write(palmas, PALMAS_GPIO_SET_DATA_OUT,
+ offset, BIT(offset % 8));
else
- ret = palmas_write(palmas, PALMAS_GPIO_BASE,
- PALMAS_GPIO_CLEAR_DATA_OUT, BIT(offset));
+ ret = palmas_gpio_write(palmas, PALMAS_GPIO_CLEAR_DATA_OUT,
+ offset, BIT(offset % 8));
if (ret < 0)
dev_err(gc->dev, "%s write failed, err = %d\n",
(value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT",
@@ -80,8 +110,8 @@ static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,
/* Set the initial value */
palmas_gpio_set(gc, offset, value);
- ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE,
- PALMAS_GPIO_DATA_DIR, BIT(offset), BIT(offset));
+ ret = palmas_gpio_update_bits(palmas, PALMAS_GPIO_DATA_DIR,
+ offset, 1 << (offset % 8), 1 << (offset % 8));
if (ret < 0)
dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
return ret;
@@ -93,8 +123,8 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
struct palmas *palmas = pg->palmas;
int ret;
- ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE,
- PALMAS_GPIO_DATA_DIR, BIT(offset), 0);
+ ret = palmas_gpio_update_bits(palmas, PALMAS_GPIO_DATA_DIR,
+ offset, 1 << (offset % 8), 0);
if (ret < 0)
dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
return ret;
@@ -108,7 +138,21 @@ static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset);
}
-static int palmas_gpio_probe(struct platform_device *pdev)
+static int palmas_gpio_set_debounce(struct gpio_chip *gc, unsigned offset,
+ unsigned debounce)
+{
+ struct palmas_gpio *palmas_gpio = to_palmas_gpio(gc);
+ struct palmas *palmas = palmas_gpio->palmas;
+ unsigned int data = 0;
+
+ if (debounce)
+ data = 0xff;
+
+ return palmas_gpio_update_bits(palmas, PALMAS_GPIO_DEBOUNCE_EN,
+ offset, 1 << (offset % 8), data);
+}
+
+static int palmas_gpio_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
struct palmas_platform_data *palmas_pdata;
@@ -125,10 +169,15 @@ static int palmas_gpio_probe(struct platform_device *pdev)
palmas_gpio->palmas = palmas;
palmas_gpio->gpio_chip.owner = THIS_MODULE;
palmas_gpio->gpio_chip.label = dev_name(&pdev->dev);
- palmas_gpio->gpio_chip.ngpio = 8;
+ /* palmas charger has 16 gpios */
+ if (is_palmas_charger(palmas->product_id))
+ palmas_gpio->gpio_chip.ngpio = 16;
+ else
+ palmas_gpio->gpio_chip.ngpio = 8;
palmas_gpio->gpio_chip.can_sleep = 1;
palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
+ palmas_gpio->gpio_chip.set_debounce = palmas_gpio_set_debounce,
palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
palmas_gpio->gpio_chip.set = palmas_gpio_set;
palmas_gpio->gpio_chip.get = palmas_gpio_get;
diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
index 4cffe31..d8c303b 100644
--- a/include/linux/mfd/palmas.h
+++ b/include/linux/mfd/palmas.h
@@ -244,7 +244,7 @@ struct palmas_platform_data {
* then the two value to load into the registers if true
*/
int mux_from_pdata;
- u8 pad1, pad2;
+ u8 pad1, pad2, pad3, pad4;
struct palmas_pmic_platform_data *pmic_pdata;
struct palmas_gpadc_platform_data *gpadc_pdata;
@@ -2513,6 +2513,16 @@ enum usb_irq_events {
#define PALMAS_PU_PD_GPIO_CTRL1 0x6
#define PALMAS_PU_PD_GPIO_CTRL2 0x7
#define PALMAS_OD_OUTPUT_GPIO_CTRL 0x8
+#define PALMAS_GPIO_DATA_IN2 0x9
+#define PALMAS_GPIO_DATA_DIR2 0xA
+#define PALMAS_GPIO_DATA_OUT2 0xB
+#define PALMAS_GPIO_DEBOUNCE_EN2 0xC
+#define PALMAS_GPIO_CLEAR_DATA_OUT2 0xD
+#define PALMAS_GPIO_SET_DATA_OUT2 0xE
+#define PALMAS_PU_PD_GPIO_CTRL3 0xF
+#define PALMAS_PU_PD_GPIO_CTRL4 0x10
+#define PALMAS_OD_OUTPUT_GPIO_CTRL2 0x11
+#define PALMAS_GPO_CTRL 0x12
/* Bit definitions for GPIO_DATA_IN */
#define PALMAS_GPIO_DATA_IN_GPIO_7_IN 0x80
--
1.7.0.4
Signed-off-by: Graeme Gregory <[email protected]>
Signed-off-by: J Keerthy <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
---
drivers/clk/Kconfig | 8 ++++++++
drivers/clk/Makefile | 1 +
2 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a47e6ee..a8e0655 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -63,6 +63,14 @@ config CLK_TWL6040
McPDM. McPDM module is using the external bit clock on the McPDM bus
as functional clock.
+config CLK_PALMAS
+ tristate "Palmas PMIC Clock Support"
+ depends on MFD_PALMAS
+ ---help---
+ Enable clock support for the two clocks on Palmas PMIC chips:
+ - A 32KHz audio-gated clock (primarily for audio devices)
+ - A digitally-gated 32KHz clock
+
endmenu
source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 0147022..34f8d97 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -35,3 +35,4 @@ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
+obj-$(CONFIG_CLK_PALMAS) += clk-palmas.o
--
1.7.0.4
is_palmas_charger checks for the presence of charging
functionality in the device
Signed-off-by: Graeme Gregory <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
Acked-by: Laxman Dewangani <[email protected]>
---
include/linux/mfd/palmas.h | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
index 3bbda22..4a066d0 100644
--- a/include/linux/mfd/palmas.h
+++ b/include/linux/mfd/palmas.h
@@ -1,9 +1,10 @@
/*
* TI Palmas
*
- * Copyright 2011 Texas Instruments Inc.
+ * Copyright 2011-2013 Texas Instruments Inc.
*
* Author: Graeme Gregory <[email protected]>
+ * Author: Ian Lartey <[email protected]>
*
* 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
@@ -22,6 +23,15 @@
#define PALMAS_NUM_CLIENTS 3
+/* The ID_REVISION NUMBERS */
+#define PALMAS_CHIP_OLD_ID 0x0000
+#define PALMAS_CHIP_ID 0xC035
+#define PALMAS_CHIP_CHARGER_ID 0xC036
+
+#define is_palmas(a) (((a) == PALMAS_CHIP_OLD_ID) || \
+ ((a) == PALMAS_CHIP_ID))
+#define is_palmas_charger(a) ((a) == PALMAS_CHIP_CHARGER_ID)
+
struct palmas_pmic;
struct palmas_gpadc;
struct palmas_resource;
--
1.7.0.4
Palmas has two clock generators in it, clk32kg and clk32kg audio. They
are fixed frequency clocks that only have enable/disable functionality.
Signed-off-by: Graeme Gregory <[email protected]>
Signed-off-by: J Keerthy <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
Acked-by: Mike Turquette <[email protected]>
---
drivers/clk/clk-palmas.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 275 insertions(+), 0 deletions(-)
create mode 100644 drivers/clk/clk-palmas.c
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
new file mode 100644
index 0000000..aea7710
--- /dev/null
+++ b/drivers/clk/clk-palmas.c
@@ -0,0 +1,275 @@
+/*
+ * PALMAS resource clock module driver
+ *
+ * Copyright (C) 2011-2013 Texas Instruments Inc.
+ * Graeme Gregory <[email protected]>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/palmas.h>
+#include <linux/clk-provider.h>
+#include <linux/of_platform.h>
+
+struct palmas_clk {
+ struct palmas *palmas;
+ struct device *dev;
+ struct clk_hw clk32kg;
+ struct clk_hw clk32kgaudio;
+ int clk32kgaudio_mode_sleep;
+ int clk32kg_mode_sleep;
+};
+
+static int palmas_clock_setbits(struct palmas *palmas, unsigned int reg,
+ unsigned int data)
+{
+ return palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
+ reg, data, data);
+}
+
+static int palmas_clock_clrbits(struct palmas *palmas, unsigned int reg,
+ unsigned int data)
+{
+ return palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
+ reg, data, 0);
+}
+
+static int palmas_prepare_clk32kg(struct clk_hw *hw)
+{
+ struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+ clk32kg);
+ int ret;
+
+ ret = palmas_clock_setbits(palmas_clk->palmas,
+ PALMAS_CLK32KG_CTRL, PALMAS_CLK32KG_CTRL_MODE_ACTIVE);
+ if (ret)
+ dev_err(palmas_clk->dev, "Failed to enable clk32kg: %d\n", ret);
+
+ return ret;
+}
+
+static void palmas_unprepare_clk32kg(struct clk_hw *hw)
+{
+ struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+ clk32kg);
+ int ret;
+
+ ret = palmas_clock_clrbits(palmas_clk->palmas,
+ PALMAS_CLK32KG_CTRL, PALMAS_CLK32KG_CTRL_MODE_ACTIVE);
+ if (ret)
+ dev_err(palmas_clk->dev, "Failed to enable clk32kg: %d\n", ret);
+
+ return;
+}
+
+static const struct clk_ops palmas_clk32kg_ops = {
+ .prepare = palmas_prepare_clk32kg,
+ .unprepare = palmas_unprepare_clk32kg,
+};
+
+static int palmas_prepare_clk32kgaudio(struct clk_hw *hw)
+{
+ struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+ clk32kgaudio);
+ int ret;
+
+ ret = palmas_clock_setbits(palmas_clk->palmas,
+ PALMAS_CLK32KGAUDIO_CTRL, PALMAS_CLK32KGAUDIO_CTRL_MODE_ACTIVE);
+ if (ret)
+ dev_err(palmas_clk->dev,
+ "Failed to enable clk32kgaudio: %d\n", ret);
+
+ return ret;
+}
+
+static void palmas_unprepare_clk32kgaudio(struct clk_hw *hw)
+{
+ struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+ clk32kgaudio);
+ int ret;
+
+ ret = palmas_clock_clrbits(palmas_clk->palmas,
+ PALMAS_CLK32KGAUDIO_CTRL, PALMAS_CLK32KGAUDIO_CTRL_MODE_ACTIVE);
+ if (ret)
+ dev_err(palmas_clk->dev,
+ "Failed to enable clk32kgaudio: %d\n", ret);
+
+ return;
+}
+
+static const struct clk_ops palmas_clk32kgaudio_ops = {
+ .prepare = palmas_prepare_clk32kgaudio,
+ .unprepare = palmas_unprepare_clk32kgaudio,
+};
+
+static int palmas_initialise_clk(struct palmas_clk *palmas_clk)
+{
+ int ret;
+
+ if (palmas_clk->clk32kg_mode_sleep) {
+ ret = palmas_clock_setbits(palmas_clk->palmas,
+ PALMAS_CLK32KG_CTRL, PALMAS_CLK32KG_CTRL_MODE_SLEEP);
+ if (ret)
+ return ret;
+ }
+
+ if (palmas_clk->clk32kgaudio_mode_sleep) {
+ ret = palmas_clock_setbits(palmas_clk->palmas,
+ PALMAS_CLK32KGAUDIO_CTRL,
+ PALMAS_CLK32KGAUDIO_CTRL_MODE_SLEEP);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void palmas_dt_to_pdata(struct device_node *node,
+ struct palmas_clk_platform_data *pdata,
+ const char **name1, const char **name2)
+{
+ int ret;
+ u32 prop;
+
+ ret = of_property_read_u32(node, "ti,clk32kg-mode-sleep", &prop);
+ if (!ret)
+ pdata->clk32kg_mode_sleep = prop;
+
+ ret = of_property_read_u32(node, "ti,clk32kgaudio-mode-sleep", &prop);
+ if (!ret)
+ pdata->clk32kgaudio_mode_sleep = prop;
+
+ of_property_read_string_index(node, "clock-names", 1, name1);
+
+ of_property_read_string_index(node, "clock-names", 2, name2);
+}
+
+static int palmas_clk_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_clk_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct palmas_clk *palmas_clk;
+ struct clk *clk;
+ struct clk_init_data init_clk32g, init_clk32gaudio;
+ int ret;
+
+ if (node && !pdata) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+
+ if (!pdata)
+ return -ENOMEM;
+
+ palmas_dt_to_pdata(node, pdata, &init_clk32g.name,
+ &init_clk32gaudio.name);
+ }
+
+ palmas_clk = devm_kzalloc(&pdev->dev, sizeof(*palmas_clk), GFP_KERNEL);
+ if (!palmas_clk)
+ return -ENOMEM;
+
+ palmas_clk->palmas = palmas;
+ palmas_clk->dev = &pdev->dev;
+
+ if (!init_clk32g.name)
+ init_clk32g.name = "clk32kg";
+ init_clk32g.ops = &palmas_clk32kg_ops;
+ init_clk32g.parent_names = NULL;
+ init_clk32g.num_parents = 0;
+ palmas_clk->clk32kg.init = &init_clk32g;
+
+ clk = clk_register(palmas_clk->dev, &palmas_clk->clk32kg);
+ if (IS_ERR(clk)) {
+ dev_dbg(&pdev->dev, "clk32kg clock register failed %ld\n",
+ PTR_ERR(clk));
+ ret = PTR_ERR(clk);
+ goto err_clk32kg;
+ }
+
+ if (!init_clk32gaudio.name)
+ init_clk32gaudio.name = "clk32kgaudio";
+ init_clk32gaudio.ops = &palmas_clk32kgaudio_ops;
+ init_clk32gaudio.parent_names = NULL;
+ init_clk32gaudio.num_parents = 0;
+ palmas_clk->clk32kgaudio.init = &init_clk32gaudio;
+
+ clk = clk_register(palmas_clk->dev, &palmas_clk->clk32kgaudio);
+ if (IS_ERR(clk)) {
+ dev_dbg(&pdev->dev, "clk32kgaudio clock register failed %ld\n",
+ PTR_ERR(clk));
+ ret = PTR_ERR(clk);
+ goto err_audio;
+ }
+
+ ret = palmas_initialise_clk(palmas_clk);
+ if (ret)
+ goto err;
+
+ dev_set_drvdata(&pdev->dev, palmas_clk);
+
+ return 0;
+
+err:
+ clk_unregister(palmas_clk->clk32kgaudio.clk);
+err_audio:
+ clk_unregister(palmas_clk->clk32kg.clk);
+err_clk32kg:
+
+ return ret;
+}
+
+static int palmas_clk_remove(struct platform_device *pdev)
+{
+ struct palmas_clk *palmas_clk = dev_get_drvdata(&pdev->dev);
+
+ clk_unregister(palmas_clk->clk32kgaudio.clk);
+ clk_unregister(palmas_clk->clk32kg.clk);
+
+ return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+ { .compatible = "ti,palmas-clk", },
+ { .compatible = "ti,twl6035-clk", },
+ { .compatible = "ti,twl6036-clk", },
+ { .compatible = "ti,twl6037-clk", },
+ { .compatible = "ti,tps65913-clk", },
+ { .compatible = "ti,tps65914-clk", },
+ { .compatible = "ti,tps80036-clk", },
+ { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
+
+static struct platform_driver palmas_clk_driver = {
+ .probe = palmas_clk_probe,
+ .remove = palmas_clk_remove,
+ .driver = {
+ .name = "palmas-clk",
+ .of_match_table = of_palmas_match_tbl,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(palmas_clk_driver);
+
+MODULE_AUTHOR("Graeme Gregory <[email protected]>");
+MODULE_DESCRIPTION("PALMAS clock driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-clk");
--
1.7.0.4
palmas_gpio_(read|write|update) gpio parameter renamed to offset
palmas_gpio_write renamed to palmas_gpio_clear_and_set
as it no longer takes a data value instead it always sets the
offset bit and clears all other bits -
Signed-off-by: Ian Lartey <[email protected]>
---
drivers/gpio/gpio-palmas.c | 32 ++++++++++++++++----------------
1 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index 04da57a..689c633 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -32,30 +32,31 @@ struct palmas_gpio {
};
static int palmas_gpio_read(struct palmas *palmas, unsigned int reg,
- int gpio, unsigned int *dest)
+ int offset, unsigned int *dest)
{
/* registers for second bank are identical and offset by 0x9 */
- if (gpio > 7)
+ if (offset > 7)
reg += PALMAS_GPIO_DATA_IN2;
return palmas_read(palmas, PALMAS_GPIO_BASE, reg, dest);
}
-static int palmas_gpio_write(struct palmas *palmas, unsigned int reg,
- int gpio, unsigned int data)
+/* Note: This procedure sets 1 bit and clears _all_ others */
+static int palmas_gpio_clear_and_set(struct palmas *palmas, unsigned int reg,
+ int offset)
{
/* registers for second bank are identical and offset by 0x9 */
- if (gpio > 7)
+ if (offset > 7)
reg += PALMAS_GPIO_DATA_IN2;
- return palmas_write(palmas, PALMAS_GPIO_BASE, reg, data);
+ return palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset % 8));
}
static int palmas_gpio_update_bits(struct palmas *palmas, unsigned int reg,
- int gpio, unsigned int mask, unsigned int data)
+ int offset, unsigned int mask, unsigned int data)
{
/* registers for second bank are identical and offset by 0x9 */
- if (gpio > 7)
+ if (offset > 7)
reg += PALMAS_GPIO_DATA_IN2;
return palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, mask, data);
@@ -89,11 +90,11 @@ static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
int ret;
if (value)
- ret = palmas_gpio_write(palmas, PALMAS_GPIO_SET_DATA_OUT,
- offset, BIT(offset % 8));
+ ret = palmas_gpio_clear_and_set(palmas,
+ PALMAS_GPIO_SET_DATA_OUT, offset);
else
- ret = palmas_gpio_write(palmas, PALMAS_GPIO_CLEAR_DATA_OUT,
- offset, BIT(offset % 8));
+ ret = palmas_gpio_clear_and_set(palmas,
+ PALMAS_GPIO_CLEAR_DATA_OUT, offset);
if (ret < 0)
dev_err(gc->dev, "%s write failed, err = %d\n",
(value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT",
@@ -111,7 +112,7 @@ static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,
palmas_gpio_set(gc, offset, value);
ret = palmas_gpio_update_bits(palmas, PALMAS_GPIO_DATA_DIR,
- offset, 1 << (offset % 8), 1 << (offset % 8));
+ offset, BIT(offset % 8), BIT(offset % 8));
if (ret < 0)
dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
return ret;
@@ -124,7 +125,7 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
int ret;
ret = palmas_gpio_update_bits(palmas, PALMAS_GPIO_DATA_DIR,
- offset, 1 << (offset % 8), 0);
+ offset, BIT(offset % 8), 0);
if (ret < 0)
dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
return ret;
@@ -149,7 +150,7 @@ static int palmas_gpio_set_debounce(struct gpio_chip *gc, unsigned offset,
data = 0xff;
return palmas_gpio_update_bits(palmas, PALMAS_GPIO_DEBOUNCE_EN,
- offset, 1 << (offset % 8), data);
+ offset, BIT(offset % 8), data);
}
static int palmas_gpio_probe(struct platform_device *pdev)
@@ -210,7 +211,6 @@ static int palmas_gpio_remove(struct platform_device *pdev)
static struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas-gpio", },
- { .compatible = "ti,palmas-charger-gpio", },
{ .compatible = "ti,twl6035-gpio", },
{ .compatible = "ti,twl6036-gpio", },
{ .compatible = "ti,twl6037-gpio", },
--
1.7.0.4
The Palmas familly of chips has LED support. This is not always muxed
to output pins so depending on the setting of the mux this driver
will create the appropriate LED class devices.
Signed-off-by: Graeme Gregory <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
---
drivers/leds/Kconfig | 9 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-palmas.c | 590 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/palmas.h | 80 ++++++
4 files changed, 680 insertions(+), 0 deletions(-)
create mode 100644 drivers/leds/leds-palmas.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index d44806d..249027e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -479,6 +479,15 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
+config LEDS_PALMAS
+ bool "LED support for the Palmas family of PMICs"
+ depends on LEDS_CLASS
+ depends on MFD_PALMAS
+ help
+ This option enables the driver for LED1 & LED2 pins on Palmas PMIC
+ if these pins are enabled in the mux configuration. The driver support
+ ON/OFF and blinking with hardware control.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ac28977..3acb116 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_PALMAS) += leds-palmas.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-palmas.c b/drivers/leds/leds-palmas.c
new file mode 100644
index 0000000..6bfdab2
--- /dev/null
+++ b/drivers/leds/leds-palmas.c
@@ -0,0 +1,590 @@
+/*
+ * Driver for LED part of Palmas PMIC Chips
+ *
+ * Copyright 2011-2013 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <[email protected]>
+ * Author: Ian Lartey <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/mfd/palmas.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct palmas_leds_data;
+
+enum palmas_led_on_time {
+ LED_ON_62_5MS = 0x00,
+ LED_ON_125MS = 0x01,
+ LED_ON_250MS = 0x02,
+ LED_ON_500MS = 0x03,
+};
+
+enum palmas_led_period {
+ LED_PERIOD_OFF = 0x00,
+ LED_PERIOD_125MS = 0x01,
+ LED_PERIOD_250MS = 0x02,
+ LED_PERIOD_500MS = 0x03,
+ LED_PERIOD_1000MS = 0x04,
+ LED_PERIOD_2000MS = 0x05,
+ LED_PERIOD_4000MS = 0x06,
+ LED_PERIOD_8000MS = 0x07,
+};
+
+struct palmas_led {
+ struct led_classdev cdev;
+ struct palmas_leds_data *leds_data;
+ int led_no;
+ struct work_struct work;
+ struct mutex mutex;
+
+ spinlock_t value_lock;
+
+ int blink;
+ enum palmas_led_on_time on_time;
+ enum palmas_led_period period;
+ enum led_brightness brightness;
+};
+
+struct palmas_leds_data {
+ struct device *dev;
+ struct led_classdev cdev;
+ struct palmas *palmas;
+
+ struct palmas_led *palmas_led;
+ int no_leds;
+};
+
+#define to_palmas_led(led_cdev) \
+ container_of(led_cdev, struct palmas_led, cdev)
+
+static char *palmas_led_names[] = {
+ "palmas:led1",
+ "palmas:led2",
+ "palmas:led3",
+ "palmas:led4",
+};
+
+struct palmas_led_info {
+ int mask;
+ int shift;
+};
+
+struct palmas_led_info led_period_info[] = {
+ {
+ PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK,
+ PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT,
+ },
+ {
+ PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK,
+ PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT,
+ },
+ {
+ PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK,
+ PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT,
+ },
+ {
+ PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK,
+ PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT,
+ },
+};
+
+struct palmas_led_info led_time_info[] = {
+ {
+ PALMAS_LED_CTRL_LED_1_ON_TIME_MASK,
+ PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT,
+ },
+ {
+ PALMAS_LED_CTRL_LED_2_ON_TIME_MASK,
+ PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT,
+ },
+ {
+ PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK,
+ PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT,
+ },
+ {
+ PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK,
+ PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT,
+ },
+};
+
+enum palmas_led_period_shift {
+ PALMAS_LED1_PERIOD_SHIFT = PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT,
+ PALMAS_LED2_PERIOD_SHIFT = PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT,
+ PALMAS_LED3_PERIOD_SHIFT = PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT,
+ PALMAS_LED4_PERIOD_SHIFT =
+ PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT,
+};
+
+enum palmas_led_period_mask {
+ PALMAS_LED1_PERIOD_MASK = PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK,
+ PALMAS_LED2_PERIOD_MASK = PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK,
+ PALMAS_LED3_PERIOD_MASK = PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK,
+ PALMAS_LED4_PERIOD_MASK = PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK,
+};
+
+enum palmas_led_time_shift {
+ PALMAS_LED1_TIME_SHIFT = PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT,
+ PALMAS_LED2_TIME_SHIFT = PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT,
+ PALMAS_LED3_TIME_SHIFT = PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT,
+ PALMAS_LED4_TIME_SHIFT = PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT,
+};
+
+enum palmas_led_time_mask {
+ PALMAS_LED1_TIME_MASK = PALMAS_LED_CTRL_LED_1_ON_TIME_MASK,
+ PALMAS_LED2_TIME_MASK = PALMAS_LED_CTRL_LED_2_ON_TIME_MASK,
+ PALMAS_LED3_TIME_MASK = PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK,
+ PALMAS_LED4_TIME_MASK = PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK,
+};
+
+static int palmas_led_read(struct palmas_leds_data *leds, unsigned int reg,
+ unsigned int *dest)
+{
+ return palmas_read(leds->palmas, PALMAS_LED_BASE, reg, dest);
+}
+
+static int palmas_led_write(struct palmas_leds_data *leds, unsigned int reg,
+ unsigned int value)
+{
+ return palmas_write(leds->palmas, PALMAS_LED_BASE, reg, value);
+}
+
+static int palmas_led_update_bits(struct palmas_leds_data *leds,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ return palmas_update_bits(leds->palmas, PALMAS_LED_BASE,
+ reg, mask, value);
+}
+
+static void palmas_get_crtl_and_period(struct palmas_led *led,
+ unsigned int *ctrl, unsigned int *period)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+
+ *ctrl &= ~led_time_info[led->led_no].mask;
+ *period &= ~led_period_info[led->led_no].mask;
+
+ if (led->blink) {
+ *ctrl |= led->on_time << led_time_info[led->led_no].shift;
+ *period |= led->period << led_period_info[led->led_no].shift;
+ } else {
+ /*
+ * for off value is zero which we already set by
+ * masking earlier.
+ */
+ if (led->brightness) {
+ *ctrl |= LED_ON_500MS <<
+ led_time_info[led->led_no].shift;
+ *period |= LED_PERIOD_500MS <<
+ led_period_info[led->led_no].shift;
+ }
+ }
+
+ spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static void palmas_leds_work(struct work_struct *work)
+{
+ struct palmas_led *led = container_of(work, struct palmas_led, work);
+ unsigned int period, ctrl;
+
+ mutex_lock(&led->mutex);
+
+ palmas_led_read(led->leds_data, PALMAS_LED_CTRL, &ctrl);
+ palmas_led_read(led->leds_data, PALMAS_LED_PERIOD_CTRL, &period);
+
+ palmas_get_crtl_and_period(led, &ctrl, &period);
+
+ if (led->led_no < 3) {
+ palmas_led_write(led->leds_data, PALMAS_LED_CTRL, ctrl);
+ palmas_led_write(led->leds_data, PALMAS_LED_PERIOD_CTRL,
+ period);
+ } else {
+ palmas_led_write(led->leds_data, PALMAS_LED_CTRL2, ctrl);
+ palmas_led_write(led->leds_data, PALMAS_LED_PERIOD2_CTRL,
+ period);
+ }
+
+ if (is_palmas_charger(led->leds_data->palmas->product_id)) {
+ if (led->brightness || led->blink)
+ palmas_led_update_bits(led->leds_data, PALMAS_LED_EN,
+ 1 << (led->led_no - 1), 0xFF);
+ else
+ palmas_led_update_bits(led->leds_data, PALMAS_LED_EN,
+ 1 << (led->led_no - 1), 0x00);
+ }
+ mutex_unlock(&led->mutex);
+}
+
+static void palmas_leds_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct palmas_led *led = to_palmas_led(led_cdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ led->brightness = value;
+ led->blink = 0;
+ schedule_work(&led->work);
+ spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static int palmas_leds_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct palmas_led *led = to_palmas_led(led_cdev);
+ unsigned long flags;
+ int ret = 0;
+ int period;
+
+ /* Pick some defaults if we've not been given times */
+ if (*delay_on == 0 && *delay_off == 0) {
+ *delay_on = 250;
+ *delay_off = 250;
+ }
+
+ spin_lock_irqsave(&led->value_lock, flags);
+
+ /*
+ * We only have a limited selection of settings, see if we can
+ * support the configuration we're being given
+ */
+ switch (*delay_on) {
+ case 500:
+ led->on_time = LED_ON_500MS;
+ break;
+ case 250:
+ led->on_time = LED_ON_250MS;
+ break;
+ case 125:
+ led->on_time = LED_ON_125MS;
+ break;
+ case 62:
+ case 63:
+ /* Actually 62.5ms */
+ led->on_time = LED_ON_62_5MS;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ period = *delay_on + *delay_off;
+
+ if (ret == 0) {
+ switch (period) {
+ case 124:
+ case 125:
+ case 126:
+ /* All possible variations of 62.5 + 62.5 */
+ led->period = LED_PERIOD_125MS;
+ break;
+ case 250:
+ led->period = LED_PERIOD_250MS;
+ break;
+ case 500:
+ led->period = LED_PERIOD_500MS;
+ break;
+ case 1000:
+ led->period = LED_PERIOD_1000MS;
+ break;
+ case 2000:
+ led->period = LED_PERIOD_2000MS;
+ break;
+ case 4000:
+ led->period = LED_PERIOD_4000MS;
+ break;
+ case 8000:
+ led->period = LED_PERIOD_8000MS;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ if (ret == 0)
+ led->blink = 1;
+ else
+ led->blink = 0;
+
+ /*
+ * Always update; if we fail turn off blinking since we expect
+ * a software fallback.
+ */
+ schedule_work(&led->work);
+
+ spin_unlock_irqrestore(&led->value_lock, flags);
+
+ return ret;
+}
+
+static void palmas_init_led(struct palmas_leds_data *leds_data, int offset,
+ int led_no)
+{
+ mutex_init(&leds_data->palmas_led[offset].mutex);
+ INIT_WORK(&leds_data->palmas_led[offset].work, palmas_leds_work);
+ spin_lock_init(&leds_data->palmas_led[offset].value_lock);
+
+ leds_data->palmas_led[offset].leds_data = leds_data;
+ leds_data->palmas_led[offset].led_no = led_no;
+ leds_data->palmas_led[offset].cdev.name = palmas_led_names[led_no - 1];
+ leds_data->palmas_led[offset].cdev.default_trigger = NULL;
+ leds_data->palmas_led[offset].cdev.brightness_set = palmas_leds_set;
+ leds_data->palmas_led[offset].cdev.blink_set = palmas_leds_blink_set;
+}
+
+
+static int palmas_dt_to_pdata(struct device *dev,
+ struct device_node *node,
+ struct palmas_leds_platform_data *pdata)
+{
+ int ret;
+ u32 prop;
+
+ ret = of_property_read_u32(node, "ti,led1-current", &prop);
+ if (!ret) {
+ if (is_palmas_led_current_ok(prop))
+ pdata->led1_current = prop;
+ else
+ goto err;
+ }
+
+ ret = of_property_read_u32(node, "ti,led2-current", &prop);
+ if (!ret) {
+ if (is_palmas_led_current_ok(prop))
+ pdata->led2_current = prop;
+ else
+ goto err;
+ }
+
+ ret = of_property_read_u32(node, "ti,led3-current", &prop);
+ if (!ret) {
+ if (is_palmas_led_current_ok(prop))
+ pdata->led3_current = prop;
+ else
+ goto err;
+ }
+
+ ret = of_property_read_u32(node, "ti,led4-current", &prop);
+ if (!ret) {
+ if (is_palmas_led_current_ok(prop))
+ pdata->led4_current = prop;
+ else
+ goto err;
+ }
+
+ ret = of_property_read_u32(node, "ti,chrg-led-mode", &prop);
+ if (!ret)
+ pdata->chrg_led_mode = prop;
+
+ ret = of_property_read_u32(node, "ti,chrg-led-vbat-low", &prop);
+ if (!ret)
+ pdata->chrg_led_vbat_low = prop;
+
+ return 0;
+
+err:
+ dev_err(dev, "there are no LED curent - out of bounds\n");
+ return -EINVAL;
+}
+
+static int palmas_leds_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct palmas_leds_data *leds_data;
+ struct device_node *node = pdev->dev.of_node;
+ int ret, i;
+ int offset = 0;
+
+ if (!palmas->led_muxed && !is_palmas_charger(palmas->product_id)) {
+ dev_err(&pdev->dev, "there are no LEDs muxed\n");
+ return -EINVAL;
+ }
+
+ /* Palmas charger requires platform data */
+ if (is_palmas_charger(palmas->product_id) && node && !pdata) {
+
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = palmas_dt_to_pdata(&pdev->dev, node, pdata);
+ if (ret)
+ return ret;
+ }
+
+ if (is_palmas_charger(palmas->product_id) && !pdata)
+ return -EINVAL;
+
+ leds_data = devm_kzalloc(&pdev->dev, sizeof(*leds_data), GFP_KERNEL);
+ if (!leds_data)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, leds_data);
+
+ leds_data->palmas = palmas;
+
+ switch (palmas->led_muxed) {
+ case PALMAS_LED1_MUXED | PALMAS_LED2_MUXED:
+ leds_data->no_leds = 2;
+ break;
+ case PALMAS_LED1_MUXED:
+ case PALMAS_LED2_MUXED:
+ leds_data->no_leds = 1;
+ break;
+ default:
+ leds_data->no_leds = 0;
+ break;
+ }
+
+ if (is_palmas_charger(palmas->product_id)) {
+ if (pdata->chrg_led_mode)
+ leds_data->no_leds += 2;
+ else
+ leds_data->no_leds++;
+ }
+
+ if (leds_data->no_leds == 0)
+ leds_data->palmas_led = NULL;
+ else
+ leds_data = devm_kzalloc(&pdev->dev,
+ leds_data->no_leds * sizeof(*leds_data),
+ GFP_KERNEL);
+
+ /* Initialise LED1 */
+ if (palmas->led_muxed & PALMAS_LED1_MUXED) {
+ palmas_init_led(leds_data, offset, 1);
+ offset++;
+ }
+
+ /* Initialise LED2 */
+ if (palmas->led_muxed & PALMAS_LED2_MUXED) {
+ palmas_init_led(leds_data, offset, 2);
+ offset++;
+ }
+
+ if (is_palmas_charger(palmas->product_id)) {
+ palmas_init_led(leds_data, offset, 3);
+ offset++;
+
+ if (pdata->chrg_led_mode) {
+ palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
+ PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE,
+ PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE);
+
+ palmas_init_led(leds_data, offset, 4);
+ }
+ }
+
+ for (i = 0; i < leds_data->no_leds; i++) {
+ ret = led_classdev_register(leds_data->dev,
+ &leds_data->palmas_led[i].cdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Failed to register LED no: %d err: %d\n",
+ i, ret);
+ goto err_led;
+ }
+ }
+
+ if (!is_palmas_charger(palmas->product_id))
+ return 0;
+
+ /* Set the LED current registers if set in platform data */
+ if (pdata->led1_current)
+ palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL1,
+ PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_MASK,
+ pdata->led1_current);
+
+ if (pdata->led2_current)
+ palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL1,
+ PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_MASK,
+ pdata->led2_current <<
+ PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_SHIFT);
+
+ if (pdata->led3_current)
+ palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL2,
+ PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK,
+ pdata->led3_current);
+
+ if (pdata->led3_current)
+ palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL2,
+ PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK,
+ pdata->led3_current);
+
+ if (pdata->led4_current)
+ palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
+ PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_MASK,
+ pdata->led4_current <<
+ PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_SHIFT);
+
+ if (pdata->chrg_led_vbat_low)
+ palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
+ PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS,
+ PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS);
+
+ return 0;
+
+err_led:
+ for (i = 0; i < leds_data->no_leds; i++)
+ led_classdev_unregister(&leds_data->palmas_led[i].cdev);
+ return ret;
+}
+
+static int palmas_leds_remove(struct platform_device *pdev)
+{
+ struct palmas_leds_data *leds_data = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < leds_data->no_leds; i++)
+ led_classdev_unregister(&leds_data->palmas_led[i].cdev);
+ if (i)
+ kfree(leds_data->palmas_led);
+ kfree(leds_data);
+
+ return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+ { .compatible = "ti,palmas-leds", },
+ { .compatible = "ti,twl6035-leds", },
+ { .compatible = "ti,twl6036-leds", },
+ { .compatible = "ti,twl6037-leds", },
+ { .compatible = "ti,tps65913-leds", },
+ { .compatible = "ti,tps65914-leds", },
+ { .compatible = "ti,tps80036-leds", },
+ { /* end */ }
+};
+
+static struct platform_driver palmas_leds_driver = {
+ .driver = {
+ .name = "palmas-leds",
+ .of_match_table = of_palmas_match_tbl,
+ .owner = THIS_MODULE,
+ },
+ .probe = palmas_leds_probe,
+ .remove = palmas_leds_remove,
+};
+
+module_platform_driver(palmas_leds_driver);
+
+MODULE_AUTHOR("Graeme Gregory <[email protected]>");
+MODULE_DESCRIPTION("Palmas LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-leds");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
index d8c303b..73186c8 100644
--- a/include/linux/mfd/palmas.h
+++ b/include/linux/mfd/palmas.h
@@ -232,6 +232,36 @@ struct palmas_clk_platform_data {
int clk32kgaudio_mode_sleep;
};
+enum palmas_led_current {
+ PALMAS_ILED_0mA = 0, /* 0 mA */
+ PALMAS_ILED_0m25A, /* 0.25 mA */
+ PALMAS_ILED_0m5A, /* 0.5 mA */
+ PALMAS_ILED_1m0A, /* 1.0 mA */
+ PALMAS_ILED_2m5A, /* 2.5 mA */
+ PALMAS_ILED_5m0A, /* 5.0 mA */
+ PALMAS_ILED_10m0A, /* 10.0 mA */
+ PALMAS_ILED_0m0A, /* 0 mA */
+};
+
+#define is_palmas_led_current_ok(a) (((a) == PALMAS_ILED_0mA) || \
+ ((a) == PALMAS_ILED_0m25A) || \
+ ((a) == PALMAS_ILED_0m5A) || \
+ ((a) == PALMAS_ILED_1m0A) || \
+ ((a) == PALMAS_ILED_2m5A) || \
+ ((a) == PALMAS_ILED_5m0A) || \
+ ((a) == PALMAS_ILED_10m0A) || \
+ ((a) == PALMAS_ILED_0m0A))
+
+struct palmas_leds_platform_data {
+ int led1_current;
+ int led2_current;
+ int led3_current;
+ int led4_current;
+
+ int chrg_led_mode;
+ int chrg_led_vbat_low;
+};
+
struct palmas_platform_data {
int irq_flags;
int gpio_base;
@@ -1856,6 +1886,12 @@ enum usb_irq_events {
#define PALMAS_LED_CTRL 0x1
#define PALMAS_PWM_CTRL1 0x2
#define PALMAS_PWM_CTRL2 0x3
+#define PALMAS_LED_PERIOD2_CTRL 0x4
+#define PALMAS_LED_CTRL2 0x5
+#define PALMAS_LED_CURRENT_CTRL1 0x6
+#define PALMAS_LED_CURRENT_CTRL2 0x7
+#define PALMAS_CHRG_LED_CTRL 0x8
+#define PALMAS_LED_EN 0x9
/* Bit definitions for LED_PERIOD_CTRL */
#define PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK 0x38
@@ -1883,6 +1919,50 @@ enum usb_irq_events {
#define PALMAS_PWM_CTRL2_PWM_DUTY_SEL_MASK 0xff
#define PALMAS_PWM_CTRL2_PWM_DUTY_SEL_SHIFT 0
+/* Bit definitions for LED_PERIOD2_CTRL */
+#define PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK 0x38
+#define PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT 3
+#define PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK 0x07
+#define PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT 0
+
+/* Bit definitions for LED_CTRL2 */
+#define PALMAS_LED_CTRL2_CHRG_LED_SEQ 0x20
+#define PALMAS_LED_CTRL2_CHRG_LED_SEQ_SHIFT 5
+#define PALMAS_LED_CTRL2_LED_3_SEQ 0x10
+#define PALMAS_LED_CTRL2_LED_3_SEQ_SHIFT 4
+#define PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK 0x0c
+#define PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT 2
+#define PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK 0x03
+#define PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT 0
+
+/* Bit definitions for LED_CURRENT_CTRL1 */
+#define PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_MASK 0x38
+#define PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_SHIFT 3
+#define PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_MASK 0x07
+#define PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_SHIFT 0
+
+/* Bit definitions for LED_CURRENT_CTRL2 */
+#define PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK 0x07
+#define PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_SHIFT 0
+
+/* Bit definitions for CHRG_LED_CTRL */
+#define PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_MASK 0x38
+#define PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_SHIFT 3
+#define PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS 0x02
+#define PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS_SHIFT 1
+#define PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE 0x01
+#define PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE_SHIFT 0
+
+/* Bit definitions for LED_EN */
+#define PALMAS_LED_EN_CHRG_LED_EN 0x08
+#define PALMAS_LED_EN_CHRG_LED_EN_SHIFT 3
+#define PALMAS_LED_EN_LED_3_EN 0x04
+#define PALMAS_LED_EN_LED_3_EN_SHIFT 2
+#define PALMAS_LED_EN_LED_2_EN 0x02
+#define PALMAS_LED_EN_LED_2_EN_SHIFT 1
+#define PALMAS_LED_EN_LED_1_EN 0x01
+#define PALMAS_LED_EN_LED_1_EN_SHIFT 0
+
/* Registers for function INTERRUPT */
#define PALMAS_INT1_STATUS 0x0
#define PALMAS_INT1_MASK 0x1
--
1.7.0.4
Add the Kconfig and Makefile for the Palmas watchdog driver
Signed-off-by: Ian Lartey <[email protected]>
Signed-off-by: Graeme Gregory <[email protected]>
---
drivers/watchdog/Kconfig | 8 ++++++++
drivers/watchdog/Makefile | 1 +
2 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9fcc70c..d48cb0e 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -103,6 +103,14 @@ config WM8350_WATCHDOG
Support for the watchdog in the WM8350 AudioPlus PMIC. When
the watchdog triggers the system will be reset.
+config PALMAS_WATCHDOG
+ tristate "Palmas Watchdog"
+ depends on MFD_PALMAS
+ select WATCHDOG_CORE
+ help
+ Support for the watchdog in the Palmas family of PMICs. When the
+ watchdog times out system will be reset.
+
# ALPHA Architecture
# ARM Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index a300b94..e845c21 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -171,3 +171,4 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_PALMAS_WATCHDOG) += palmas_wdt.o
--
1.7.0.4
From: Graeme Gregory <[email protected]>
Add support for the Palmas watchdog timer which has a timeout configurable
from 1s to 128s.
Signed-off-by: Graeme Gregory <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
---
drivers/watchdog/palmas_wdt.c | 169 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 169 insertions(+), 0 deletions(-)
create mode 100644 drivers/watchdog/palmas_wdt.c
diff --git a/drivers/watchdog/palmas_wdt.c b/drivers/watchdog/palmas_wdt.c
new file mode 100644
index 0000000..da7e379
--- /dev/null
+++ b/drivers/watchdog/palmas_wdt.c
@@ -0,0 +1,169 @@
+/*
+ * Driver for Watchdog part of Palmas PMIC Chips
+ *
+ * Copyright 2011-2013 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <[email protected]>
+ * Author: Ian Lartey <[email protected]>
+ *
+ * Based on twl4030_wdt.c
+ *
+ * Author: Timo Kokkonen <timo.t.kokkonen at nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/mfd/palmas.h>
+
+struct palmas_wdt {
+ struct palmas *palmas;
+ struct watchdog_device wdt;
+ struct device *dev;
+
+ int timer_margin;
+};
+
+
+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 int palmas_wdt_write(struct palmas *palmas, unsigned int data)
+{
+ unsigned int addr;
+
+ addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_WATCHDOG);
+
+ return palmas_write(palmas, PALMAS_PMU_CONTROL_BASE, addr, addr);
+}
+
+static int palmas_wdt_enable(struct watchdog_device *wdt)
+{
+ struct palmas_wdt *driver_data = watchdog_get_drvdata(wdt);
+ struct palmas *palmas = driver_data->palmas;
+
+ return palmas_wdt_write(palmas, driver_data->timer_margin |
+ PALMAS_WATCHDOG_ENABLE);
+}
+
+static int palmas_wdt_disable(struct watchdog_device *wdt)
+{
+ struct palmas_wdt *driver_data = watchdog_get_drvdata(wdt);
+ struct palmas *palmas = driver_data->palmas;
+
+ return palmas_wdt_write(palmas, driver_data->timer_margin);
+}
+
+static int palmas_wdt_set_timeout(struct watchdog_device *wdt, unsigned timeout)
+{
+ struct palmas_wdt *driver_data = watchdog_get_drvdata(wdt);
+
+ if (timeout < 1 || timeout > 128) {
+ dev_warn(driver_data->dev,
+ "Timeout can only be in the range [1-128] seconds");
+ return -EINVAL;
+ }
+ driver_data->timer_margin = fls(timeout) - 1;
+ return 0;
+}
+
+static const struct watchdog_info palmas_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Palmas Watchdog",
+ .firmware_version = 0,
+};
+
+static const struct watchdog_ops palmas_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = palmas_wdt_enable,
+ .stop = palmas_wdt_disable,
+ .ping = palmas_wdt_enable,
+ .set_timeout = palmas_wdt_set_timeout,
+};
+
+static int palmas_wdt_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_wdt *driver_data;
+ struct watchdog_device *palmas_wdt;
+ int ret = 0;
+
+ driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
+ GFP_KERNEL);
+ if (!driver_data) {
+ dev_err(&pdev->dev, "Unable to alloacate watchdog device\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ driver_data->palmas = palmas;
+
+ palmas_wdt = &driver_data->wdt;
+
+ palmas_wdt->info = &palmas_wdt_info;
+ palmas_wdt->ops = &palmas_wdt_ops;
+ watchdog_set_nowayout(palmas_wdt, nowayout);
+ watchdog_set_drvdata(palmas_wdt, driver_data);
+
+ ret = watchdog_register_device(&driver_data->wdt);
+ if (ret) {
+ platform_set_drvdata(pdev, NULL);
+ goto err;
+ }
+
+ dev_set_drvdata(&pdev->dev, driver_data);
+
+ return 0;
+err:
+ return ret;
+}
+
+static int palmas_wdt_remove(struct platform_device *pdev)
+{
+ struct palmas_wdt *driver_data = dev_get_drvdata(&pdev->dev);
+
+ watchdog_unregister_device(&driver_data->wdt);
+
+ return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+ { .compatible = "ti,palmas-wdt", },
+ { .compatible = "ti,twl6035-wdt", },
+ { .compatible = "ti,twl6036-wdt", },
+ { .compatible = "ti,twl6037-wdt", },
+ { .compatible = "ti,tps65913-wdt", },
+ { .compatible = "ti,tps65914-wdt", },
+ { .compatible = "ti,tps80036-wdt", },
+ { /* end */ }
+};
+
+static struct platform_driver palmas_wdt_driver = {
+ .probe = palmas_wdt_probe,
+ .remove = palmas_wdt_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .of_match_table = of_palmas_match_tbl,
+ .name = "palmas-wdt",
+ },
+};
+
+module_platform_driver(palmas_wdt_driver);
+
+MODULE_AUTHOR("Graeme Gregory <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-wdt");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
--
1.7.0.4
From: Graeme Gregory <[email protected]>
Read the chip varient and the OTP information from the chip and display
this on probe to aid in debugging of issues.
Older palmas chips do not have the USB_ID programmed and will therefore
return 0x0000 for this field.
palmas register read/write/update API is now used
palmas_read_product_id_and_revs used by palmas_i2c_probe to get
the device id, design and software revisions
id field of pamas struct renamed to device_id
Updated the DT parsing to agree with the bindings document, just some minor
value name changes.
Signed-off-by: Graeme Gregory <[email protected]>
Signed-off-by: Ian Lartey <[email protected]>
---
drivers/mfd/palmas.c | 102 ++++++++++++++++++++++++++++++++++----------
include/linux/mfd/palmas.h | 35 +++++++++++++++-
2 files changed, 113 insertions(+), 24 deletions(-)
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index 73bf76d..913416f 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -1,9 +1,10 @@
/*
* TI Palmas MFD Driver
*
- * Copyright 2011-2012 Texas Instruments Inc.
+ * Copyright 2011-2013 Texas Instruments Inc.
*
* Author: Graeme Gregory <[email protected]>
+ * Author: Ian Lartey <[email protected]>
*
* 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
@@ -257,6 +258,56 @@ static struct regmap_irq_chip palmas_irq_chip = {
PALMAS_INT1_MASK),
};
+static int palmas_read_product_id_and_revs(struct palmas *palmas)
+{
+ int ret;
+ unsigned int reg;
+
+ /* Read variant info from the device */
+ ret = palmas_read(palmas, PALMAS_ID_BASE, PALMAS_PRODUCT_ID_LSB, ®);
+ if (ret < 0) {
+ dev_err(palmas->dev, "Unable to read ID err: %d\n", ret);
+ return ret;
+ }
+
+ palmas->product_id = reg;
+
+ ret = palmas_read(palmas, PALMAS_ID_BASE, PALMAS_PRODUCT_ID_MSB, ®);
+ if (ret < 0) {
+ dev_err(palmas->dev, "Unable to read ID err: %d\n", ret);
+ return ret;
+ }
+
+ palmas->product_id |= reg << 8;
+
+ dev_info(palmas->dev, "Product ID %x\n", palmas->product_id);
+
+ ret = palmas_read(palmas, PALMAS_DESIGNREV_BASE,
+ PALMAS_DESIGNREV, ®);
+ if (ret < 0) {
+ dev_err(palmas->dev, "Unable to read DESIGNREV err: %d\n", ret);
+ return ret;
+ }
+
+ palmas->designrev = reg & PALMAS_DESIGNREV_DESIGNREV_MASK;
+
+ dev_info(palmas->dev, "Product Design Rev %x\n", palmas->designrev);
+
+ ret = palmas_read(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_SW_REVISION,
+ ®);
+ if (ret < 0) {
+ dev_err(palmas->dev, "Unable to read SW_REVISION err: %d\n",
+ ret);
+ return ret;
+ }
+
+ palmas->sw_revision = reg;
+
+ dev_info(palmas->dev, "Product SW Rev %x\n", palmas->sw_revision);
+
+ return 0;
+}
+
static int palmas_set_pdata_irq_flag(struct i2c_client *i2c,
struct palmas_platform_data *pdata)
{
@@ -278,20 +329,20 @@ static void palmas_dt_to_pdata(struct i2c_client *i2c,
int ret;
u32 prop;
- ret = of_property_read_u32(node, "ti,mux_pad1", &prop);
+ ret = of_property_read_u32(node, "ti,mux-pad1", &prop);
if (!ret) {
pdata->mux_from_pdata = 1;
pdata->pad1 = prop;
}
- ret = of_property_read_u32(node, "ti,mux_pad2", &prop);
+ ret = of_property_read_u32(node, "ti,mux-pad2", &prop);
if (!ret) {
pdata->mux_from_pdata = 1;
pdata->pad2 = prop;
}
/* The default for this register is all masked */
- ret = of_property_read_u32(node, "ti,power_ctrl", &prop);
+ ret = of_property_read_u32(node, "ti,power-ctrl", &prop);
if (!ret)
pdata->power_ctrl = prop;
else
@@ -309,7 +360,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
struct palmas_platform_data *pdata;
struct device_node *node = i2c->dev.of_node;
int ret = 0, i;
- unsigned int reg, addr;
+ unsigned int reg;
int slave;
struct mfd_cell *children;
@@ -333,7 +384,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, palmas);
palmas->dev = &i2c->dev;
- palmas->id = id->driver_data;
+ palmas->product_id = id->driver_data;
palmas->irq = i2c->irq;
for (i = 0; i < PALMAS_NUM_CLIENTS; i++) {
@@ -374,12 +425,16 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
goto err;
}
+ /* Read variant info from the device */
+ ret = palmas_read_product_id_and_revs(palmas);
+ if (ret < 0)
+ goto err;
+
/* Change IRQ into clear on read mode for efficiency */
slave = PALMAS_BASE_TO_SLAVE(PALMAS_INTERRUPT_BASE);
- addr = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, PALMAS_INT_CTRL);
reg = PALMAS_INT_CTRL_INT_CLEAR;
- regmap_write(palmas->regmap[slave], addr, reg);
+ ret = palmas_write(palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT_CTRL, reg);
ret = regmap_add_irq_chip(palmas->regmap[slave], palmas->irq,
IRQF_ONESHOT | pdata->irq_flags, 0, &palmas_irq_chip,
@@ -387,17 +442,15 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err;
- slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
- addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
- PALMAS_PRIMARY_SECONDARY_PAD1);
-
if (pdata->mux_from_pdata) {
reg = pdata->pad1;
- ret = regmap_write(palmas->regmap[slave], addr, reg);
+ ret = palmas_write(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD1, reg);
if (ret)
goto err_irq;
} else {
- ret = regmap_read(palmas->regmap[slave], addr, ®);
+ ret = palmas_read(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD1, ®);
if (ret)
goto err_irq;
}
@@ -423,16 +476,15 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_3))
palmas->gpio_muxed |= PALMAS_GPIO_3_MUXED;
- addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
- PALMAS_PRIMARY_SECONDARY_PAD2);
-
if (pdata->mux_from_pdata) {
reg = pdata->pad2;
- ret = regmap_write(palmas->regmap[slave], addr, reg);
+ ret = palmas_write(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD2, reg);
if (ret)
goto err_irq;
} else {
- ret = regmap_read(palmas->regmap[slave], addr, ®);
+ ret = palmas_read(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD2, ®);
if (ret)
goto err_irq;
}
@@ -452,10 +504,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
reg = pdata->power_ctrl;
- slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
- addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_POWER_CTRL);
-
- ret = regmap_write(palmas->regmap[slave], addr, reg);
+ ret = palmas_write(palmas, PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD2, reg);
if (ret)
goto err_irq;
@@ -534,6 +584,12 @@ MODULE_DEVICE_TABLE(i2c, palmas_i2c_id);
static struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas", },
+ { .compatible = "ti,twl6035", },
+ { .compatible = "ti,twl6036", },
+ { .compatible = "ti,twl6037", },
+ { .compatible = "ti,tps65913", },
+ { .compatible = "ti,tps65914", },
+ { .compatible = "ti,tps80036", },
{ /* end */ }
};
diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
index 4a066d0..4cffe31 100644
--- a/include/linux/mfd/palmas.h
+++ b/include/linux/mfd/palmas.h
@@ -44,7 +44,9 @@ struct palmas {
struct regmap *regmap[PALMAS_NUM_CLIENTS];
/* Stored chip id */
- int id;
+ int product_id;
+ int designrev;
+ int sw_revision;
/* IRQ Data */
int irq;
@@ -438,11 +440,13 @@ enum usb_irq_events {
#define PALMAS_PU_PD_OD_BASE 0x1F4
#define PALMAS_LED_BASE 0x200
#define PALMAS_INTERRUPT_BASE 0x210
+#define PALMAS_ID_BASE 0x24F
#define PALMAS_USB_OTG_BASE 0x250
#define PALMAS_VIBRATOR_BASE 0x270
#define PALMAS_GPIO_BASE 0x280
#define PALMAS_USB_BASE 0x290
#define PALMAS_GPADC_BASE 0x2C0
+#define PALMAS_DESIGNREV_BASE 0x357
#define PALMAS_TRIM_GPADC_BASE 0x3CD
/* Registers for function RTC */
@@ -2160,6 +2164,28 @@ enum usb_irq_events {
#define PALMAS_INT_CTRL_INT_CLEAR 0x01
#define PALMAS_INT_CTRL_INT_CLEAR_SHIFT 0
+/* Registers for function ID */
+#define PALMAS_VENDOR_ID_LSB 0x0
+#define PALMAS_VENDOR_ID_MSB 0x1
+#define PALMAS_PRODUCT_ID_LSB 0x2
+#define PALMAS_PRODUCT_ID_MSB 0x3
+
+/* Bit definitions for VENDOR_ID_LSB */
+#define PALMAS_VENDOR_ID_LSB_VENDOR_ID_MASK 0xff
+#define PALMAS_VENDOR_ID_LSB_VENDOR_ID_SHIFT 0
+
+/* Bit definitions for VENDOR_ID_MSB */
+#define PALMAS_VENDOR_ID_MSB_VENDOR_ID_MASK 0xff
+#define PALMAS_VENDOR_ID_MSB_VENDOR_ID_SHIFT 0
+
+/* Bit definitions for PRODUCT_ID_LSB */
+#define PALMAS_PRODUCT_ID_LSB_PRODUCT_ID_MASK 0xff
+#define PALMAS_PRODUCT_ID_LSB_PRODUCT_ID_SHIFT 0
+
+/* Bit definitions for PRODUCT_ID_MSB */
+#define PALMAS_PRODUCT_ID_MSB_PRODUCT_ID_MASK 0xff
+#define PALMAS_PRODUCT_ID_MSB_PRODUCT_ID_SHIFT 0
+
/* Registers for function USB_OTG */
#define PALMAS_USB_WAKEUP 0x3
#define PALMAS_USB_VBUS_CTRL_SET 0x4
@@ -2782,6 +2808,13 @@ enum usb_irq_events {
#define PALMAS_GPADC_SMPS_VSEL_MONITORING_SMPS_VSEL_MONITORING_MASK 0x7f
#define PALMAS_GPADC_SMPS_VSEL_MONITORING_SMPS_VSEL_MONITORING_SHIFT 0
+/* Registers for function DESIGNREV */
+#define PALMAS_DESIGNREV 0x0
+
+/* Bit definitions for DESIGNREV */
+#define PALMAS_DESIGNREV_DESIGNREV_MASK 0x0f
+#define PALMAS_DESIGNREV_DESIGNREV_SHIFT 0
+
/* Registers for function GPADC */
#define PALMAS_GPADC_TRIM1 0x0
#define PALMAS_GPADC_TRIM2 0x1
--
1.7.0.4
On 22/03/13 13:36, Ian Lartey wrote:
> The Palmas familly of chips has LED support. This is not always muxed
> to output pins so depending on the setting of the mux this driver
> will create the appropriate LED class devices.
>
> Signed-off-by: Graeme Gregory <[email protected]>
> Signed-off-by: Ian Lartey <[email protected]>
> ---
> drivers/leds/Kconfig | 9 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-palmas.c | 590 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/palmas.h | 80 ++++++
> 4 files changed, 680 insertions(+), 0 deletions(-)
> create mode 100644 drivers/leds/leds-palmas.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index d44806d..249027e 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -479,6 +479,15 @@ config LEDS_BLINKM
> This option enables support for the BlinkM RGB LED connected
> through I2C. Say Y to enable support for the BlinkM LED.
>
> +config LEDS_PALMAS
> + bool "LED support for the Palmas family of PMICs"
> + depends on LEDS_CLASS
> + depends on MFD_PALMAS
> + help
> + This option enables the driver for LED1 & LED2 pins on Palmas PMIC
> + if these pins are enabled in the mux configuration. The driver support
> + ON/OFF and blinking with hardware control.
> +
> comment "LED Triggers"
> source "drivers/leds/trigger/Kconfig"
>
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index ac28977..3acb116 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
> obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
> obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
> obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
> +obj-$(CONFIG_LEDS_PALMAS) += leds-palmas.o
>
> # LED SPI Drivers
> obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
> diff --git a/drivers/leds/leds-palmas.c b/drivers/leds/leds-palmas.c
> new file mode 100644
> index 0000000..6bfdab2
> --- /dev/null
> +++ b/drivers/leds/leds-palmas.c
> @@ -0,0 +1,590 @@
> +/*
> + * Driver for LED part of Palmas PMIC Chips
> + *
> + * Copyright 2011-2013 Texas Instruments Inc.
> + *
> + * Author: Graeme Gregory <[email protected]>
> + * Author: Ian Lartey <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/mfd/palmas.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +struct palmas_leds_data;
> +
> +enum palmas_led_on_time {
> + LED_ON_62_5MS = 0x00,
> + LED_ON_125MS = 0x01,
> + LED_ON_250MS = 0x02,
> + LED_ON_500MS = 0x03,
> +};
> +
> +enum palmas_led_period {
> + LED_PERIOD_OFF = 0x00,
> + LED_PERIOD_125MS = 0x01,
> + LED_PERIOD_250MS = 0x02,
> + LED_PERIOD_500MS = 0x03,
> + LED_PERIOD_1000MS = 0x04,
> + LED_PERIOD_2000MS = 0x05,
> + LED_PERIOD_4000MS = 0x06,
> + LED_PERIOD_8000MS = 0x07,
> +};
> +
> +struct palmas_led {
> + struct led_classdev cdev;
> + struct palmas_leds_data *leds_data;
> + int led_no;
> + struct work_struct work;
> + struct mutex mutex;
> +
> + spinlock_t value_lock;
> +
> + int blink;
> + enum palmas_led_on_time on_time;
> + enum palmas_led_period period;
> + enum led_brightness brightness;
> +};
> +
> +struct palmas_leds_data {
> + struct device *dev;
> + struct led_classdev cdev;
> + struct palmas *palmas;
> +
> + struct palmas_led *palmas_led;
> + int no_leds;
> +};
> +
> +#define to_palmas_led(led_cdev) \
> + container_of(led_cdev, struct palmas_led, cdev)
> +
> +static char *palmas_led_names[] = {
> + "palmas:led1",
> + "palmas:led2",
> + "palmas:led3",
> + "palmas:led4",
> +};
> +
> +struct palmas_led_info {
> + int mask;
> + int shift;
> +};
> +
> +struct palmas_led_info led_period_info[] = {
> + {
> + PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK,
> + PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT,
> + },
> + {
> + PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK,
> + PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT,
> + },
> + {
> + PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK,
> + PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT,
> + },
> + {
> + PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK,
> + PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT,
> + },
> +};
> +
> +struct palmas_led_info led_time_info[] = {
> + {
> + PALMAS_LED_CTRL_LED_1_ON_TIME_MASK,
> + PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT,
> + },
> + {
> + PALMAS_LED_CTRL_LED_2_ON_TIME_MASK,
> + PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT,
> + },
> + {
> + PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK,
> + PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT,
> + },
> + {
> + PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK,
> + PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT,
> + },
> +};
> +
> +enum palmas_led_period_shift {
> + PALMAS_LED1_PERIOD_SHIFT = PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT,
> + PALMAS_LED2_PERIOD_SHIFT = PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT,
> + PALMAS_LED3_PERIOD_SHIFT = PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT,
> + PALMAS_LED4_PERIOD_SHIFT =
> + PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT,
> +};
> +
> +enum palmas_led_period_mask {
> + PALMAS_LED1_PERIOD_MASK = PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK,
> + PALMAS_LED2_PERIOD_MASK = PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK,
> + PALMAS_LED3_PERIOD_MASK = PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK,
> + PALMAS_LED4_PERIOD_MASK = PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK,
> +};
> +
> +enum palmas_led_time_shift {
> + PALMAS_LED1_TIME_SHIFT = PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT,
> + PALMAS_LED2_TIME_SHIFT = PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT,
> + PALMAS_LED3_TIME_SHIFT = PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT,
> + PALMAS_LED4_TIME_SHIFT = PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT,
> +};
> +
> +enum palmas_led_time_mask {
> + PALMAS_LED1_TIME_MASK = PALMAS_LED_CTRL_LED_1_ON_TIME_MASK,
> + PALMAS_LED2_TIME_MASK = PALMAS_LED_CTRL_LED_2_ON_TIME_MASK,
> + PALMAS_LED3_TIME_MASK = PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK,
> + PALMAS_LED4_TIME_MASK = PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK,
> +};
> +
It looks like these enums are some dead code you forgot to remove in
this version.
> +static int palmas_led_read(struct palmas_leds_data *leds, unsigned int reg,
> + unsigned int *dest)
> +{
> + return palmas_read(leds->palmas, PALMAS_LED_BASE, reg, dest);
> +}
> +
> +static int palmas_led_write(struct palmas_leds_data *leds, unsigned int reg,
> + unsigned int value)
> +{
> + return palmas_write(leds->palmas, PALMAS_LED_BASE, reg, value);
> +}
> +
> +static int palmas_led_update_bits(struct palmas_leds_data *leds,
> + unsigned int reg, unsigned int mask, unsigned int value)
> +{
> + return palmas_update_bits(leds->palmas, PALMAS_LED_BASE,
> + reg, mask, value);
> +}
> +
> +static void palmas_get_crtl_and_period(struct palmas_led *led,
> + unsigned int *ctrl, unsigned int *period)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&led->value_lock, flags);
> +
> + *ctrl &= ~led_time_info[led->led_no].mask;
> + *period &= ~led_period_info[led->led_no].mask;
> +
> + if (led->blink) {
> + *ctrl |= led->on_time << led_time_info[led->led_no].shift;
> + *period |= led->period << led_period_info[led->led_no].shift;
> + } else {
> + /*
> + * for off value is zero which we already set by
> + * masking earlier.
> + */
> + if (led->brightness) {
> + *ctrl |= LED_ON_500MS <<
> + led_time_info[led->led_no].shift;
> + *period |= LED_PERIOD_500MS <<
> + led_period_info[led->led_no].shift;
> + }
> + }
> +
> + spin_unlock_irqrestore(&led->value_lock, flags);
> +}
> +
> +static void palmas_leds_work(struct work_struct *work)
> +{
> + struct palmas_led *led = container_of(work, struct palmas_led, work);
> + unsigned int period, ctrl;
> +
> + mutex_lock(&led->mutex);
> +
> + palmas_led_read(led->leds_data, PALMAS_LED_CTRL, &ctrl);
> + palmas_led_read(led->leds_data, PALMAS_LED_PERIOD_CTRL, &period);
> +
> + palmas_get_crtl_and_period(led, &ctrl, &period);
> +
> + if (led->led_no < 3) {
> + palmas_led_write(led->leds_data, PALMAS_LED_CTRL, ctrl);
> + palmas_led_write(led->leds_data, PALMAS_LED_PERIOD_CTRL,
> + period);
> + } else {
> + palmas_led_write(led->leds_data, PALMAS_LED_CTRL2, ctrl);
> + palmas_led_write(led->leds_data, PALMAS_LED_PERIOD2_CTRL,
> + period);
> + }
> +
> + if (is_palmas_charger(led->leds_data->palmas->product_id)) {
> + if (led->brightness || led->blink)
> + palmas_led_update_bits(led->leds_data, PALMAS_LED_EN,
> + 1 << (led->led_no - 1), 0xFF);
> + else
> + palmas_led_update_bits(led->leds_data, PALMAS_LED_EN,
> + 1 << (led->led_no - 1), 0x00);
> + }
> + mutex_unlock(&led->mutex);
> +}
> +
> +static void palmas_leds_set(struct led_classdev *led_cdev,
> + enum led_brightness value)
> +{
> + struct palmas_led *led = to_palmas_led(led_cdev);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&led->value_lock, flags);
> + led->brightness = value;
> + led->blink = 0;
> + schedule_work(&led->work);
> + spin_unlock_irqrestore(&led->value_lock, flags);
> +}
> +
> +static int palmas_leds_blink_set(struct led_classdev *led_cdev,
> + unsigned long *delay_on,
> + unsigned long *delay_off)
> +{
> + struct palmas_led *led = to_palmas_led(led_cdev);
> + unsigned long flags;
> + int ret = 0;
> + int period;
> +
> + /* Pick some defaults if we've not been given times */
> + if (*delay_on == 0 && *delay_off == 0) {
> + *delay_on = 250;
> + *delay_off = 250;
> + }
> +
> + spin_lock_irqsave(&led->value_lock, flags);
> +
> + /*
> + * We only have a limited selection of settings, see if we can
> + * support the configuration we're being given
> + */
> + switch (*delay_on) {
> + case 500:
> + led->on_time = LED_ON_500MS;
> + break;
> + case 250:
> + led->on_time = LED_ON_250MS;
> + break;
> + case 125:
> + led->on_time = LED_ON_125MS;
> + break;
> + case 62:
> + case 63:
> + /* Actually 62.5ms */
> + led->on_time = LED_ON_62_5MS;
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + period = *delay_on + *delay_off;
> +
> + if (ret == 0) {
> + switch (period) {
> + case 124:
> + case 125:
> + case 126:
> + /* All possible variations of 62.5 + 62.5 */
> + led->period = LED_PERIOD_125MS;
> + break;
> + case 250:
> + led->period = LED_PERIOD_250MS;
> + break;
> + case 500:
> + led->period = LED_PERIOD_500MS;
> + break;
> + case 1000:
> + led->period = LED_PERIOD_1000MS;
> + break;
> + case 2000:
> + led->period = LED_PERIOD_2000MS;
> + break;
> + case 4000:
> + led->period = LED_PERIOD_4000MS;
> + break;
> + case 8000:
> + led->period = LED_PERIOD_8000MS;
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> + }
> +
> + if (ret == 0)
> + led->blink = 1;
> + else
> + led->blink = 0;
> +
> + /*
> + * Always update; if we fail turn off blinking since we expect
> + * a software fallback.
> + */
> + schedule_work(&led->work);
> +
> + spin_unlock_irqrestore(&led->value_lock, flags);
> +
> + return ret;
> +}
> +
> +static void palmas_init_led(struct palmas_leds_data *leds_data, int offset,
> + int led_no)
> +{
> + mutex_init(&leds_data->palmas_led[offset].mutex);
> + INIT_WORK(&leds_data->palmas_led[offset].work, palmas_leds_work);
> + spin_lock_init(&leds_data->palmas_led[offset].value_lock);
> +
> + leds_data->palmas_led[offset].leds_data = leds_data;
> + leds_data->palmas_led[offset].led_no = led_no;
> + leds_data->palmas_led[offset].cdev.name = palmas_led_names[led_no - 1];
> + leds_data->palmas_led[offset].cdev.default_trigger = NULL;
> + leds_data->palmas_led[offset].cdev.brightness_set = palmas_leds_set;
> + leds_data->palmas_led[offset].cdev.blink_set = palmas_leds_blink_set;
> +}
> +
> +
> +static int palmas_dt_to_pdata(struct device *dev,
> + struct device_node *node,
> + struct palmas_leds_platform_data *pdata)
> +{
> + int ret;
> + u32 prop;
> +
> + ret = of_property_read_u32(node, "ti,led1-current", &prop);
> + if (!ret) {
> + if (is_palmas_led_current_ok(prop))
> + pdata->led1_current = prop;
> + else
> + goto err;
> + }
> +
> + ret = of_property_read_u32(node, "ti,led2-current", &prop);
> + if (!ret) {
> + if (is_palmas_led_current_ok(prop))
> + pdata->led2_current = prop;
> + else
> + goto err;
> + }
> +
> + ret = of_property_read_u32(node, "ti,led3-current", &prop);
> + if (!ret) {
> + if (is_palmas_led_current_ok(prop))
> + pdata->led3_current = prop;
> + else
> + goto err;
> + }
> +
> + ret = of_property_read_u32(node, "ti,led4-current", &prop);
> + if (!ret) {
> + if (is_palmas_led_current_ok(prop))
> + pdata->led4_current = prop;
> + else
> + goto err;
> + }
> +
> + ret = of_property_read_u32(node, "ti,chrg-led-mode", &prop);
> + if (!ret)
> + pdata->chrg_led_mode = prop;
> +
> + ret = of_property_read_u32(node, "ti,chrg-led-vbat-low", &prop);
> + if (!ret)
> + pdata->chrg_led_vbat_low = prop;
> +
> + return 0;
> +
> +err:
> + dev_err(dev, "there are no LED curent - out of bounds\n");
> + return -EINVAL;
> +}
> +
> +static int palmas_leds_probe(struct platform_device *pdev)
> +{
> + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
> + struct palmas_leds_platform_data *pdata = pdev->dev.platform_data;
> + struct palmas_leds_data *leds_data;
> + struct device_node *node = pdev->dev.of_node;
> + int ret, i;
> + int offset = 0;
> +
> + if (!palmas->led_muxed && !is_palmas_charger(palmas->product_id)) {
> + dev_err(&pdev->dev, "there are no LEDs muxed\n");
> + return -EINVAL;
> + }
> +
> + /* Palmas charger requires platform data */
> + if (is_palmas_charger(palmas->product_id) && node && !pdata) {
> +
> + if (!pdata)
> + return -ENOMEM;
> +
> + ret = palmas_dt_to_pdata(&pdev->dev, node, pdata);
> + if (ret)
> + return ret;
> + }
> +
> + if (is_palmas_charger(palmas->product_id) && !pdata)
> + return -EINVAL;
> +
> + leds_data = devm_kzalloc(&pdev->dev, sizeof(*leds_data), GFP_KERNEL);
> + if (!leds_data)
> + return -ENOMEM;
> + platform_set_drvdata(pdev, leds_data);
> +
> + leds_data->palmas = palmas;
> +
> + switch (palmas->led_muxed) {
> + case PALMAS_LED1_MUXED | PALMAS_LED2_MUXED:
> + leds_data->no_leds = 2;
> + break;
> + case PALMAS_LED1_MUXED:
> + case PALMAS_LED2_MUXED:
> + leds_data->no_leds = 1;
> + break;
> + default:
> + leds_data->no_leds = 0;
> + break;
> + }
> +
> + if (is_palmas_charger(palmas->product_id)) {
> + if (pdata->chrg_led_mode)
> + leds_data->no_leds += 2;
> + else
> + leds_data->no_leds++;
> + }
> +
> + if (leds_data->no_leds == 0)
> + leds_data->palmas_led = NULL;
> + else
> + leds_data = devm_kzalloc(&pdev->dev,
> + leds_data->no_leds * sizeof(*leds_data),
> + GFP_KERNEL);
> +
> + /* Initialise LED1 */
> + if (palmas->led_muxed & PALMAS_LED1_MUXED) {
> + palmas_init_led(leds_data, offset, 1);
> + offset++;
> + }
> +
> + /* Initialise LED2 */
> + if (palmas->led_muxed & PALMAS_LED2_MUXED) {
> + palmas_init_led(leds_data, offset, 2);
> + offset++;
> + }
> +
> + if (is_palmas_charger(palmas->product_id)) {
> + palmas_init_led(leds_data, offset, 3);
> + offset++;
> +
> + if (pdata->chrg_led_mode) {
> + palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
> + PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE,
> + PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE);
> +
> + palmas_init_led(leds_data, offset, 4);
> + }
> + }
> +
> + for (i = 0; i < leds_data->no_leds; i++) {
> + ret = led_classdev_register(leds_data->dev,
> + &leds_data->palmas_led[i].cdev);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "Failed to register LED no: %d err: %d\n",
> + i, ret);
> + goto err_led;
> + }
> + }
> +
> + if (!is_palmas_charger(palmas->product_id))
> + return 0;
> +
> + /* Set the LED current registers if set in platform data */
> + if (pdata->led1_current)
> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL1,
> + PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_MASK,
> + pdata->led1_current);
> +
> + if (pdata->led2_current)
> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL1,
> + PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_MASK,
> + pdata->led2_current <<
> + PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_SHIFT);
> +
> + if (pdata->led3_current)
> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL2,
> + PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK,
> + pdata->led3_current);
> +
> + if (pdata->led3_current)
> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL2,
> + PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK,
> + pdata->led3_current);
> +
> + if (pdata->led4_current)
> + palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
> + PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_MASK,
> + pdata->led4_current <<
> + PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_SHIFT);
> +
> + if (pdata->chrg_led_vbat_low)
> + palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
> + PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS,
> + PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS);
> +
> + return 0;
> +
> +err_led:
> + for (i = 0; i < leds_data->no_leds; i++)
> + led_classdev_unregister(&leds_data->palmas_led[i].cdev);
> + return ret;
> +}
> +
> +static int palmas_leds_remove(struct platform_device *pdev)
> +{
> + struct palmas_leds_data *leds_data = platform_get_drvdata(pdev);
> + int i;
> +
> + for (i = 0; i < leds_data->no_leds; i++)
> + led_classdev_unregister(&leds_data->palmas_led[i].cdev);
> + if (i)
> + kfree(leds_data->palmas_led);
> + kfree(leds_data);
> +
> + return 0;
> +}
> +
> +static struct of_device_id of_palmas_match_tbl[] = {
> + { .compatible = "ti,palmas-leds", },
> + { .compatible = "ti,twl6035-leds", },
> + { .compatible = "ti,twl6036-leds", },
> + { .compatible = "ti,twl6037-leds", },
> + { .compatible = "ti,tps65913-leds", },
> + { .compatible = "ti,tps65914-leds", },
> + { .compatible = "ti,tps80036-leds", },
> + { /* end */ }
> +};
> +
> +static struct platform_driver palmas_leds_driver = {
> + .driver = {
> + .name = "palmas-leds",
> + .of_match_table = of_palmas_match_tbl,
> + .owner = THIS_MODULE,
> + },
> + .probe = palmas_leds_probe,
> + .remove = palmas_leds_remove,
> +};
> +
> +module_platform_driver(palmas_leds_driver);
> +
> +MODULE_AUTHOR("Graeme Gregory <[email protected]>");
> +MODULE_DESCRIPTION("Palmas LED driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:palmas-leds");
> +MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
> diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
> index d8c303b..73186c8 100644
> --- a/include/linux/mfd/palmas.h
> +++ b/include/linux/mfd/palmas.h
> @@ -232,6 +232,36 @@ struct palmas_clk_platform_data {
> int clk32kgaudio_mode_sleep;
> };
>
> +enum palmas_led_current {
> + PALMAS_ILED_0mA = 0, /* 0 mA */
> + PALMAS_ILED_0m25A, /* 0.25 mA */
> + PALMAS_ILED_0m5A, /* 0.5 mA */
> + PALMAS_ILED_1m0A, /* 1.0 mA */
> + PALMAS_ILED_2m5A, /* 2.5 mA */
> + PALMAS_ILED_5m0A, /* 5.0 mA */
> + PALMAS_ILED_10m0A, /* 10.0 mA */
> + PALMAS_ILED_0m0A, /* 0 mA */
> +};
> +
> +#define is_palmas_led_current_ok(a) (((a) == PALMAS_ILED_0mA) || \
> + ((a) == PALMAS_ILED_0m25A) || \
> + ((a) == PALMAS_ILED_0m5A) || \
> + ((a) == PALMAS_ILED_1m0A) || \
> + ((a) == PALMAS_ILED_2m5A) || \
> + ((a) == PALMAS_ILED_5m0A) || \
> + ((a) == PALMAS_ILED_10m0A) || \
> + ((a) == PALMAS_ILED_0m0A))
> +
> +struct palmas_leds_platform_data {
> + int led1_current;
> + int led2_current;
> + int led3_current;
> + int led4_current;
> +
> + int chrg_led_mode;
> + int chrg_led_vbat_low;
> +};
> +
> struct palmas_platform_data {
> int irq_flags;
> int gpio_base;
> @@ -1856,6 +1886,12 @@ enum usb_irq_events {
> #define PALMAS_LED_CTRL 0x1
> #define PALMAS_PWM_CTRL1 0x2
> #define PALMAS_PWM_CTRL2 0x3
> +#define PALMAS_LED_PERIOD2_CTRL 0x4
> +#define PALMAS_LED_CTRL2 0x5
> +#define PALMAS_LED_CURRENT_CTRL1 0x6
> +#define PALMAS_LED_CURRENT_CTRL2 0x7
> +#define PALMAS_CHRG_LED_CTRL 0x8
> +#define PALMAS_LED_EN 0x9
>
> /* Bit definitions for LED_PERIOD_CTRL */
> #define PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK 0x38
> @@ -1883,6 +1919,50 @@ enum usb_irq_events {
> #define PALMAS_PWM_CTRL2_PWM_DUTY_SEL_MASK 0xff
> #define PALMAS_PWM_CTRL2_PWM_DUTY_SEL_SHIFT 0
>
> +/* Bit definitions for LED_PERIOD2_CTRL */
> +#define PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK 0x38
> +#define PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT 3
> +#define PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK 0x07
> +#define PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT 0
> +
> +/* Bit definitions for LED_CTRL2 */
> +#define PALMAS_LED_CTRL2_CHRG_LED_SEQ 0x20
> +#define PALMAS_LED_CTRL2_CHRG_LED_SEQ_SHIFT 5
> +#define PALMAS_LED_CTRL2_LED_3_SEQ 0x10
> +#define PALMAS_LED_CTRL2_LED_3_SEQ_SHIFT 4
> +#define PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK 0x0c
> +#define PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT 2
> +#define PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK 0x03
> +#define PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT 0
> +
> +/* Bit definitions for LED_CURRENT_CTRL1 */
> +#define PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_MASK 0x38
> +#define PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_SHIFT 3
> +#define PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_MASK 0x07
> +#define PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_SHIFT 0
> +
> +/* Bit definitions for LED_CURRENT_CTRL2 */
> +#define PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK 0x07
> +#define PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_SHIFT 0
> +
> +/* Bit definitions for CHRG_LED_CTRL */
> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_MASK 0x38
> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_SHIFT 3
> +#define PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS 0x02
> +#define PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS_SHIFT 1
> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE 0x01
> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE_SHIFT 0
> +
> +/* Bit definitions for LED_EN */
> +#define PALMAS_LED_EN_CHRG_LED_EN 0x08
> +#define PALMAS_LED_EN_CHRG_LED_EN_SHIFT 3
> +#define PALMAS_LED_EN_LED_3_EN 0x04
> +#define PALMAS_LED_EN_LED_3_EN_SHIFT 2
> +#define PALMAS_LED_EN_LED_2_EN 0x02
> +#define PALMAS_LED_EN_LED_2_EN_SHIFT 1
> +#define PALMAS_LED_EN_LED_1_EN 0x01
> +#define PALMAS_LED_EN_LED_1_EN_SHIFT 0
> +
> /* Registers for function INTERRUPT */
> #define PALMAS_INT1_STATUS 0x0
> #define PALMAS_INT1_MASK 0x1
On 22/03/13 14:04, Graeme Gregory wrote:
> On 22/03/13 13:36, Ian Lartey wrote:
>> The Palmas familly of chips has LED support. This is not always muxed
>> to output pins so depending on the setting of the mux this driver
>> will create the appropriate LED class devices.
>>
>> Signed-off-by: Graeme Gregory <[email protected]>
>> Signed-off-by: Ian Lartey <[email protected]>
>> ---
>> drivers/leds/Kconfig | 9 +
>> drivers/leds/Makefile | 1 +
>> drivers/leds/leds-palmas.c | 590 ++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/mfd/palmas.h | 80 ++++++
>> 4 files changed, 680 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/leds/leds-palmas.c
>>
>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>> index d44806d..249027e 100644
>> --- a/drivers/leds/Kconfig
>> +++ b/drivers/leds/Kconfig
>> @@ -479,6 +479,15 @@ config LEDS_BLINKM
>> This option enables support for the BlinkM RGB LED connected
>> through I2C. Say Y to enable support for the BlinkM LED.
>>
>> +config LEDS_PALMAS
>> + bool "LED support for the Palmas family of PMICs"
>> + depends on LEDS_CLASS
>> + depends on MFD_PALMAS
>> + help
>> + This option enables the driver for LED1 & LED2 pins on Palmas PMIC
>> + if these pins are enabled in the mux configuration. The driver support
>> + ON/OFF and blinking with hardware control.
>> +
>> comment "LED Triggers"
>> source "drivers/leds/trigger/Kconfig"
>>
>> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
>> index ac28977..3acb116 100644
>> --- a/drivers/leds/Makefile
>> +++ b/drivers/leds/Makefile
>> @@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
>> obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
>> obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
>> obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
>> +obj-$(CONFIG_LEDS_PALMAS) += leds-palmas.o
>>
>> # LED SPI Drivers
>> obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
>> diff --git a/drivers/leds/leds-palmas.c b/drivers/leds/leds-palmas.c
>> new file mode 100644
>> index 0000000..6bfdab2
>> --- /dev/null
>> +++ b/drivers/leds/leds-palmas.c
>> @@ -0,0 +1,590 @@
>> +/*
>> + * Driver for LED part of Palmas PMIC Chips
>> + *
>> + * Copyright 2011-2013 Texas Instruments Inc.
>> + *
>> + * Author: Graeme Gregory <[email protected]>
>> + * Author: Ian Lartey <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> + * Free Software Foundation; either version 2 of the License, or (at your
>> + * option) any later version.
>> + *
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/kernel.h>
>> +#include <linux/leds.h>
>> +#include <linux/mfd/palmas.h>
>> +#include <linux/module.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +struct palmas_leds_data;
>> +
>> +enum palmas_led_on_time {
>> + LED_ON_62_5MS = 0x00,
>> + LED_ON_125MS = 0x01,
>> + LED_ON_250MS = 0x02,
>> + LED_ON_500MS = 0x03,
>> +};
>> +
>> +enum palmas_led_period {
>> + LED_PERIOD_OFF = 0x00,
>> + LED_PERIOD_125MS = 0x01,
>> + LED_PERIOD_250MS = 0x02,
>> + LED_PERIOD_500MS = 0x03,
>> + LED_PERIOD_1000MS = 0x04,
>> + LED_PERIOD_2000MS = 0x05,
>> + LED_PERIOD_4000MS = 0x06,
>> + LED_PERIOD_8000MS = 0x07,
>> +};
>> +
>> +struct palmas_led {
>> + struct led_classdev cdev;
>> + struct palmas_leds_data *leds_data;
>> + int led_no;
>> + struct work_struct work;
>> + struct mutex mutex;
>> +
>> + spinlock_t value_lock;
>> +
>> + int blink;
>> + enum palmas_led_on_time on_time;
>> + enum palmas_led_period period;
>> + enum led_brightness brightness;
>> +};
>> +
>> +struct palmas_leds_data {
>> + struct device *dev;
>> + struct led_classdev cdev;
>> + struct palmas *palmas;
>> +
>> + struct palmas_led *palmas_led;
>> + int no_leds;
>> +};
>> +
>> +#define to_palmas_led(led_cdev) \
>> + container_of(led_cdev, struct palmas_led, cdev)
>> +
>> +static char *palmas_led_names[] = {
>> + "palmas:led1",
>> + "palmas:led2",
>> + "palmas:led3",
>> + "palmas:led4",
>> +};
>> +
>> +struct palmas_led_info {
>> + int mask;
>> + int shift;
>> +};
>> +
>> +struct palmas_led_info led_period_info[] = {
>> + {
>> + PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK,
>> + PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT,
>> + },
>> + {
>> + PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK,
>> + PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT,
>> + },
>> + {
>> + PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK,
>> + PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT,
>> + },
>> + {
>> + PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK,
>> + PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT,
>> + },
>> +};
>> +
>> +struct palmas_led_info led_time_info[] = {
>> + {
>> + PALMAS_LED_CTRL_LED_1_ON_TIME_MASK,
>> + PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT,
>> + },
>> + {
>> + PALMAS_LED_CTRL_LED_2_ON_TIME_MASK,
>> + PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT,
>> + },
>> + {
>> + PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK,
>> + PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT,
>> + },
>> + {
>> + PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK,
>> + PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT,
>> + },
>> +};
>> +
>> +enum palmas_led_period_shift {
>> + PALMAS_LED1_PERIOD_SHIFT = PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT,
>> + PALMAS_LED2_PERIOD_SHIFT = PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT,
>> + PALMAS_LED3_PERIOD_SHIFT = PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT,
>> + PALMAS_LED4_PERIOD_SHIFT =
>> + PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT,
>> +};
>> +
>> +enum palmas_led_period_mask {
>> + PALMAS_LED1_PERIOD_MASK = PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK,
>> + PALMAS_LED2_PERIOD_MASK = PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK,
>> + PALMAS_LED3_PERIOD_MASK = PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK,
>> + PALMAS_LED4_PERIOD_MASK = PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK,
>> +};
>> +
>> +enum palmas_led_time_shift {
>> + PALMAS_LED1_TIME_SHIFT = PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT,
>> + PALMAS_LED2_TIME_SHIFT = PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT,
>> + PALMAS_LED3_TIME_SHIFT = PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT,
>> + PALMAS_LED4_TIME_SHIFT = PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT,
>> +};
>> +
>> +enum palmas_led_time_mask {
>> + PALMAS_LED1_TIME_MASK = PALMAS_LED_CTRL_LED_1_ON_TIME_MASK,
>> + PALMAS_LED2_TIME_MASK = PALMAS_LED_CTRL_LED_2_ON_TIME_MASK,
>> + PALMAS_LED3_TIME_MASK = PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK,
>> + PALMAS_LED4_TIME_MASK = PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK,
>> +};
>> +
> It looks like these enums are some dead code you forgot to remove in
> this version.
Thanks for that. I'll wait for some more comments before I submit v10.
>
>> +static int palmas_led_read(struct palmas_leds_data *leds, unsigned int reg,
>> + unsigned int *dest)
>> +{
>> + return palmas_read(leds->palmas, PALMAS_LED_BASE, reg, dest);
>> +}
>> +
>> +static int palmas_led_write(struct palmas_leds_data *leds, unsigned int reg,
>> + unsigned int value)
>> +{
>> + return palmas_write(leds->palmas, PALMAS_LED_BASE, reg, value);
>> +}
>> +
>> +static int palmas_led_update_bits(struct palmas_leds_data *leds,
>> + unsigned int reg, unsigned int mask, unsigned int value)
>> +{
>> + return palmas_update_bits(leds->palmas, PALMAS_LED_BASE,
>> + reg, mask, value);
>> +}
>> +
>> +static void palmas_get_crtl_and_period(struct palmas_led *led,
>> + unsigned int *ctrl, unsigned int *period)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&led->value_lock, flags);
>> +
>> + *ctrl &= ~led_time_info[led->led_no].mask;
>> + *period &= ~led_period_info[led->led_no].mask;
>> +
>> + if (led->blink) {
>> + *ctrl |= led->on_time << led_time_info[led->led_no].shift;
>> + *period |= led->period << led_period_info[led->led_no].shift;
>> + } else {
>> + /*
>> + * for off value is zero which we already set by
>> + * masking earlier.
>> + */
>> + if (led->brightness) {
>> + *ctrl |= LED_ON_500MS <<
>> + led_time_info[led->led_no].shift;
>> + *period |= LED_PERIOD_500MS <<
>> + led_period_info[led->led_no].shift;
>> + }
>> + }
>> +
>> + spin_unlock_irqrestore(&led->value_lock, flags);
>> +}
>> +
>> +static void palmas_leds_work(struct work_struct *work)
>> +{
>> + struct palmas_led *led = container_of(work, struct palmas_led, work);
>> + unsigned int period, ctrl;
>> +
>> + mutex_lock(&led->mutex);
>> +
>> + palmas_led_read(led->leds_data, PALMAS_LED_CTRL, &ctrl);
>> + palmas_led_read(led->leds_data, PALMAS_LED_PERIOD_CTRL, &period);
>> +
>> + palmas_get_crtl_and_period(led, &ctrl, &period);
>> +
>> + if (led->led_no < 3) {
>> + palmas_led_write(led->leds_data, PALMAS_LED_CTRL, ctrl);
>> + palmas_led_write(led->leds_data, PALMAS_LED_PERIOD_CTRL,
>> + period);
>> + } else {
>> + palmas_led_write(led->leds_data, PALMAS_LED_CTRL2, ctrl);
>> + palmas_led_write(led->leds_data, PALMAS_LED_PERIOD2_CTRL,
>> + period);
>> + }
>> +
>> + if (is_palmas_charger(led->leds_data->palmas->product_id)) {
>> + if (led->brightness || led->blink)
>> + palmas_led_update_bits(led->leds_data, PALMAS_LED_EN,
>> + 1 << (led->led_no - 1), 0xFF);
>> + else
>> + palmas_led_update_bits(led->leds_data, PALMAS_LED_EN,
>> + 1 << (led->led_no - 1), 0x00);
>> + }
>> + mutex_unlock(&led->mutex);
>> +}
>> +
>> +static void palmas_leds_set(struct led_classdev *led_cdev,
>> + enum led_brightness value)
>> +{
>> + struct palmas_led *led = to_palmas_led(led_cdev);
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&led->value_lock, flags);
>> + led->brightness = value;
>> + led->blink = 0;
>> + schedule_work(&led->work);
>> + spin_unlock_irqrestore(&led->value_lock, flags);
>> +}
>> +
>> +static int palmas_leds_blink_set(struct led_classdev *led_cdev,
>> + unsigned long *delay_on,
>> + unsigned long *delay_off)
>> +{
>> + struct palmas_led *led = to_palmas_led(led_cdev);
>> + unsigned long flags;
>> + int ret = 0;
>> + int period;
>> +
>> + /* Pick some defaults if we've not been given times */
>> + if (*delay_on == 0 && *delay_off == 0) {
>> + *delay_on = 250;
>> + *delay_off = 250;
>> + }
>> +
>> + spin_lock_irqsave(&led->value_lock, flags);
>> +
>> + /*
>> + * We only have a limited selection of settings, see if we can
>> + * support the configuration we're being given
>> + */
>> + switch (*delay_on) {
>> + case 500:
>> + led->on_time = LED_ON_500MS;
>> + break;
>> + case 250:
>> + led->on_time = LED_ON_250MS;
>> + break;
>> + case 125:
>> + led->on_time = LED_ON_125MS;
>> + break;
>> + case 62:
>> + case 63:
>> + /* Actually 62.5ms */
>> + led->on_time = LED_ON_62_5MS;
>> + break;
>> + default:
>> + ret = -EINVAL;
>> + break;
>> + }
>> +
>> + period = *delay_on + *delay_off;
>> +
>> + if (ret == 0) {
>> + switch (period) {
>> + case 124:
>> + case 125:
>> + case 126:
>> + /* All possible variations of 62.5 + 62.5 */
>> + led->period = LED_PERIOD_125MS;
>> + break;
>> + case 250:
>> + led->period = LED_PERIOD_250MS;
>> + break;
>> + case 500:
>> + led->period = LED_PERIOD_500MS;
>> + break;
>> + case 1000:
>> + led->period = LED_PERIOD_1000MS;
>> + break;
>> + case 2000:
>> + led->period = LED_PERIOD_2000MS;
>> + break;
>> + case 4000:
>> + led->period = LED_PERIOD_4000MS;
>> + break;
>> + case 8000:
>> + led->period = LED_PERIOD_8000MS;
>> + break;
>> + default:
>> + ret = -EINVAL;
>> + break;
>> + }
>> + }
>> +
>> + if (ret == 0)
>> + led->blink = 1;
>> + else
>> + led->blink = 0;
>> +
>> + /*
>> + * Always update; if we fail turn off blinking since we expect
>> + * a software fallback.
>> + */
>> + schedule_work(&led->work);
>> +
>> + spin_unlock_irqrestore(&led->value_lock, flags);
>> +
>> + return ret;
>> +}
>> +
>> +static void palmas_init_led(struct palmas_leds_data *leds_data, int offset,
>> + int led_no)
>> +{
>> + mutex_init(&leds_data->palmas_led[offset].mutex);
>> + INIT_WORK(&leds_data->palmas_led[offset].work, palmas_leds_work);
>> + spin_lock_init(&leds_data->palmas_led[offset].value_lock);
>> +
>> + leds_data->palmas_led[offset].leds_data = leds_data;
>> + leds_data->palmas_led[offset].led_no = led_no;
>> + leds_data->palmas_led[offset].cdev.name = palmas_led_names[led_no - 1];
>> + leds_data->palmas_led[offset].cdev.default_trigger = NULL;
>> + leds_data->palmas_led[offset].cdev.brightness_set = palmas_leds_set;
>> + leds_data->palmas_led[offset].cdev.blink_set = palmas_leds_blink_set;
>> +}
>> +
>> +
>> +static int palmas_dt_to_pdata(struct device *dev,
>> + struct device_node *node,
>> + struct palmas_leds_platform_data *pdata)
>> +{
>> + int ret;
>> + u32 prop;
>> +
>> + ret = of_property_read_u32(node, "ti,led1-current", &prop);
>> + if (!ret) {
>> + if (is_palmas_led_current_ok(prop))
>> + pdata->led1_current = prop;
>> + else
>> + goto err;
>> + }
>> +
>> + ret = of_property_read_u32(node, "ti,led2-current", &prop);
>> + if (!ret) {
>> + if (is_palmas_led_current_ok(prop))
>> + pdata->led2_current = prop;
>> + else
>> + goto err;
>> + }
>> +
>> + ret = of_property_read_u32(node, "ti,led3-current", &prop);
>> + if (!ret) {
>> + if (is_palmas_led_current_ok(prop))
>> + pdata->led3_current = prop;
>> + else
>> + goto err;
>> + }
>> +
>> + ret = of_property_read_u32(node, "ti,led4-current", &prop);
>> + if (!ret) {
>> + if (is_palmas_led_current_ok(prop))
>> + pdata->led4_current = prop;
>> + else
>> + goto err;
>> + }
>> +
>> + ret = of_property_read_u32(node, "ti,chrg-led-mode", &prop);
>> + if (!ret)
>> + pdata->chrg_led_mode = prop;
>> +
>> + ret = of_property_read_u32(node, "ti,chrg-led-vbat-low", &prop);
>> + if (!ret)
>> + pdata->chrg_led_vbat_low = prop;
>> +
>> + return 0;
>> +
>> +err:
>> + dev_err(dev, "there are no LED curent - out of bounds\n");
>> + return -EINVAL;
>> +}
>> +
>> +static int palmas_leds_probe(struct platform_device *pdev)
>> +{
>> + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
>> + struct palmas_leds_platform_data *pdata = pdev->dev.platform_data;
>> + struct palmas_leds_data *leds_data;
>> + struct device_node *node = pdev->dev.of_node;
>> + int ret, i;
>> + int offset = 0;
>> +
>> + if (!palmas->led_muxed && !is_palmas_charger(palmas->product_id)) {
>> + dev_err(&pdev->dev, "there are no LEDs muxed\n");
>> + return -EINVAL;
>> + }
>> +
>> + /* Palmas charger requires platform data */
>> + if (is_palmas_charger(palmas->product_id) && node && !pdata) {
>> +
>> + if (!pdata)
>> + return -ENOMEM;
>> +
>> + ret = palmas_dt_to_pdata(&pdev->dev, node, pdata);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (is_palmas_charger(palmas->product_id) && !pdata)
>> + return -EINVAL;
>> +
>> + leds_data = devm_kzalloc(&pdev->dev, sizeof(*leds_data), GFP_KERNEL);
>> + if (!leds_data)
>> + return -ENOMEM;
>> + platform_set_drvdata(pdev, leds_data);
>> +
>> + leds_data->palmas = palmas;
>> +
>> + switch (palmas->led_muxed) {
>> + case PALMAS_LED1_MUXED | PALMAS_LED2_MUXED:
>> + leds_data->no_leds = 2;
>> + break;
>> + case PALMAS_LED1_MUXED:
>> + case PALMAS_LED2_MUXED:
>> + leds_data->no_leds = 1;
>> + break;
>> + default:
>> + leds_data->no_leds = 0;
>> + break;
>> + }
>> +
>> + if (is_palmas_charger(palmas->product_id)) {
>> + if (pdata->chrg_led_mode)
>> + leds_data->no_leds += 2;
>> + else
>> + leds_data->no_leds++;
>> + }
>> +
>> + if (leds_data->no_leds == 0)
>> + leds_data->palmas_led = NULL;
>> + else
>> + leds_data = devm_kzalloc(&pdev->dev,
>> + leds_data->no_leds * sizeof(*leds_data),
>> + GFP_KERNEL);
>> +
>> + /* Initialise LED1 */
>> + if (palmas->led_muxed & PALMAS_LED1_MUXED) {
>> + palmas_init_led(leds_data, offset, 1);
>> + offset++;
>> + }
>> +
>> + /* Initialise LED2 */
>> + if (palmas->led_muxed & PALMAS_LED2_MUXED) {
>> + palmas_init_led(leds_data, offset, 2);
>> + offset++;
>> + }
>> +
>> + if (is_palmas_charger(palmas->product_id)) {
>> + palmas_init_led(leds_data, offset, 3);
>> + offset++;
>> +
>> + if (pdata->chrg_led_mode) {
>> + palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
>> + PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE,
>> + PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE);
>> +
>> + palmas_init_led(leds_data, offset, 4);
>> + }
>> + }
>> +
>> + for (i = 0; i < leds_data->no_leds; i++) {
>> + ret = led_classdev_register(leds_data->dev,
>> + &leds_data->palmas_led[i].cdev);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev,
>> + "Failed to register LED no: %d err: %d\n",
>> + i, ret);
>> + goto err_led;
>> + }
>> + }
>> +
>> + if (!is_palmas_charger(palmas->product_id))
>> + return 0;
>> +
>> + /* Set the LED current registers if set in platform data */
>> + if (pdata->led1_current)
>> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL1,
>> + PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_MASK,
>> + pdata->led1_current);
>> +
>> + if (pdata->led2_current)
>> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL1,
>> + PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_MASK,
>> + pdata->led2_current <<
>> + PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_SHIFT);
>> +
>> + if (pdata->led3_current)
>> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL2,
>> + PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK,
>> + pdata->led3_current);
>> +
>> + if (pdata->led3_current)
>> + palmas_led_update_bits(leds_data, PALMAS_LED_CURRENT_CTRL2,
>> + PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK,
>> + pdata->led3_current);
>> +
>> + if (pdata->led4_current)
>> + palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
>> + PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_MASK,
>> + pdata->led4_current <<
>> + PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_SHIFT);
>> +
>> + if (pdata->chrg_led_vbat_low)
>> + palmas_led_update_bits(leds_data, PALMAS_CHRG_LED_CTRL,
>> + PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS,
>> + PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS);
>> +
>> + return 0;
>> +
>> +err_led:
>> + for (i = 0; i < leds_data->no_leds; i++)
>> + led_classdev_unregister(&leds_data->palmas_led[i].cdev);
>> + return ret;
>> +}
>> +
>> +static int palmas_leds_remove(struct platform_device *pdev)
>> +{
>> + struct palmas_leds_data *leds_data = platform_get_drvdata(pdev);
>> + int i;
>> +
>> + for (i = 0; i < leds_data->no_leds; i++)
>> + led_classdev_unregister(&leds_data->palmas_led[i].cdev);
>> + if (i)
>> + kfree(leds_data->palmas_led);
>> + kfree(leds_data);
>> +
>> + return 0;
>> +}
>> +
>> +static struct of_device_id of_palmas_match_tbl[] = {
>> + { .compatible = "ti,palmas-leds", },
>> + { .compatible = "ti,twl6035-leds", },
>> + { .compatible = "ti,twl6036-leds", },
>> + { .compatible = "ti,twl6037-leds", },
>> + { .compatible = "ti,tps65913-leds", },
>> + { .compatible = "ti,tps65914-leds", },
>> + { .compatible = "ti,tps80036-leds", },
>> + { /* end */ }
>> +};
>> +
>> +static struct platform_driver palmas_leds_driver = {
>> + .driver = {
>> + .name = "palmas-leds",
>> + .of_match_table = of_palmas_match_tbl,
>> + .owner = THIS_MODULE,
>> + },
>> + .probe = palmas_leds_probe,
>> + .remove = palmas_leds_remove,
>> +};
>> +
>> +module_platform_driver(palmas_leds_driver);
>> +
>> +MODULE_AUTHOR("Graeme Gregory <[email protected]>");
>> +MODULE_DESCRIPTION("Palmas LED driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:palmas-leds");
>> +MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
>> diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
>> index d8c303b..73186c8 100644
>> --- a/include/linux/mfd/palmas.h
>> +++ b/include/linux/mfd/palmas.h
>> @@ -232,6 +232,36 @@ struct palmas_clk_platform_data {
>> int clk32kgaudio_mode_sleep;
>> };
>>
>> +enum palmas_led_current {
>> + PALMAS_ILED_0mA = 0, /* 0 mA */
>> + PALMAS_ILED_0m25A, /* 0.25 mA */
>> + PALMAS_ILED_0m5A, /* 0.5 mA */
>> + PALMAS_ILED_1m0A, /* 1.0 mA */
>> + PALMAS_ILED_2m5A, /* 2.5 mA */
>> + PALMAS_ILED_5m0A, /* 5.0 mA */
>> + PALMAS_ILED_10m0A, /* 10.0 mA */
>> + PALMAS_ILED_0m0A, /* 0 mA */
>> +};
>> +
>> +#define is_palmas_led_current_ok(a) (((a) == PALMAS_ILED_0mA) || \
>> + ((a) == PALMAS_ILED_0m25A) || \
>> + ((a) == PALMAS_ILED_0m5A) || \
>> + ((a) == PALMAS_ILED_1m0A) || \
>> + ((a) == PALMAS_ILED_2m5A) || \
>> + ((a) == PALMAS_ILED_5m0A) || \
>> + ((a) == PALMAS_ILED_10m0A) || \
>> + ((a) == PALMAS_ILED_0m0A))
>> +
>> +struct palmas_leds_platform_data {
>> + int led1_current;
>> + int led2_current;
>> + int led3_current;
>> + int led4_current;
>> +
>> + int chrg_led_mode;
>> + int chrg_led_vbat_low;
>> +};
>> +
>> struct palmas_platform_data {
>> int irq_flags;
>> int gpio_base;
>> @@ -1856,6 +1886,12 @@ enum usb_irq_events {
>> #define PALMAS_LED_CTRL 0x1
>> #define PALMAS_PWM_CTRL1 0x2
>> #define PALMAS_PWM_CTRL2 0x3
>> +#define PALMAS_LED_PERIOD2_CTRL 0x4
>> +#define PALMAS_LED_CTRL2 0x5
>> +#define PALMAS_LED_CURRENT_CTRL1 0x6
>> +#define PALMAS_LED_CURRENT_CTRL2 0x7
>> +#define PALMAS_CHRG_LED_CTRL 0x8
>> +#define PALMAS_LED_EN 0x9
>>
>> /* Bit definitions for LED_PERIOD_CTRL */
>> #define PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK 0x38
>> @@ -1883,6 +1919,50 @@ enum usb_irq_events {
>> #define PALMAS_PWM_CTRL2_PWM_DUTY_SEL_MASK 0xff
>> #define PALMAS_PWM_CTRL2_PWM_DUTY_SEL_SHIFT 0
>>
>> +/* Bit definitions for LED_PERIOD2_CTRL */
>> +#define PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_MASK 0x38
>> +#define PALMAS_LED_PERIOD2_CTRL_CHRG_LED_PERIOD_SHIFT 3
>> +#define PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_MASK 0x07
>> +#define PALMAS_LED_PERIOD2_CTRL_LED_3_PERIOD_SHIFT 0
>> +
>> +/* Bit definitions for LED_CTRL2 */
>> +#define PALMAS_LED_CTRL2_CHRG_LED_SEQ 0x20
>> +#define PALMAS_LED_CTRL2_CHRG_LED_SEQ_SHIFT 5
>> +#define PALMAS_LED_CTRL2_LED_3_SEQ 0x10
>> +#define PALMAS_LED_CTRL2_LED_3_SEQ_SHIFT 4
>> +#define PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_MASK 0x0c
>> +#define PALMAS_LED_CTRL2_CHRG_LED_ON_TIME_SHIFT 2
>> +#define PALMAS_LED_CTRL2_LED_3_ON_TIME_MASK 0x03
>> +#define PALMAS_LED_CTRL2_LED_3_ON_TIME_SHIFT 0
>> +
>> +/* Bit definitions for LED_CURRENT_CTRL1 */
>> +#define PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_MASK 0x38
>> +#define PALMAS_LED_CURRENT_CTRL1_LED_2_CURRENT_SHIFT 3
>> +#define PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_MASK 0x07
>> +#define PALMAS_LED_CURRENT_CTRL1_LED_1_CURRENT_SHIFT 0
>> +
>> +/* Bit definitions for LED_CURRENT_CTRL2 */
>> +#define PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_MASK 0x07
>> +#define PALMAS_LED_CURRENT_CTRL2_LED_3_CURRENT_SHIFT 0
>> +
>> +/* Bit definitions for CHRG_LED_CTRL */
>> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_MASK 0x38
>> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_CURRENT_SHIFT 3
>> +#define PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS 0x02
>> +#define PALMAS_CHRG_LED_CTRL_CHRG_LOWBAT_BLK_DIS_SHIFT 1
>> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE 0x01
>> +#define PALMAS_CHRG_LED_CTRL_CHRG_LED_MODE_SHIFT 0
>> +
>> +/* Bit definitions for LED_EN */
>> +#define PALMAS_LED_EN_CHRG_LED_EN 0x08
>> +#define PALMAS_LED_EN_CHRG_LED_EN_SHIFT 3
>> +#define PALMAS_LED_EN_LED_3_EN 0x04
>> +#define PALMAS_LED_EN_LED_3_EN_SHIFT 2
>> +#define PALMAS_LED_EN_LED_2_EN 0x02
>> +#define PALMAS_LED_EN_LED_2_EN_SHIFT 1
>> +#define PALMAS_LED_EN_LED_1_EN 0x01
>> +#define PALMAS_LED_EN_LED_1_EN_SHIFT 0
>> +
>> /* Registers for function INTERRUPT */
>> #define PALMAS_INT1_STATUS 0x0
>> #define PALMAS_INT1_MASK 0x1
>
On Fri, Mar 22, 2013 at 01:36:13PM +0000, Ian Lartey wrote:
> Signed-off-by: Ian Lartey <[email protected]>
Applied, thanks.
On Fri, Mar 22, 2013 at 2:36 PM, Ian Lartey <[email protected]> wrote:
> Palmas charger has 16 GPIOs
> add palmas_gpio_[read|write|update] api to take account
> second bank of GPIOs
>
> Signed-off-by: Ian Lartey <[email protected]>
> Signed-off-by: Graeme Gregory <[email protected]>
> ---
> drivers/gpio/gpio-palmas.c | 77 ++++++++++++++++++++++++++++++++++++--------
> include/linux/mfd/palmas.h | 12 ++++++-
> 2 files changed, 74 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
> index e3a4e56..636648c 100644
> --- a/drivers/gpio/gpio-palmas.c
> +++ b/drivers/gpio/gpio-palmas.c
> @@ -1,7 +1,7 @@
> /*
> - * TI Palma series PMIC's GPIO driver.
> + * TI Palmas series PMIC's GPIO driver.
> *
> - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
> + * Copyright (c) 2012-2012, NVIDIA CORPORATION. All rights reserved.
Do you mean 2012-2013?
Anyway, Acked-by: Linus Walleij <[email protected]>
Yours,
Linus Walleij