2011-03-08 09:42:03

by Linus Walleij

[permalink] [raw]
Subject: [PATCH 1/3] mfd: add a core driver for TI TPS61050/TPS61052 chips

From: Linus Walleij <[email protected]>

The TPS61050/TPS61052 are boost converters, LED drivers, LED flash
drivers and a simple GPIO pin chips.

Cc: Liam Girdwood <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Ola Lilja <[email protected]>
Signed-off-by: Linus Walleij <[email protected]>
---
drivers/mfd/Kconfig | 12 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/tps6105x.c | 251 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/tps6105x.h | 101 +++++++++++++++++
4 files changed, 365 insertions(+), 0 deletions(-)
create mode 100644 drivers/mfd/tps6105x.c
create mode 100644 include/linux/mfd/tps6105x.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fd01836..cb61262 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -118,6 +118,18 @@ config UCB1400_CORE
To compile this driver as a module, choose M here: the
module will be called ucb1400_core.

+config TPS6105X
+ tristate "TPS61050/61052 Boost Converters"
+ depends on I2C && EXPERIMENTAL
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ default y if MACH_U8500
+ help
+ This option enables a driver for the TP61050/TPS61052
+ high-power "white LED driver". This boost converter is
+ sometimes used for other things than white LEDs, and
+ also contains a GPIO pin.
+
config TPS65010
tristate "TPS6501x Power Management chips"
depends on I2C && GPIOLIB
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a54e2c7..6eb8372 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o

+obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MENELAUS) += menelaus.o
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
new file mode 100644
index 0000000..24f4043
--- /dev/null
+++ b/drivers/mfd/tps6105x.c
@@ -0,0 +1,251 @@
+/*
+ * Core driver for TPS61050/61052 boost converters, used for while LED
+ * driving, audio power amplification, white LED flash, and generic
+ * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in)
+ * and a flash synchronization pin to synchronize flash events when used as
+ * flashgun.
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Written on behalf of Linaro
+ *
+ * Author: Linus Walleij <[email protected]> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6105x.h>
+
+int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value)
+{
+ int ret;
+
+ ret = mutex_lock_interruptible(&tps6105x->lock);
+ if (ret)
+ return ret;
+ ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value);
+ mutex_unlock(&tps6105x->lock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf)
+{
+ int ret;
+
+ ret = mutex_lock_interruptible(&tps6105x->lock);
+ if (ret)
+ return ret;
+ ret = i2c_smbus_read_byte_data(tps6105x->client, reg);
+ mutex_unlock(&tps6105x->lock);
+ if (ret < 0)
+ return ret;
+
+ *buf = ret;
+ return 0;
+}
+
+/*
+ * Masks off the bits in the mask and sets the bits in the bitvalues
+ * parameter in one atomic operation
+ */
+int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg,
+ u8 bitmask, u8 bitvalues)
+{
+ int ret;
+ u8 regval;
+
+ ret = mutex_lock_interruptible(&tps6105x->lock);
+ if (ret)
+ return ret;
+ ret = i2c_smbus_read_byte_data(tps6105x->client, reg);
+ if (ret < 0)
+ goto fail;
+ regval = ret;
+ regval = (~bitmask & regval) | (bitmask & bitvalues);
+ ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval);
+fail:
+ mutex_unlock(&tps6105x->lock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+
+static int __devinit tps6105x_startup(struct tps6105x *tps6105x)
+{
+ int ret;
+ u8 regval;
+
+ ret = tps6105x_get(tps6105x, TPS6105X_REG_0, &regval);
+ if (ret)
+ return ret;
+ switch (regval >> TPS6105X_REG0_MODE_SHIFT) {
+ case TPS6105X_REG0_MODE_SHUTDOWN:
+ dev_info(&tps6105x->client->dev,
+ "TPS6105x found in SHUTDOWN mode\n");
+ break;
+ case TPS6105X_REG0_MODE_TORCH:
+ dev_info(&tps6105x->client->dev,
+ "TPS6105x found in TORCH mode\n");
+ break;
+ case TPS6105X_REG0_MODE_TORCH_FLASH:
+ dev_info(&tps6105x->client->dev,
+ "TPS6105x found in FLASH mode\n");
+ break;
+ case TPS6105X_REG0_MODE_VOLTAGE:
+ dev_info(&tps6105x->client->dev,
+ "TPS6105x found in VOLTAGE mode\n");
+ break;
+ default:
+ break;
+ }
+
+ /* Put the chip in SHUTDOWN mode */
+ ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0,
+ TPS6105X_REG0_MODE_MASK,
+ TPS6105X_REG0_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* MFD cells for subdevices */
+static struct mfd_cell tps6105x_cells[] = {
+ {
+ .name = "tps6105x-regulator",
+ .id = -1,
+ },
+ {
+ .name = "tps6105x-leds",
+ .id = -1,
+ },
+ {
+ .name = "tps6105x-flash",
+ .id = -1,
+ },
+ {
+ .name = "tps6105x-gpio",
+ .id = -1,
+ },
+};
+
+static int __devinit tps6105x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps6105x *tps6105x;
+ struct tps6105x_platform_data *pdata;
+ int ret;
+ int i;
+
+ tps6105x = kzalloc(sizeof(*tps6105x), GFP_KERNEL);
+ if (!tps6105x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, tps6105x);
+ tps6105x->client = client;
+ pdata = client->dev.platform_data;
+ tps6105x->pdata = pdata;
+ mutex_init(&tps6105x->lock);
+
+ ret = tps6105x_startup(tps6105x);
+ if (ret) {
+ dev_err(&client->dev, "chip initialization failed\n");
+ goto fail;
+ }
+
+ /* Set up and register the platform devices. */
+ for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) {
+ /* One state holder for all drivers, this is simple */
+ tps6105x_cells[i].driver_data = tps6105x;
+ }
+
+ ret = mfd_add_devices(&client->dev, 0, tps6105x_cells,
+ ARRAY_SIZE(tps6105x_cells), NULL, 0);
+ if (ret)
+ goto fail;
+
+ /* Remove warning texts when you implement new cell drivers */
+ switch (pdata->mode) {
+ case TPS6105X_MODE_SHUTDOWN:
+ dev_info(&client->dev,
+ "present, not used for anything\n");
+ break;
+ case TPS6105X_MODE_TORCH:
+ dev_warn(&client->dev,
+ "torch mode is unsupported\n");
+ break;
+ case TPS6105X_MODE_TORCH_FLASH:
+ dev_warn(&client->dev,
+ "flash mode is unsupported\n");
+ break;
+ case TPS6105X_MODE_VOLTAGE:
+ default:
+ break;
+ }
+ return 0;
+
+fail:
+ i2c_set_clientdata(client, NULL);
+ kfree(tps6105x);
+ return ret;
+}
+
+static int __devexit tps6105x_remove(struct i2c_client *client)
+{
+ struct tps6105x *tps6105x = i2c_get_clientdata(client);
+
+ /* Put chip in shutdown mode */
+ tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0,
+ TPS6105X_REG0_MODE_MASK,
+ TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT);
+
+ kfree(tps6105x);
+ return 0;
+}
+
+static const struct i2c_device_id tps6105x_id[] = {
+ { "tps61050", 0 },
+ { "tps61052", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps6105x_id);
+
+static struct i2c_driver tps6105x_driver = {
+ .driver = {
+ .name = "tps6105x",
+ },
+ .probe = tps6105x_probe,
+ .remove = __devexit_p(tps6105x_remove),
+ .id_table = tps6105x_id,
+};
+
+static int __init tps6105x_init(void)
+{
+ return i2c_add_driver(&tps6105x_driver);
+}
+
+static void __exit tps6105x_exit(void)
+{
+ i2c_del_driver(&tps6105x_driver);
+}
+
+subsys_initcall(tps6105x_init);
+module_exit(tps6105x_exit);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/tps6105x.h b/include/linux/mfd/tps6105x.h
new file mode 100644
index 0000000..884c28d
--- /dev/null
+++ b/include/linux/mfd/tps6105x.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Written on behalf of Linaro
+ *
+ * Author: Linus Walleij <[email protected]> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef MFD_TPS6105X_H
+#define MFD_TPS6105X_H
+
+#include <linux/i2c.h>
+#include <linux/regulator/machine.h>
+
+/*
+ * Register definitions to all subdrivers
+ */
+#define TPS6105X_REG_0 (0x00)
+#define TPS6105X_REG0_MODE_SHIFT (6)
+#define TPS6105X_REG0_MODE_MASK (0x03<<6)
+/* These defines for both reg0 and reg1 */
+#define TPS6105X_REG0_MODE_SHUTDOWN (0x00)
+#define TPS6105X_REG0_MODE_TORCH (0x01)
+#define TPS6105X_REG0_MODE_TORCH_FLASH (0x02)
+#define TPS6105X_REG0_MODE_VOLTAGE (0x03)
+#define TPS6105X_REG0_VOLTAGE_SHIFT (4)
+#define TPS6105X_REG0_VOLTAGE_MASK (3<<4)
+#define TPS6105X_REG0_VOLTAGE_450 (0)
+#define TPS6105X_REG0_VOLTAGE_500 (1)
+#define TPS6105X_REG0_VOLTAGE_525 (2)
+#define TPS6105X_REG0_VOLTAGE_500_2 (3)
+#define TPS6105X_REG0_DIMMING_SHIFT (3)
+#define TPS6105X_REG0_TORCHC_SHIFT (0)
+#define TPS6105X_REG0_TORCHC_MASK (7<<0)
+#define TPS6105X_REG0_TORCHC_0 (0x00)
+#define TPS6105X_REG0_TORCHC_50 (0x01)
+#define TPS6105X_REG0_TORCHC_75 (0x02)
+#define TPS6105X_REG0_TORCHC_100 (0x03)
+#define TPS6105X_REG0_TORCHC_150 (0x04)
+#define TPS6105X_REG0_TORCHC_200 (0x05)
+#define TPS6105X_REG0_TORCHC_250_400 (0x06)
+#define TPS6105X_REG0_TORCHC_250_500 (0x07)
+#define TPS6105X_REG_1 (0x01)
+#define TPS6105X_REG1_MODE_SHIFT (6)
+#define TPS6105X_REG1_MODE_MASK (0x03<<6)
+#define TPS6105X_REG1_MODE_SHUTDOWN (0x00)
+#define TPS6105X_REG1_MODE_TORCH (0x01)
+#define TPS6105X_REG1_MODE_TORCH_FLASH (0x02)
+#define TPS6105X_REG1_MODE_VOLTAGE (0x03)
+#define TPS6105X_REG_2 (0x02)
+#define TPS6105X_REG_3 (0x03)
+
+/**
+ * enum tps6105x_mode - desired mode for the TPS6105x
+ * @TPS6105X_MODE_SHUTDOWN: this instance is inactive, not used for anything
+ * @TPS61905X_MODE_TORCH: this instance is used as a LED, usually a while
+ * LED, for example as backlight or flashlight. If this is set, the
+ * TPS6105X will register to the LED framework
+ * @TPS6105X_MODE_TORCH_FLASH: this instance is used as a flashgun, usually
+ * in a camera
+ * @TPS6105X_MODE_VOLTAGE: this instance is used as a voltage regulator and
+ * will register to the regulator framework
+ */
+enum tps6105x_mode {
+ TPS6105X_MODE_SHUTDOWN,
+ TPS6105X_MODE_TORCH,
+ TPS6105X_MODE_TORCH_FLASH,
+ TPS6105X_MODE_VOLTAGE,
+};
+
+/**
+ * struct tps6105x_platform_data - TPS61905x platform data
+ * @mode: what mode this instance shall be operated in,
+ * this is not selectable at runtime
+ * @regulator_data: initialization data for the voltage
+ * regulator if used as a voltage source
+ */
+struct tps6105x_platform_data {
+ enum tps6105x_mode mode;
+ struct regulator_init_data *regulator_data;
+};
+
+/**
+ * struct tps6105x - state holder for the TPS6105x drivers
+ * @mutex: mutex to serialize I2C accesses
+ * @i2c_client: corresponding I2C client
+ * @regulator: regulator device if used in voltage mode
+ */
+struct tps6105x {
+ struct tps6105x_platform_data *pdata;
+ struct mutex lock;
+ struct i2c_client *client;
+ struct regulator_dev *regulator;
+};
+
+extern int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value);
+extern int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf);
+extern int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg,
+ u8 bitmask, u8 bitvalues);
+
+#endif
--
1.7.3.2


2011-03-08 11:52:25

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 1/3] mfd: add a core driver for TI TPS61050/TPS61052 chips

On Tue, Mar 08, 2011 at 10:40:41AM +0100, Linus Walleij wrote:

> +config TPS6105X
> + tristate "TPS61050/61052 Boost Converters"
> + depends on I2C && EXPERIMENTAL

Why EXPERIMENTAL?

> + select REGULATOR
> + select REGULATOR_FIXED_VOLTAGE

> + default y if MACH_U8500

Not sure I'm enthusiastic about that - it's sensible for the machine to
want that but I can see this getting out of hand if you've got a lot of
machines doing that. Perhaps have the machine select the regulator for
builds with REGULATOR enabled?

> + /* Put the chip in SHUTDOWN mode */
> + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0,
> + TPS6105X_REG0_MODE_MASK,
> + TPS6105X_REG0_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT);
> + if (ret)
> + return ret;

I'm not sure this is what we want. If the device is driving the
backlight it'll result in the backlight flashing off during boot which
doesn't seem desirable. The standard thing is just to inherit the
hardware state.

2011-03-08 12:59:27

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/3] mfd: add a core driver for TI TPS61050/TPS61052 chips

On 03/08/2011 12:52 PM, Mark Brown wrote:
> On Tue, Mar 08, 2011 at 10:40:41AM +0100, Linus Walleij wrote:
>
>
>> +config TPS6105X
>> + tristate "TPS61050/61052 Boost Converters"
>> + depends on I2C&& EXPERIMENTAL
>>
> Why EXPERIMENTAL?
>

No real reason, I'll drop it.

> + default y if MACH_U8500
>
> Not sure I'm enthusiastic about that - it's sensible for the machine to
> want that but I can see this getting out of hand if you've got a lot of
> machines doing that. Perhaps have the machine select the regulator for
> builds with REGULATOR enabled?
>

OK I'll move this over to the mach-ux500 patch using
select.

>> + /* Put the chip in SHUTDOWN mode */
>> + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0,
>> + TPS6105X_REG0_MODE_MASK,
>> + TPS6105X_REG0_MODE_SHUTDOWN<< TPS6105X_REG0_MODE_SHIFT);
>> + if (ret)
>> + return ret;
>>
> I'm not sure this is what we want. If the device is driving the
> backlight it'll result in the backlight flashing off during boot which
> doesn't seem desirable. The standard thing is just to inherit the
> hardware state.
>

It's a leftover from code that was toggling the mode to see if
the hardware was there (it was reflected in another register).

I'll take it out.

Thanks,
Linus Walleij

2011-03-18 01:05:47

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 1/3] mfd: add a core driver for TI TPS61050/TPS61052 chips

Hi Linus,

On Tue, Mar 08, 2011 at 10:40:41AM +0100, Linus Walleij wrote:
> From: Linus Walleij <[email protected]>
>
> The TPS61050/TPS61052 are boost converters, LED drivers, LED flash
> drivers and a simple GPIO pin chips.
Actualy, I had to fix some more stuff from this code:

> +int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value)
> +{
> + int ret;
> +
> + ret = mutex_lock_interruptible(&tps6105x->lock);
> + if (ret)
> + return ret;
> + ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value);
> + mutex_unlock(&tps6105x->lock);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf)
> +{
> + int ret;
> +
> + ret = mutex_lock_interruptible(&tps6105x->lock);
> + if (ret)
> + return ret;
> + ret = i2c_smbus_read_byte_data(tps6105x->client, reg);
> + mutex_unlock(&tps6105x->lock);
> + if (ret < 0)
> + return ret;
> +
> + *buf = ret;
> + return 0;
> +}
> +
> +/*
> + * Masks off the bits in the mask and sets the bits in the bitvalues
> + * parameter in one atomic operation
> + */
> +int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg,
> + u8 bitmask, u8 bitvalues)
> +{
> + int ret;
> + u8 regval;
> +
> + ret = mutex_lock_interruptible(&tps6105x->lock);
> + if (ret)
> + return ret;
> + ret = i2c_smbus_read_byte_data(tps6105x->client, reg);
> + if (ret < 0)
> + goto fail;
> + regval = ret;
> + regval = (~bitmask & regval) | (bitmask & bitvalues);
> + ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval);
> +fail:
> + mutex_unlock(&tps6105x->lock);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
Export the 3 above symbols for the regulator sub device to be able to build as
a module.

> +static int __devinit tps6105x_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct tps6105x *tps6105x;
> + struct tps6105x_platform_data *pdata;
> + int ret;
> + int i;
> +
> + tps6105x = kzalloc(sizeof(*tps6105x), GFP_KERNEL);
> + if (!tps6105x)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(client, tps6105x);
> + tps6105x->client = client;
> + pdata = client->dev.platform_data;
> + tps6105x->pdata = pdata;
> + mutex_init(&tps6105x->lock);
> +
> + ret = tps6105x_startup(tps6105x);
> + if (ret) {
> + dev_err(&client->dev, "chip initialization failed\n");
> + goto fail;
> + }
> +
> + /* Set up and register the platform devices. */
> + for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) {
> + /* One state holder for all drivers, this is simple */
> + tps6105x_cells[i].driver_data = tps6105x;
driver_data has been replaced by mfd_data, and sub devices are supposed to
fetch it back with mfd_get_data(). I fixed the regulator driver as well,
please drop me a patch if it's not working as expected.

Cheers,
Samuel.

--
Intel Open Source Technology Centre
http://oss.intel.com/