From: David Brownell <david-b@pacbell.net>
To: Manikandan Pillai <mani.pillai@ti.com>
Cc: linux-omap@vger.kernel.org, broonie@sirena.org.uk
Subject: Re: [PATCH 1/2] Include TPS6235x based Power regulator support
Date: Tue, 13 Jan 2009 16:19:28 -0800 [thread overview]
Message-ID: <200901131619.28700.david-b@pacbell.net> (raw)
In-Reply-To: <1231832468-7132-1-git-send-email-mani.pillai@ti.com>
Feedback-in-the-form-of-a-patch ...
- Dave
Fives various problems with the latest tps6235x driver patch:
- Remove most board-specific comments, policy, and infrastructure
- Let it compile as a module; useful for built tests etc
- Support the other five values of "x", not just "2" and "3"
- Implement the missing register read/write operations
- Partial bugfix to is_enabled() ... fault handling is unclear
Initialization still looks iffy to me; it's making assumptions that
may not be correct in any given system. There's a comment saying how
to address that using the regulator_init_data.
---
drivers/regulator/Kconfig | 12 -
drivers/regulator/tps6235x-regulator.c | 353 ++++++++++++++++++-------------
2 files changed, 211 insertions(+), 154 deletions(-)
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -81,13 +81,11 @@ config REGULATOR_DA903X
Dialog Semiconductor DA9030/DA9034 PMIC.
config REGULATOR_TPS6235X
- bool "TPS6235X Power regulator for OMAP3EVM"
- depends on I2C=y
+ tristate "TI TPS6235x Power regulators"
+ depends on I2C
help
- This driver supports the voltage regulators provided by TPS6235x chips.
- The TPS62352 and TPS62353 are mounted on PR785 Power module card for
- providing voltage regulator functions. The PR785 Power board from
- Texas Instruments Inc is a TPS6235X based power card used with OMAP3
- EVM boards.
+ This driver supports TPS6235x voltage regulator chips, for values
+ of "x" from 0 to 6. These are buck converters which support TI's
+ hardware based "SmartReflex" dynamic voltage scaling.
endif
--- a/drivers/regulator/tps6235x-regulator.c
+++ b/drivers/regulator/tps6235x-regulator.c
@@ -19,39 +19,17 @@
#include <linux/i2c.h>
#include <linux/delay.h>
-int tps_6235x_read_reg(struct i2c_client *client, u8 reg, u8 *val);
-int tps_6235x_write_reg(struct i2c_client *client, u8 reg, u8 val);
-extern struct regulator_consumer_supply tps62352_core_consumers;
-extern struct regulator_consumer_supply tps62352_mpu_consumers;
-
-/* Minimum and Maximum dc-dc voltage supported by the TPS6235x devices
-All voltages given in millivolts */
-#define TPS62352_MIN_CORE_VOLT 750
-#define TPS62352_MAX_CORE_VOLT 1537
-#define TPS62353_MIN_MPU_VOLT 750
-#define TPS62353_MAX_MPU_VOLT 1537
-/* Register bit settings */
-#define TPS6235X_EN_DCDC (0x1 << 0x7)
-#define TPS6235X_VSM_MSK (0x3F)
-#define TPS6235X_EN_SYN_MSK (0x1 << 0x5)
-#define TPS6235X_SW_VOLT_MSK (0x1 << 0x4)
-#define TPS6235X_PWR_OK_MSK (0x1 << 0x5)
-#define TPS6235X_OUT_DIS_MSK (0x1 << 0x6)
-#define TPS6235X_GO_MSK (0x1 << 0x7)
-
-#define MODULE_NAME "tps_6235x_pwr"
/*
* These chips are often used in OMAP-based systems.
*
* This driver implements software-based resource control for various
* voltage regulators. This is usually augmented with state machine
* based control.
- */
-
-/* LDO control registers ... offset is from the base of its register bank.
- * The first three registers of all power resource banks help hardware to
- * manage the various resource groups.
+ *
+ * For now, all regulator operations apply to VSEL1 (the "ceiling"),
+ * instead of VSEL0 (the "floor") which is used for low power modes.
+ * Also, this *assumes* only software mode control is used...
*/
#define TPS6235X_REG_VSEL0 0
@@ -59,37 +37,74 @@ All voltages given in millivolts */
#define TPS6235X_REG_CTRL1 2
#define TPS6235X_REG_CTRL2 3
-/* Device addresses for TPS devices */
-#define TPS_62352_CORE_ADDR 0x4A
-#define TPS_62353_MPU_ADDR 0x48
+/* VSEL bitfields (EN_DCDC is shared) */
+#define TPS6235X_EN_DCDC BIT(7)
+#define TPS6235X_LIGHTPFM BIT(6)
+#define TPS6235X_VSM_MSK (0x3F)
-int omap_i2c_match_child(struct device *dev, void *data);
+/* CTRL1 bitfields */
+#define TPS6235X_EN_SYNC BIT(5)
+#define TPS6235X_HW_nSW BIT(4)
+/* REVISIT plus mode controls */
+
+/* CTRL2 bitfields */
+#define TPS6235X_PWR_OK_MSK BIT(5)
+#define TPS6235X_OUT_DIS_MSK BIT(6)
+#define TPS6235X_GO_MSK BIT(7)
+
+struct tps_info {
+ unsigned min_uV;
+ unsigned max_uV;
+ unsigned mult_uV;
+ bool fixed;
+};
+
+struct tps {
+ struct regulator_desc desc;
+ struct i2c_client *client;
+ struct regulator_dev *rdev;
+ const struct tps_info *info;
+};
+
+
+static inline int tps_6235x_read_reg(struct tps *tps, u8 reg, u8 *val)
+{
+ int status;
+
+ status = i2c_smbus_read_byte_data(tps->client, reg);
+ *val = status;
+ if (status < 0)
+ return status;
+ return 0;
+}
+
+static inline int tps_6235x_write_reg(struct tps *tps, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(tps->client, reg, val);
+}
static int tps6235x_dcdc_is_enabled(struct regulator_dev *dev)
{
unsigned char vsel1;
int ret;
- struct i2c_client *tps_info =
- rdev_get_drvdata(dev);
- ret = tps_6235x_read_reg(tps_info, TPS6235X_REG_VSEL1, &vsel1);
- ret &= TPS6235X_EN_DCDC;
- if (ret)
- return 1;
- else
- return 0;
+ struct tps *tps = rdev_get_drvdata(dev);
+
+ ret = tps_6235x_read_reg(tps, TPS6235X_REG_VSEL1, &vsel1);
+ /* REVISIT we need to be able to report errors here ... */
+
+ return !!(vsel1 & TPS6235X_EN_DCDC);
}
static int tps6235x_dcdc_enable(struct regulator_dev *dev)
{
unsigned char vsel1;
int ret;
+ struct tps *tps = rdev_get_drvdata(dev);
- struct i2c_client *client = rdev_get_drvdata(dev);
-
- ret = tps_6235x_read_reg(client, TPS6235X_REG_VSEL1, &vsel1);
+ ret = tps_6235x_read_reg(tps, TPS6235X_REG_VSEL1, &vsel1);
if (ret == 0) {
vsel1 |= TPS6235X_EN_DCDC;
- ret = tps_6235x_write_reg(client, TPS6235X_REG_VSEL1, vsel1);
+ ret = tps_6235x_write_reg(tps, TPS6235X_REG_VSEL1, vsel1);
}
return ret;
}
@@ -98,164 +113,216 @@ static int tps6235x_dcdc_disable(struct
{
unsigned char vsel1;
int ret;
- struct i2c_client *client = rdev_get_drvdata(dev);
+ struct tps *tps = rdev_get_drvdata(dev);
- ret = tps_6235x_read_reg(client, TPS6235X_REG_VSEL1, &vsel1);
+ ret = tps_6235x_read_reg(tps, TPS6235X_REG_VSEL1, &vsel1);
if (ret == 0) {
vsel1 &= ~(TPS6235X_EN_DCDC);
- ret = tps_6235x_write_reg(client, TPS6235X_REG_VSEL1, vsel1);
+ ret = tps_6235x_write_reg(tps, TPS6235X_REG_VSEL1, vsel1);
}
return ret;
}
static int tps6235x_dcdc_get_voltage(struct regulator_dev *dev)
{
- struct i2c_client *tps_info = rdev_get_drvdata(dev);
+ struct tps *tps = rdev_get_drvdata(dev);
+ const struct tps_info *info = tps->info;
+ int status;
unsigned char vsel1;
- unsigned int volt;
/* Read the VSEL1 register to get VSM */
- tps_6235x_read_reg(tps_info, TPS6235X_REG_VSEL1, &vsel1);
- /* Output voltage set is = min_op_volt + ( VSM * 12.5mv) */
- /* To cut out floating point operation we will multiply by 25
- divide by 2 */
- volt = (((vsel1 & TPS6235X_VSM_MSK) * 25) / 2) + TPS62352_MIN_CORE_VOLT;
+ status = tps_6235x_read_reg(tps, TPS6235X_REG_VSEL1, &vsel1);
+ if (status < 0)
+ return status;
- return volt * 1000;
+ return info->min_uV + ((vsel1 & TPS6235X_VSM_MSK) * info->mult_uV);
}
static int tps6235x_dcdc_set_voltage(struct regulator_dev *dev,
int min_uV, int max_uV)
{
+ struct tps *tps = rdev_get_drvdata(dev);
+ const struct tps_info *info = tps->info;
unsigned char vsel1;
- unsigned int volt;
- struct i2c_client *tps_info = rdev_get_drvdata(dev);
- unsigned int millivolts = min_uV / 1000;
+ unsigned step;
+ int status;
- /* check if the millivolts is within range */
- if ((millivolts < TPS62352_MIN_CORE_VOLT) ||
- (millivolts > TPS62352_MAX_CORE_VOLT))
+ /* adjust to match supported range, fail if out of range */
+ if (min_uV < info->min_uV)
+ min_uV = info->min_uV;
+ if (max_uV > info->max_uV)
+ max_uV = info->min_uV;
+ if (min_uV > max_uV)
return -EINVAL;
- /* Output voltage set is = min_op_volt + ( VSM * 12.5mv) */
- volt = millivolts - TPS62352_MIN_CORE_VOLT;
- volt /= 25;
- volt *= 2;
- vsel1 = ((TPS6235X_EN_DCDC) | (volt & TPS6235X_VSM_MSK));
- tps_6235x_write_reg(tps_info, TPS6235X_REG_VSEL1, vsel1);
- return 0;
+ /* compute and sanity-check voltage step multiplier */
+ step = DIV_ROUND_UP(min_uV - info->min_uV, info->mult_uV);
+ if ((info->min_uV + (step * info->mult_uV)) > max_uV)
+ return -EINVAL;
+
+ status = tps_6235x_read_reg(tps, TPS6235X_REG_VSEL1, &vsel1);
+ if (status < 0)
+ return status;
+
+ /* update voltage */
+ vsel1 &= ~TPS6235X_VSM_MSK;
+ vsel1 |= step;
+ return tps_6235x_write_reg(tps, TPS6235X_REG_VSEL1, vsel1);
}
-static struct regulator_ops tps62352_dcdc_ops = {
- .is_enabled = tps6235x_dcdc_is_enabled,
- .get_voltage = tps6235x_dcdc_get_voltage,
- .set_voltage = tps6235x_dcdc_set_voltage,
-};
+/* tps6345{0,2,4,5} have some parameters hard-wired */
+static struct regulator_ops tps6235x_fixed_dcdc_ops = {
+ .is_enabled = tps6235x_dcdc_is_enabled,
+ .get_voltage = tps6235x_dcdc_get_voltage,
+ .set_voltage = tps6235x_dcdc_set_voltage,
-static struct regulator_ops tps62353_dcdc_ops = {
- .is_enabled = tps6235x_dcdc_is_enabled,
- .enable = tps6235x_dcdc_enable,
- .disable = tps6235x_dcdc_disable,
- .get_voltage = tps6235x_dcdc_get_voltage,
- .set_voltage = tps6235x_dcdc_set_voltage,
+ /* REVISIT these can support regulator mode operations too */
};
-static struct regulator_desc regulators[] = {
- {
- .name = "tps62352",
- .id = 2,
- .ops = &tps62352_dcdc_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- },
- {
- .name = "tps62353",
- .id = 3,
- .ops = &tps62353_dcdc_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- },
-};
+/* tps6345{1,3,6} are more programmable */
+static struct regulator_ops tps6235x_dcdc_ops = {
+ .is_enabled = tps6235x_dcdc_is_enabled,
+ .enable = tps6235x_dcdc_enable,
+ .disable = tps6235x_dcdc_disable,
+ .get_voltage = tps6235x_dcdc_get_voltage,
+ .set_voltage = tps6235x_dcdc_set_voltage,
-static const char *regulator_consumer_name[] = {
- "vdd2",
- "vdd1",
+ /* REVISIT these can support regulator mode operations too */
};
static
int tps_6235x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
- struct regulator_dev *rdev = NULL;
+ static int desc_id;
+
+ const struct tps_info *info = (void *)id->driver_data;
+ struct regulator_init_data *init_data;
+ struct regulator_dev *rdev;
+ struct tps *tps;
unsigned char reg_val;
- struct device *dev_child = NULL;
- tps_6235x_read_reg(client, TPS6235X_REG_CTRL2, ®_val);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ init_data = client->dev.platform_data;
+ if (!init_data)
+ return -EIO;
+
+ tps = kzalloc(sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->desc.name = id->name;
+ tps->desc.id = desc_id++;
+ tps->desc.ops = info->fixed ? &tps6235x_fixed_dcdc_ops : &tps6235x_dcdc_ops;
+ tps->desc.type = REGULATOR_VOLTAGE;
+ tps->desc.owner = THIS_MODULE;
+
+ tps->client = client;
+ tps->info = info;
+
+ /* FIXME board init code should provide init_data->driver_data
+ * saying how to configure this regulator: how big is the
+ * inductor (affects light PFM mode optimization), slew rate,
+ * PLL multiplier, and so forth.
+ */
+ tps_6235x_read_reg(tps, TPS6235X_REG_CTRL2, ®_val);
reg_val |= (TPS6235X_OUT_DIS_MSK | TPS6235X_GO_MSK);
- tps_6235x_write_reg(client, TPS6235X_REG_CTRL2, reg_val);
- tps_6235x_read_reg(client, TPS6235X_REG_CTRL2, ®_val);
+ tps_6235x_write_reg(tps, TPS6235X_REG_CTRL2, reg_val);
+ tps_6235x_read_reg(tps, TPS6235X_REG_CTRL2, ®_val);
if (reg_val & TPS6235X_PWR_OK_MSK)
dev_dbg(&client->dev, "Power is OK %x\n", reg_val);
- else {
+ else
dev_dbg(&client->dev, "Power not in range = %x\n", reg_val);
- return -EIO;
- }
-
- /* Register the regulators */
- dev_child = device_find_child(client->adapter->dev.parent,
- (void *)regulator_consumer_name[id->driver_data],
- omap_i2c_match_child);
- rdev = regulator_register(®ulators[id->driver_data],
- dev_child, client);
+ /* Register the regulator */
+ rdev = regulator_register(&tps->desc, &client->dev, tps);
if (IS_ERR(rdev)) {
- dev_err(dev_child, "failed to register %s\n",
- regulator_consumer_name[id->driver_data]);
+ dev_err(&client->dev, "failed to register %s\n", id->name);
+ kfree(tps);
return PTR_ERR(rdev);
}
- /* Set the regulator platform data for unregistration later on */
- i2c_set_clientdata(client, rdev);
+ /* Save regulator for cleanup */
+ tps->rdev = rdev;
+ i2c_set_clientdata(client, tps);
return 0;
}
-/**
- * tps_6235x_remove - TPS6235x driver i2c remove handler
- * @client: i2c driver client device structure
- *
- * Unregister TPS driver as an i2c client device driver
- */
-static int __exit tps_6235x_remove(struct i2c_client *client)
+static int __devexit tps_6235x_remove(struct i2c_client *client)
{
- struct regulator_dev *rdev = NULL;
-
- if (!client->adapter)
- return -ENODEV; /* our client isn't attached */
+ struct tps *tps = i2c_get_clientdata(client);
- rdev = (struct regulator_dev *)i2c_get_clientdata(client);
- regulator_unregister(rdev);
- /* clear the client data in i2c */
+ regulator_unregister(tps->rdev);
+ kfree(tps);
i2c_set_clientdata(client, NULL);
-
return 0;
}
+/*
+ * These regulators have the same register structure, and differ
+ * primarily according to supported voltages and default settings.
+ */
+static const struct tps_info tps62350_info = {
+ .min_uV = 750000,
+ .max_uV = 1537500,
+ .mult_uV = 12500,
+ .fixed = true,
+};
+static const struct tps_info tps62351_info = {
+ .min_uV = 900000,
+ .max_uV = 1687500,
+ .mult_uV = 12500,
+};
+static const struct tps_info tps62352_info = {
+ .min_uV = 750000,
+ .max_uV = 1437500,
+ .mult_uV = 12500,
+ .fixed = true,
+};
+static const struct tps_info tps62353_info = {
+ .min_uV = 750000,
+ .max_uV = 1537500,
+ .mult_uV = 12500,
+};
+static const struct tps_info tps62354_info = {
+ .min_uV = 750000,
+ .max_uV = 1537500,
+ .mult_uV = 12500,
+ .fixed = true,
+};
+static const struct tps_info tps62355_info = {
+ .min_uV = 750000,
+ .max_uV = 1537500,
+ .mult_uV = 12500,
+ .fixed = true,
+};
+static const struct tps_info tps62356_info = {
+ .min_uV = 1500000,
+ .max_uV = 1975000,
+ .mult_uV = 25000,
+};
+
static const struct i2c_device_id tps_6235x_id[] = {
- { "tps62352", 0},
- { "tps62353", 1},
+ { "tps62350", (unsigned long) &tps62350_info, },
+ { "tps62351", (unsigned long) &tps62351_info, },
+ { "tps62352", (unsigned long) &tps62352_info, },
+ { "tps62353", (unsigned long) &tps62353_info, },
+ { "tps62354", (unsigned long) &tps62354_info, },
+ { "tps62355", (unsigned long) &tps62355_info, },
+ { "tps62356", (unsigned long) &tps62356_info, },
{},
};
-
MODULE_DEVICE_TABLE(i2c, tps_6235x_id);
static struct i2c_driver tps_6235x_i2c_driver = {
.driver = {
- .name = MODULE_NAME,
- .owner = THIS_MODULE,
+ .name = "tps_6235x_pwr"
},
.probe = tps_6235x_probe,
- .remove = __exit_p(tps_6235x_remove),
+ .remove = __devexit_p(tps_6235x_remove),
.id_table = tps_6235x_id,
};
@@ -266,15 +333,9 @@ static struct i2c_driver tps_6235x_i2c_d
*/
static int __init tps_6235x_init(void)
{
- int err;
-
- err = i2c_add_driver(&tps_6235x_i2c_driver);
- if (err) {
- printk(KERN_ERR "Failed to register " MODULE_NAME ".\n");
- return err;
- }
- return 0;
+ return i2c_add_driver(&tps_6235x_i2c_driver);
}
+subsys_initcall(tps_6235x_init);
/**
@@ -286,10 +347,8 @@ static void __exit tps_6235x_cleanup(voi
{
i2c_del_driver(&tps_6235x_i2c_driver);
}
-
-late_initcall(tps_6235x_init);
module_exit(tps_6235x_cleanup);
MODULE_AUTHOR("Texas Instruments");
-MODULE_DESCRIPTION("TPS6235x based linux driver");
+MODULE_DESCRIPTION("TPS6235x voltage regulator driver");
MODULE_LICENSE("GPL");
next prev parent reply other threads:[~2009-01-14 0:19 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-13 7:41 [PATCH 1/2] Include TPS6235x based Power regulator support Manikandan Pillai
2009-01-13 21:15 ` Mark Brown
2009-01-13 22:07 ` Mark Brown
2009-01-14 10:02 ` Pillai, Manikandan
2009-01-14 11:01 ` Mark Brown
2009-01-14 11:04 ` Pillai, Manikandan
2009-01-14 11:57 ` Mark Brown
2009-01-14 18:21 ` David Brownell
2009-01-14 18:16 ` David Brownell
2009-01-14 0:19 ` David Brownell [this message]
2009-01-14 10:39 ` Pillai, Manikandan
2009-01-14 11:48 ` Mark Brown
2009-01-14 17:55 ` David Brownell
2009-01-14 18:10 ` Mark Brown
2009-01-14 18:40 ` David Brownell
[not found] ` <200901201106.00914.david-b@pacbell.net>
2009-01-20 20:28 ` Mark Brown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200901131619.28700.david-b@pacbell.net \
--to=david-b@pacbell.net \
--cc=broonie@sirena.org.uk \
--cc=linux-omap@vger.kernel.org \
--cc=mani.pillai@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.