From: Manikandan Pillai <mani.pillai@xxxxxx> Resending this patch with comments fixed : This patch moves the TPS6235x drivers from the drivers/i2c to drivers/ regulator to put the TPS6235x drivers under the power regulator framework suffix core_pwr and mpu_pwr has been removed I2C bus speeds replaced with numbers again Camel case has been removed TPS moved into regulator framework and drivers/i2c/chips left untouched. Not fixed comments: Not clear on how to implement runtime check for PR785 and would require some inputs on implementing the same. Signed-off-by: Manikandan Pillai <mani.pillai@xxxxxx> --- arch/arm/mach-omap2/board-omap3evm.c | 6 +- arch/arm/plat-omap/devices.c | 78 +++++ arch/arm/plat-omap/i2c.c | 21 +- drivers/i2c/busses/i2c-omap.c | 154 +++++++++- drivers/i2c/chips/Kconfig | 23 ++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/core.c | 27 -- drivers/regulator/tps6235x-regulator.c | 544 ++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 23 ++- 10 files changed, 848 insertions(+), 36 deletions(-) create mode 100644 drivers/regulator/tps6235x-regulator.c diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 22ac2e9..ab3070d 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -168,7 +168,7 @@ static struct i2c_board_info __initdata tps_6235x_i2c_board_info[] = { static int __init omap3_evm_i2c_init(void) { -#if defined(CONFIG_PR785) +#if defined(CONFIG_PR785) && defined(CONFIG_TPS6235X_I2C2) omap_register_i2c_bus(1, 2600, tps_6235x_i2c_board_info, ARRAY_SIZE(tps_6235x_i2c_board_info)); #endif @@ -178,6 +178,10 @@ static int __init omap3_evm_i2c_init(void) #endif omap_register_i2c_bus(2, 400, NULL, 0); omap_register_i2c_bus(3, 400, NULL, 0); +#if defined(CONFIG_PR785) && defined(CONFIG_TPS6235X_I2C4) + omap_register_i2c_bus(4, 2600, tps_6235x_i2c_board_info, + ARRAY_SIZE(tps_6235x_i2c_board_info)); +#endif return 0; } diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index 25c6d10..4c81eaf 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -27,6 +27,7 @@ #include <mach/gpio.h> #include <mach/dsp_common.h> #include <mach/mcbsp.h> +#include <linux/regulator/machine.h> #if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE) @@ -350,6 +351,83 @@ static void omap_init_rng(void) static inline void omap_init_rng(void) {} #endif +/*-------------------------------------------------------------------------*/ +#if defined(CONFIG_PR785) || defined(CONFIG_PR785_MODULE) + +static struct regulator_init_data tps_regulator_data[]; + +static struct resource tps6235x_resources[] = { + { + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap_tps6235x_device[] = { + { + .name = "tps6235x_reg", + .id = 2, + .num_resources = ARRAY_SIZE(tps6235x_resources), + .resource = tps6235x_resources, + .dev = { + .platform_data = &tps_regulator_data[0], + }, + }, + { + .name = "tps6235x_reg", + .id = 3, + .num_resources = ARRAY_SIZE(tps6235x_resources), + .resource = tps6235x_resources, + .dev = { + .platform_data = &tps_regulator_data[1], + }, + }, +}; + +static struct regulator_consumer_supply tps6235x_consumers[] = { + { + .dev = &omap_tps6235x_device[0].dev, + .supply = "tps62352", + }, + { + .dev = &omap_tps6235x_device[1].dev, + .supply = "tps62353", + }, +}; + +static struct regulator_init_data tps_regulator_data[] = { + { + .constraints = { + .min_uV = 750000, + .max_uV = 1537000, + .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS), + }, + .num_consumer_supplies = 1, + .consumer_supplies = &tps6235x_consumers[0], + }, + { + .constraints = { + .min_uV = 750000, + .max_uV = 1537000, + .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS), + }, + .num_consumer_supplies = 1, + .consumer_supplies = &tps6235x_consumers[1], + }, + +}; + +static void omap_init_pr785(void) +{ + (void) platform_device_register(&omap_tps6235x_device[0]); + (void) platform_device_register(&omap_tps6235x_device[1]); +} +#else +static inline void omap_init_pr785(void) {} +#endif +/*-------------------------------------------------------------------------*/ + /* * This gets called after board-specific INIT_MACHINE, and initializes most * on-chip peripherals accessible on this board (except for few like USB): diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c index 89a6ab0..e0c763a 100644 --- a/arch/arm/plat-omap/i2c.c +++ b/arch/arm/plat-omap/i2c.c @@ -35,6 +35,7 @@ #define OMAP2_I2C_BASE3 0x48060000 static const char name[] = "i2c_omap"; +static const char name4[] = "i2c4_omap"; #define I2C_RESOURCE_BUILDER(base, irq) \ { \ @@ -54,6 +55,7 @@ static struct resource i2c_resources[][2] = { #endif #if defined(CONFIG_ARCH_OMAP34XX) { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE3, INT_34XX_I2C3_IRQ) }, + { I2C_RESOURCE_BUILDER(0x0, 0x0) }, #endif }; @@ -68,6 +70,17 @@ static struct resource i2c_resources[][2] = { }, \ } +#define I2C_DEV_BUILDER4(bus_id, res, data) \ + { \ + .id = (bus_id), \ + .name = name4, \ + .num_resources = ARRAY_SIZE(res), \ + .resource = (res), \ + .dev = { \ + .platform_data = (data), \ + }, \ + } + static u32 i2c_rate[ARRAY_SIZE(i2c_resources)]; static struct platform_device omap_i2c_devices[] = { I2C_DEV_BUILDER(1, i2c_resources[0], &i2c_rate[0]), @@ -76,6 +89,7 @@ static struct platform_device omap_i2c_devices[] = { #endif #if defined(CONFIG_ARCH_OMAP34XX) I2C_DEV_BUILDER(3, i2c_resources[2], &i2c_rate[2]), + I2C_DEV_BUILDER4(4, i2c_resources[3], &i2c_rate[3]), #endif }; @@ -132,7 +146,8 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, else if (cpu_is_omap24xx()) ports = 2; else if (cpu_is_omap34xx()) - ports = 3; + /* There are 4 I2C controller on OMAP3 */ + ports = 4; BUG_ON(bus_id < 1 || bus_id > ports); @@ -141,7 +156,6 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, if (err) return err; } - pdev = &omap_i2c_devices[bus_id - 1]; *(u32 *)pdev->dev.platform_data = clkrate; @@ -159,6 +173,7 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, res[1].start = irq; } - omap_i2c_mux_pins(bus_id - 1); + if (bus_id != 4) + omap_i2c_mux_pins(bus_id - 1); return platform_device_register(pdev); } diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 96f3bed..bf8dd1e 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -157,6 +157,13 @@ #define SYSC_CLOCKACTIVITY_FCLK 0x2 +/* I2C4 Programming interface register address */ +#define OMAP_I2C4_PRM_VC_BYPASS_VAL (IO_ADDRESS(0x4830723C)) +#define OMAP_I2C4_PRM_SLAVE_ADDR_MSK (0x7F) +#define OMAP_I2C4_PRM_REG_SHIFT (8) +#define OMAP_I2C4_PRM_DATA_SHIFT (16) +#define OMAP_I2C4_PRM_VALID_MSK (1<<24) + struct omap_i2c_dev { struct device *dev; void __iomem *base; /* virtual */ @@ -524,7 +531,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, return -EIO; } - /* * Prepare controller for a transaction and call omap_i2c_xfer_msg * to do the work during IRQ processing. @@ -561,6 +567,70 @@ omap_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); } +/* + * I2C4 is a special I2C controller unlike I2C1, I2C2 and I2C3. + * Prepare controller for a transaction and write into appropriate + * registers for transferring data. Only writes are supported on I2C4. + */ +static int omap_i2c4_xfer_msg(struct i2c_adapter *adap, + struct i2c_msg *msg, int stop) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + unsigned int r; + unsigned char *cptr; + unsigned char regval, dataval; + + dev_dbg(dev->dev, "I2C4:addr: 0x%04x, len: %d,flags: 0x%x,stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + + if (msg->len == 0) + return -EINVAL; + + dev->buf_len = msg->len; + + cptr = msg->buf; + regval = *cptr; + cptr++; + dataval = *cptr; + + r = (((msg->addr) & OMAP_I2C4_PRM_SLAVE_ADDR_MSK) | + (regval << OMAP_I2C4_PRM_REG_SHIFT) | + (dataval << OMAP_I2C4_PRM_DATA_SHIFT) | + OMAP_I2C4_PRM_VALID_MSK); + + regval = *(volatile unsigned int *)OMAP_I2C4_PRM_VC_BYPASS_VAL; + + while (regval & OMAP_I2C4_PRM_VALID_MSK) + regval = *(volatile unsigned int *)OMAP_I2C4_PRM_VC_BYPASS_VAL; + + *(volatile unsigned int *)OMAP_I2C4_PRM_VC_BYPASS_VAL = r; + return 0; +} + +/* + * I2C4 is a special I2C controller unlike I2C1, I2C2 and I2C3. + * Prepare controller for a transaction and call omap_i2c4_xfer_msg + * to do the work during IRQ processing. + * Only writes are supported on I2C4. + */ +static int +omap_i2c4_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + int i; + int r; + + for (i = 0; i < num; i++) { + r = omap_i2c4_xfer_msg(adap, &msgs[i], (i == (num - 1))); + if (r != 0) + break; + } + + if (r == 0) + return num; + return r; +} + + static inline void omap_i2c_complete_cmd(struct omap_i2c_dev *dev, u16 err) { @@ -761,6 +831,11 @@ omap_i2c_isr(int this_irq, void *dev_id) return count ? IRQ_HANDLED : IRQ_NONE; } +static const struct i2c_algorithm omap_i2c4_algo = { + .master_xfer = omap_i2c4_xfer, + .functionality = omap_i2c_func, +}; + static const struct i2c_algorithm omap_i2c_algo = { .master_xfer = omap_i2c_xfer, .functionality = omap_i2c_func, @@ -839,8 +914,8 @@ omap_i2c_probe(struct platform_device *pdev) */ dev->fifo_size = (dev->fifo_size / 2); dev->b_hw = 1; /* Enable hardware fixes */ - } + } /* reset ASAP, clearing any IRQs */ omap_i2c_init(dev); @@ -856,13 +931,14 @@ omap_i2c_probe(struct platform_device *pdev) pdev->id, dev->rev >> 4, dev->rev & 0xf, dev->speed); omap_i2c_idle(dev); - adap = &dev->adapter; i2c_set_adapdata(adap, dev); adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON; strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + /* if BusId is 1-3 then algorithm is omap_i2c_algo */ adap->algo = &omap_i2c_algo; + adap->dev.parent = &pdev->dev; /* i2c device drivers may be active on return from add_adapter() */ @@ -911,6 +987,61 @@ omap_i2c_remove(struct platform_device *pdev) return 0; } +static int __init +omap_i2c4_probe(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev; + struct i2c_adapter *adap; + int r = 0; + u32 *speed = NULL; + + dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); + if (!dev) + r = -ENOMEM; + + if (pdev->dev.platform_data != NULL) + speed = (u32 *) pdev->dev.platform_data; + else + *speed = 100; /* Defualt speed */ + + dev->speed = *speed; + dev->idle = 1; + dev->dev = &pdev->dev; + + platform_set_drvdata(pdev, dev); + + r = omap_i2c_get_clocks(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + /* BusId is 4(I2C4) then algorithm is different */ + adap->algo = &omap_i2c4_algo; + + adap->dev.parent = &pdev->dev; + + /* i2c device drivers may be active on return from add_adapter() */ + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) + dev_err(dev->dev, "failure adding adapter\n"); + + return r; +} + +static int +omap_i2c4_remove(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + kfree(dev); + return 0; +} + + static struct platform_driver omap_i2c_driver = { .probe = omap_i2c_probe, .remove = omap_i2c_remove, @@ -920,17 +1051,32 @@ static struct platform_driver omap_i2c_driver = { }, }; +static struct platform_driver omap_i2c4_driver = { + .probe = omap_i2c4_probe, + .remove = omap_i2c4_remove, + .driver = { + .name = "i2c4_omap", + .owner = THIS_MODULE, + }, +}; + + /* I2C may be needed to bring up other drivers */ static int __init omap_i2c_init_driver(void) { - return platform_driver_register(&omap_i2c_driver); + int ret = 0; + ret = platform_driver_register(&omap_i2c_driver); + ret = platform_driver_register(&omap_i2c4_driver); + + return ret; } subsys_initcall(omap_i2c_init_driver); static void __exit omap_i2c_exit_driver(void) { platform_driver_unregister(&omap_i2c_driver); + platform_driver_unregister(&omap_i2c4_driver); } module_exit(omap_i2c_exit_driver); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index d61589b..5702520 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -194,6 +194,29 @@ config SENSORS_MAX6875 This driver can also be built as a module. If so, the module will be called max6875. +choice + prompt "TPS6235X driver support" + depends on I2C=y && PR785=y + default n + help + nothing. + + config TPS6235X_I2C2 + bool "TPS6235X_I2C2" + help + Say yes here if you have TPS6235x devices connected to I2C Bus2 + on PR785 Power module. Note that while selecting this option, + TPS6235X_I2C4 should not be selected. + + config TPS6235X_I2C4 + bool "TPS6235X_I2C4" + help + Say yes here if you have TPS6235x devices connected to I2C Bus4 + on PR785 Power module. Note that while selecting this option, + TPS6235X_I2C2 should not be selected. + +endchoice + config SENSORS_TSL2550 tristate "Taos TSL2550 ambient light sensor" depends on EXPERIMENTAL diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 0765389..6a827e5 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -80,4 +80,11 @@ config REGULATOR_DA903X Say y here to support the BUCKs and LDOs regulators found on Dialog Semiconductor DA9030/DA9034 PMIC. +config REGULATOR_TPS6235X + bool "TPS6235X Power regulator for OMAP3EVM" + depends on PR785 + 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. endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 0dacb18..fdc5f5b 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o +obj-$(CONFIG_REGULATOR_TPS6235X)+= tps6235x-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 02a7744..901f402 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -30,33 +30,6 @@ static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); /** - * struct regulator_dev - * - * Voltage / Current regulator class device. One for each regulator. - */ -struct regulator_dev { - struct regulator_desc *desc; - int use_count; - - /* lists we belong to */ - struct list_head list; /* list of all regulators */ - struct list_head slist; /* list of supplied regulators */ - - /* lists we own */ - struct list_head consumer_list; /* consumers we supply */ - struct list_head supply_list; /* regulators we supply */ - - struct blocking_notifier_head notifier; - struct mutex mutex; /* consumer lock */ - struct module *owner; - struct device dev; - struct regulation_constraints *constraints; - struct regulator_dev *supply; /* for tree */ - - void *reg_data; /* regulator_dev data */ -}; - -/** * struct regulator_map * * Used to provide symbolic supply names to devices. diff --git a/drivers/regulator/tps6235x-regulator.c b/drivers/regulator/tps6235x-regulator.c new file mode 100644 index 0000000..8a900db --- /dev/null +++ b/drivers/regulator/tps6235x-regulator.c @@ -0,0 +1,544 @@ +/* + * tps6235x-regulator.c -- support regulators in tps6235x family chips + * + * Copyright (C) 2008 David Brownell + * Author : Manikandan Pillai<mani.pillai@xxxxxx> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +/* Minimum and Maximum dc-dc voltage supported by the TPS6235x devices +All voltages given in millivolts */ +#define PR785_MIN_CORE_VOLT 750 +#define PR785_MAX_CORE_VOLT 1537 +#define PR785_MIN_MPU_VOLT 750 +#define PR785_MAX_MPU_VOLT 1537 + +/* Maximum number of bytes to be read in a single read */ +#define PR785_RETRY_COUNT 0x3 + +/* 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) + +/* + * 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. + */ +struct tps_6235x_info { + unsigned int state; + unsigned int tps_i2c_addr; + struct i2c_client *client; + struct device *i2c_dev; + /* platform data holder */ + void *pdata; +}; + +static struct tps_6235x_info tps_6235x_infodata[2]; + + +#ifndef REGULATOR_MODE_OFF +#define REGULATOR_MODE_OFF 0 +#endif + + +/* 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. + */ + +#define TPS6235X_REG_VSEL0 0 +#define TPS6235X_REG_VSEL1 1 +#define TPS6235X_REG_CTRL1 2 +#define TPS6235X_REG_CTRL2 3 +#define TPS6235X_REG_MAX TPS6235X_REG_CTRL2 + +#define MODULE_NAME "tps6235x_power" + +/* Debug functions */ +#ifdef DEBUG + +#define dump_reg(client, reg, val) \ + do { \ + tps6235x_read_reg(client, reg, &val); \ + dev_dbg(&(client)->dev, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ + } while (0) + +#endif /* #ifdef DEBUG */ + +/* Device addresses for PR785 card */ +#define PR785_62352_CORE_ADDR 0x4A +#define PR785_62353_MPU_ADDR 0x48 + +/* Minimum and Maximum dc-dc voltage supported by the TPS6235x devices +All voltages given in millivolts */ +#define PR785_MIN_CORE_VOLT 750 +#define PR785_MAX_CORE_VOLT 1537 +#define PR785_MIN_MPU_VOLT 750 +#define PR785_MAX_MPU_VOLT 1537 + +/* Maximum number of bytes to be read in a single read */ +#define PR785_RETRY_COUNT 0x3 + +/* 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) + +int pr785_enbl_dcdc(unsigned char tps_mod_type, unsigned int en_flag); +int pr785_get_dcdc_volt(unsigned char tps_mod_type, unsigned int *millivolts); +int pr785_set_dcdc_volt(unsigned char tps_mod_type, unsigned int millivolts); + +static int tps6235x_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct tps_6235x_info *tps_info = + (struct tps_6235x_info *)dev->reg_data; + return tps_info->state; +} + +static int tps6235x_dcdc_enable(struct regulator_dev *dev) +{ + struct tps_6235x_info *tps_info = + (struct tps_6235x_info *)dev->reg_data; + + tps_info->state = 1; + return pr785_enbl_dcdc(tps_info->tps_i2c_addr, 1); +} + +static int tps6235x_dcdc_disable(struct regulator_dev *dev) +{ + struct tps_6235x_info *tps_info = + (struct tps_6235x_info *)dev->reg_data; + + tps_info->state = 0; + return pr785_enbl_dcdc(tps_info->tps_i2c_addr, 0); +} + +static int tps6235x_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct tps_6235x_info *tps_info = + (struct tps_6235x_info *)dev->reg_data; + unsigned int millivolts; + + pr785_get_dcdc_volt(tps_info->tps_i2c_addr, &millivolts); + return millivolts * 1000; +} + +static int tps6235x_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct tps_6235x_info *tps_info = + (struct tps_6235x_info *)dev->reg_data; + unsigned int millivolts = max_uV / 1000; + + return pr785_set_dcdc_volt(tps_info->tps_i2c_addr, millivolts) ; +} + + +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, +}; + +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, +}; + + +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, + }, +}; + +static int tps6235xreg_probe(struct platform_device *pdev) +{ + struct tps_6235x_info *info; + struct regulator_dev *rdev = NULL; + + info = &tps_6235x_infodata[(pdev->id-2)]; + + rdev = regulator_register(®ulators[(pdev->id-2)], &pdev->dev, info); + if (rdev == NULL) + printk(KERN_ERR "err in regulator registry = %d\n", pdev->id); + + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int __devexit tps6235xreg_remove(struct platform_device *pdev) +{ + regulator_unregister(platform_get_drvdata(pdev)); + return 0; +} + +MODULE_ALIAS("platform:tps6235x_reg"); + +static struct platform_driver tps6235xreg_driver = { + .probe = tps6235xreg_probe, + .remove = __devexit_p(tps6235xreg_remove), + .driver.name = "tps6235x_reg", + .driver.owner = THIS_MODULE, +}; + +static int __init tps6235xreg_init(void) +{ + return platform_driver_register(&tps6235xreg_driver); +} +late_initcall(tps6235xreg_init); + +static void __exit tps6235xreg_exit(void) +{ + platform_driver_unregister(&tps6235xreg_driver); +} +module_exit(tps6235xreg_exit) + +MODULE_DESCRIPTION("TPS6235X regulator driver"); +MODULE_LICENSE("GPL"); + + +/* + * Get client pointer for a particular device + * Returns zero if successful, or non-zero otherwise. + */ +static struct i2c_client *tps_6235x_get_client(unsigned char tps_i2c_addr) +{ + if (tps_i2c_addr == PR785_62352_CORE_ADDR) + return tps_6235x_infodata[0].client; + else if (tps_i2c_addr == PR785_62353_MPU_ADDR) + return tps_6235x_infodata[1].client; + else + return NULL; +} + +/* + * Read a value from a register in an tps_6235x device. + * The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int tps_6235x_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + u8 data; + + if (!client->adapter) + return -ENODEV; + + data = i2c_smbus_read_byte_data(client, reg); + *val = data; + return 0; +} + +/* + * Write a value to a register in an tps_6235x device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tps_6235x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + int retry = 0; + + if (!client->adapter) + return -ENODEV; + +again: + err = i2c_smbus_write_byte_data(client, reg, val); + if (err >= 0) + return 0; + + dev_err(&client->dev, + "wrote 0x%.2x to offset 0x%.2x error %d\n", val, reg, err); + + if (retry <= PR785_RETRY_COUNT) { + dev_info(&client->dev, "retry ... %d\n", retry); + retry++; + schedule_timeout(msecs_to_jiffies(20)); + goto again; + } + return err; +} + + +/** +* pwr_i2c_read - Allows the caller to read one register from TPS device +* based on the address given. For the PR785 it reads +* only 1 byte into a specified register +* tps_mod_type - Enum for the device to be read +* reg - Register to be read from(value has to be between 0-3 +* val - value read from the reg +* Retval - 0 -> Success else non-zero +**/ +int pwr_i2c_read(unsigned char tps_mod_type, u8 reg, u8 *val) +{ + struct i2c_client *client; + + client = tps_6235x_get_client(tps_mod_type); + /* check if register is less than <= 3 Register is 0 -3 */ + if (reg > TPS6235X_REG_MAX) + return -1; + + return tps_6235x_read_reg(client, reg, val); +} +EXPORT_SYMBOL(pwr_i2c_read); + +/** +* pwr_i2c_write - Allows the caller to write one register from TPS device +* based on the address given. For the PR785 it writes +* only 1 byte into a specified register +* tps_mod_type - Enum for the device to be written +* reg - Register to be written to(value has to be between 0-3 +* val - value to be written to reg +* Retval - 0 -> Success else non-zero +**/ +int pwr_i2c_write(unsigned char tps_mod_type, u8 reg, u8 val) +{ + struct i2c_client *client; + + client = tps_6235x_get_client(tps_mod_type); + + /* check if register is less than <= 3 Register is 0 -3 */ + if (reg > TPS6235X_REG_MAX) + return -1; + + return tps_6235x_write_reg(client, reg, val); +} +EXPORT_SYMBOL(pwr_i2c_write); + + +/** +* TPSPR785 - Specific functions +* pr785_enbl_dcdc - Allows the caller to enable or disable the TPS6235x device +* on the PR785 board. The voltage for PR785 is selected by +* VSEL1 register since VSEL pin is kept high +* +* flag - 1 == enable 0 == disable +* Retval - 0 -> Success else non-zero +**/ +int pr785_enbl_dcdc(unsigned char tps_mod_type, unsigned int en_flag) +{ + unsigned char vsel1; + int ret; + + ret = pwr_i2c_read(tps_mod_type, TPS6235X_REG_VSEL1, &vsel1); + if (ret == 0) { + if (en_flag) + vsel1 |= TPS6235X_EN_DCDC; + else + vsel1 &= ~(TPS6235X_EN_DCDC); + ret = pwr_i2c_write(tps_mod_type, TPS6235X_REG_VSEL1, vsel1); + } + return ret; +} +EXPORT_SYMBOL(pr785_enbl_dcdc); + +/** +* TPSPR785 - Specific functions +* pr785_set_dcdc_volt - Allows the caller to set a particular voltage on +* for CORE or MPU +* +* voltage - voltage to be set in millivolts (75--1537) +* Retval - 0 -> Success else non-zero +**/ +int pr785_set_dcdc_volt(unsigned char tps_mod_type, unsigned int millivolts) +{ + unsigned char vsel1; + unsigned int volt; + + /* check if the millivolts is within range */ + if ((millivolts < PR785_MIN_CORE_VOLT) || + (millivolts > PR785_MAX_CORE_VOLT)) + return -1; + + /* Output voltage set is = min_op_volt + ( VSM * 12.5mv) */ + volt = millivolts - PR785_MIN_CORE_VOLT; + volt /= 25; + volt *= 2; + vsel1 = ((TPS6235X_EN_DCDC) | (volt & TPS6235X_VSM_MSK)); + return pwr_i2c_write(tps_mod_type, TPS6235X_REG_VSEL1, vsel1); +} +EXPORT_SYMBOL(pr785_set_dcdc_volt); + +/** +* TPSPR785 - Specific functions +* pr785_get_dcdc_volt - Allows the caller to get the set voltage on a +* particular TPS 6235x device on PR785 card +* +* voltage - voltage to be set in millivolts (75--1537) +* Retval - 0 -> Success else non-zero +**/ +int pr785_get_dcdc_volt(unsigned char tps_mod_type, unsigned int *millivolts) +{ + unsigned char vsel1; + unsigned int volt; + + /* Read the VSEL1 register to get VSM */ + pwr_i2c_read(tps_mod_type, 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) + PR785_MIN_CORE_VOLT; + *millivolts = volt; + return 0; +} +EXPORT_SYMBOL(pr785_get_dcdc_volt); + +/** + * tps_6235x_probe - TPS6235x driver i2c probe handler + * @client: i2c driver client device structure + * + * Register PR785 as an i2c client device driver + */ +static struct i2c_driver tps_6235x_i2c_driver; + +static +int tps_6235x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + unsigned char reg_val; + + printk(KERN_INFO "tps_6235x_probe:i2c_addr = %x\n", (int)client->addr); + + /* Device probed is TPS62352 CORE pwr chip if driver_data = 0 + Device probed is TPS62353 MPU pwr chip if driver_data = 1 */ + tps_6235x_infodata[id->driver_data].client = client; + tps_6235x_infodata[id->driver_data].tps_i2c_addr = client->addr; + tps_6235x_infodata[id->driver_data].state = 1; + tps_6235x_infodata[id->driver_data].i2c_dev = &client->dev; + + tps_6235x_read_reg(client, 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); + + i2c_set_clientdata(client, &tps_6235x_infodata[id->driver_data]); + + if (reg_val & TPS6235X_PWR_OK_MSK) + printk(KERN_INFO "Power is OK %x\n", reg_val); + else + printk(KERN_INFO "Power not within range = %x\n", reg_val); + return 0; +} + +/** + * tps_6235x_remove - TPS6235x driver i2c remove handler + * @client: i2c driver client device structure + * + * UnregisterPR785 as an i2c client device driver + */ +static int __exit tps_6235x_remove(struct i2c_client *client) +{ +#ifdef DEBUG + printk(KERN_INFO "tps_6235x_remove invoked\n"); +#endif + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + i2c_set_clientdata(client, NULL); + + return 0; +} + + +static const struct i2c_device_id tps_6235x_id[] = { + { "tps62352", 0}, + { "tps62353", 1}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tps_6235x_id); + +static struct i2c_driver tps_6235x_i2c_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, + .probe = tps_6235x_probe, + .remove = __exit_p(tps_6235x_remove), + .id_table = tps_6235x_id, +}; + +/** + * tps_6235x_init + * + * Module init function + */ +static int __init tps_6235x_init(void) +{ + int err; + +#ifdef DEBUG + printk(KERN_INFO "tps_6235x_init invoked\n"); +#endif + + err = i2c_add_driver(&tps_6235x_i2c_driver); + if (err) { + printk(KERN_ERR "Failed to register " MODULE_NAME ".\n"); + return err; + } else + printk(KERN_INFO "I2c driver registered\n"); + + return 0; +} + +/** + * tps_6235x_cleanup + * + * Module exit function + */ +static void __exit tps_6235x_cleanup(void) +{ +#ifdef DEBUG + printk(KERN_INFO "tps_6235x_cleanup invoked\n"); +#endif + 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 PR785 linux driver"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index e37d805..9107344 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -18,9 +18,30 @@ #include <linux/device.h> #include <linux/regulator/consumer.h> -struct regulator_dev; struct regulator_init_data; +struct regulator_dev { + struct regulator_desc *desc; + int use_count; + + /* lists we belong to */ + struct list_head list; /* list of all regulators */ + struct list_head slist; /* list of supplied regulators */ + + /* lists we own */ + struct list_head consumer_list; /* consumers we supply */ + struct list_head supply_list; /* regulators we supply */ + + struct blocking_notifier_head notifier; + struct mutex mutex; /* consumer lock */ + struct module *owner; + struct device dev; + struct regulation_constraints *constraints; + struct regulator_dev *supply; /* for tree */ + + void *reg_data; /* regulator_dev data */ +}; + /** * struct regulator_ops - regulator operations. * -- 1.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html