/* * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MC34704_ONOFFA 0x8 #define MC34704_ONOFFC 0x4 #define MC34704_ONOFFD 0x2 #define MC34704_ONOFFE 0x1 /* Private data for MC34704 regulators */ struct reg_mc34704_priv { short enable; /* enable bit, if available */ short v_default; /* default regulator voltage in mV */ int dvs_min; /* minimum voltage change in units of 2.5% */ int dvs_max; /* maximum voltage change in units of 2.5% */ char i2c_dvs; /* i2c DVS register number */ char i2c_stat; /* i2c status register number */ }; struct reg_mc34704_priv mc34704_reg_priv[] = { { .v_default = REG1_V_MV, .dvs_min = REG1_DVS_MIN_PCT / 2.5, .dvs_max = REG1_DVS_MAX_PCT / 2.5, .i2c_dvs = 0x4, .i2c_stat = 0x5, .enable = MC34704_ONOFFA, }, { .v_default = REG2_V_MV, .dvs_min = REG2_DVS_MIN_PCT / 2.5, .dvs_max = REG2_DVS_MAX_PCT / 2.5, .i2c_dvs = 0x6, .i2c_stat = 0x7, }, { .v_default = REG3_V_MV, .dvs_min = REG3_DVS_MIN_PCT / 2.5, .dvs_max = REG3_DVS_MAX_PCT / 2.5, .i2c_dvs = 0x8, .i2c_stat = 0x9, }, { .v_default = REG4_V_MV, .dvs_min = REG4_DVS_MIN_PCT / 2.5, .dvs_max = REG4_DVS_MAX_PCT / 2.5, .i2c_dvs = 0xA, .i2c_stat = 0xB, }, { .v_default = REG5_V_MV, .dvs_min = REG5_DVS_MIN_PCT / 2.5, .dvs_max = REG5_DVS_MAX_PCT / 2.5, .i2c_dvs = 0xC, .i2c_stat = 0xE, .enable = MC34704_ONOFFE, }, }; static int mc34704_set_voltage(struct regulator_dev *reg, int MiniV, int uV) { struct reg_mc34704_priv *priv = rdev_get_drvdata(reg); int mV = uV / 1000; int dV = mV - priv->v_default; /* compute dynamic voltage scaling value */ int dvs = 1000 * dV / priv->v_default / 25; /* clip to regulator limits */ if (dvs > priv->dvs_max) dvs = priv->dvs_max; if (dvs < priv->dvs_min) dvs = priv->dvs_min; return pmic_write_reg(priv->i2c_dvs, dvs << 1, 0x1E); } static int mc34704_get_voltage(struct regulator_dev *reg) { int mV; struct reg_mc34704_priv *priv = rdev_get_drvdata(reg); int val, dvs; CHECK_ERROR(pmic_read_reg(priv->i2c_dvs, &val, 0xF)); dvs = (val >> 1) & 0xF; /* dvs is 4-bit 2's complement; sign-extend it */ if (dvs & 8) dvs |= -1 & ~0xF; /* Regulator voltage is adjusted by (dvs * 2.5%) */ mV = priv->v_default * (1000 + 25 * dvs) / 1000; return 1000 * mV; } static int mc34704_enable_reg(struct regulator_dev *reg) { struct reg_mc34704_priv *priv = rdev_get_drvdata(reg); if (priv->enable) return pmic_write_reg(REG_MC34704_GENERAL2, -1, priv->enable); return PMIC_ERROR; } static int mc34704_disable_reg(struct regulator_dev *reg) { struct reg_mc34704_priv *priv = rdev_get_drvdata(reg); if (priv->enable) return pmic_write_reg(REG_MC34704_GENERAL2, 0, priv->enable); return PMIC_ERROR; } static int mc34704_is_reg_enabled(struct regulator_dev *reg) { struct reg_mc34704_priv *priv = rdev_get_drvdata(reg); int val; if (priv->enable) { CHECK_ERROR(pmic_read_reg(REG_MC34704_GENERAL2, &val, priv->enable)); return val ? 1 : 0; } else { return PMIC_ERROR; } } static struct regulator_ops mc34704_full_ops = { .set_voltage = mc34704_set_voltage, .get_voltage = mc34704_get_voltage, .enable = mc34704_enable_reg, .disable = mc34704_disable_reg, .is_enabled = mc34704_is_reg_enabled, }; static struct regulator_ops mc34704_partial_ops = { .set_voltage = mc34704_set_voltage, .get_voltage = mc34704_get_voltage, }; static struct regulator_desc reg_mc34704[] = { { .name = "REG1_BKLT", .id = MC34704_BKLT, .ops = &mc34704_full_ops, .irq = 0, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE}, { .name = "REG2_CPU", .id = MC34704_CPU, .ops = &mc34704_partial_ops, .irq = 0, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE}, { .name = "REG3_CORE", .id = MC34704_CORE, .ops = &mc34704_partial_ops, .irq = 0, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE}, { .name = "REG4_DDR", .id = MC34704_DDR, .ops = &mc34704_partial_ops, .irq = 0, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE}, { .name = "REG5_PERS", .id = MC34704_PERS, .ops = &mc34704_full_ops, .irq = 0, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE}, }; static int mc34704_regulator_probe(struct platform_device *pdev) { struct regulator_dev *rdev; /* register regulator */ rdev = regulator_register(®_mc34704[pdev->id], &pdev->dev, pdev->dev.platform_data, (void *)&mc34704_reg_priv[pdev->id]); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s\n", reg_mc34704[pdev->id].name); return PTR_ERR(rdev); } return 0; } static int mc34704_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); regulator_unregister(rdev); return 0; } int mc34704_register_regulator(struct mc34704 *mc34704, int reg, struct regulator_init_data *initdata) { struct platform_device *pdev; int ret; if (mc34704->pmic.pdev[reg]) return -EBUSY; pdev = platform_device_alloc("mc34704-regulatr", reg); if (!pdev) return -ENOMEM; mc34704->pmic.pdev[reg] = pdev; initdata->driver_data = mc34704; pdev->dev.platform_data = initdata; pdev->dev.driver_data = &mc34704_reg_priv[reg]; pdev->dev.parent = mc34704->dev; platform_set_drvdata(pdev, mc34704); ret = platform_device_add(pdev); if (ret != 0) { dev_err(mc34704->dev, "Failed to register regulator %d: %d\n", reg, ret); platform_device_del(pdev); mc34704->pmic.pdev[reg] = NULL; } return ret; } EXPORT_SYMBOL_GPL(mc34704_register_regulator); static struct platform_driver mc34704_regulator_driver = { .probe = mc34704_regulator_probe, .remove = mc34704_regulator_remove, .driver = { .name = "mc34704-regulatr", }, }; static int __init mc34704_regulator_init(void) { return platform_driver_register(&mc34704_regulator_driver); } subsys_initcall(mc34704_regulator_init); static void __exit mc34704_regulator_exit(void) { platform_driver_unregister(&mc34704_regulator_driver); } module_exit(mc34704_regulator_exit); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MC34704 Regulator Driver"); MODULE_LICENSE("GPL");