/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only 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. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include #include #include "spm.h" #define FTSMPS_VCTRL_BAND_MASK 0xC0 #define FTSMPS_VCTRL_BAND_1 0x40 #define FTSMPS_VCTRL_BAND_2 0x80 #define FTSMPS_VCTRL_BAND_3 0xC0 #define FTSMPS_VCTRL_VPROG_MASK 0x3F #define FTSMPS_BAND1_UV_MIN 350000 #define FTSMPS_BAND1_UV_MAX 650000 /* 3 LSB's of program voltage must be 0 in band 1. */ /* Logical step size */ #define FTSMPS_BAND1_UV_LOG_STEP 50000 /* Physical step size */ #define FTSMPS_BAND1_UV_PHYS_STEP 6250 #define FTSMPS_BAND2_UV_MIN 700000 #define FTSMPS_BAND2_UV_MAX 1400000 #define FTSMPS_BAND2_UV_STEP 12500 #define FTSMPS_BAND3_UV_MIN 1400000 #define FTSMPS_BAND3_UV_SET_POINT_MIN 1500000 #define FTSMPS_BAND3_UV_MAX 3300000 #define FTSMPS_BAND3_UV_STEP 50000 struct saw_vreg { struct regulator_desc desc; struct regulator_dev *rdev; char *name; int uV; int last_set_uV; unsigned vlevel; bool online; }; /* Minimum core operating voltage */ #define MIN_CORE_VOLTAGE 950000 /* Specifies an uninitialized voltage */ #define INVALID_VOLTAGE -1 /* Specifies the PMIC internal slew rate in uV/us. */ #define REGULATOR_SLEW_RATE 1250 static int saw_get_voltage(struct regulator_dev *rdev) { struct saw_vreg *vreg = rdev_get_drvdata(rdev); return vreg->uV; } static int _set_voltage(struct regulator_dev *rdev) { struct saw_vreg *vreg = rdev_get_drvdata(rdev); int rc; rc = msm_spm_set_vdd(rdev_get_id(rdev), vreg->vlevel); if (!rc) { if (vreg->uV > vreg->last_set_uV) { /* Wait for voltage to stabalize. */ udelay((vreg->uV - vreg->last_set_uV) / REGULATOR_SLEW_RATE); } vreg->last_set_uV = vreg->uV; } else { pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->name, rc); vreg->uV = vreg->last_set_uV; } return rc; } static int saw_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct saw_vreg *vreg = rdev_get_drvdata(rdev); int uV = min_uV; u8 vprog, band; if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN) uV = FTSMPS_BAND1_UV_MIN; if (uV < FTSMPS_BAND1_UV_MIN || uV > FTSMPS_BAND3_UV_MAX) { pr_err("%s: request v=[%d, %d] is outside possible " "v=[%d, %d]\n", vreg->name, min_uV, max_uV, FTSMPS_BAND1_UV_MIN, FTSMPS_BAND3_UV_MAX); return -EINVAL; } /* Round up for set points in the gaps between bands. */ if (uV > FTSMPS_BAND1_UV_MAX && uV < FTSMPS_BAND2_UV_MIN) uV = FTSMPS_BAND2_UV_MIN; else if (uV > FTSMPS_BAND2_UV_MAX && uV < FTSMPS_BAND3_UV_SET_POINT_MIN) uV = FTSMPS_BAND3_UV_SET_POINT_MIN; if (uV > FTSMPS_BAND2_UV_MAX) { vprog = (uV - FTSMPS_BAND3_UV_MIN + FTSMPS_BAND3_UV_STEP - 1) / FTSMPS_BAND3_UV_STEP; band = FTSMPS_VCTRL_BAND_3; uV = FTSMPS_BAND3_UV_MIN + vprog * FTSMPS_BAND3_UV_STEP; } else if (uV > FTSMPS_BAND1_UV_MAX) { vprog = (uV - FTSMPS_BAND2_UV_MIN + FTSMPS_BAND2_UV_STEP - 1) / FTSMPS_BAND2_UV_STEP; band = FTSMPS_VCTRL_BAND_2; uV = FTSMPS_BAND2_UV_MIN + vprog * FTSMPS_BAND2_UV_STEP; } else { vprog = (uV - FTSMPS_BAND1_UV_MIN + FTSMPS_BAND1_UV_LOG_STEP - 1) / FTSMPS_BAND1_UV_LOG_STEP; uV = FTSMPS_BAND1_UV_MIN + vprog * FTSMPS_BAND1_UV_LOG_STEP; vprog *= FTSMPS_BAND1_UV_LOG_STEP / FTSMPS_BAND1_UV_PHYS_STEP; band = FTSMPS_VCTRL_BAND_1; } if (uV > max_uV) { pr_err("%s: request v=[%d, %d] cannot be met by any setpoint\n", vreg->name, min_uV, max_uV); return -EINVAL; } vreg->vlevel = band | vprog; vreg->uV = uV; if (!vreg->online) return 0; return _set_voltage(rdev); } static int saw_enable(struct regulator_dev *rdev) { struct saw_vreg *vreg = rdev_get_drvdata(rdev); int rc = 0; if (vreg->uV != vreg->last_set_uV) rc = _set_voltage(rdev); if (!rc) vreg->online = true; return rc; } static int saw_disable(struct regulator_dev *rdev) { struct saw_vreg *vreg = rdev_get_drvdata(rdev); vreg->online = false; return 0; } static int saw_is_enabled(struct regulator_dev *rdev) { struct saw_vreg *vreg = rdev_get_drvdata(rdev); return vreg->online; } static struct regulator_ops saw_ops = { .get_voltage = saw_get_voltage, .set_voltage = saw_set_voltage, .enable = saw_enable, .disable = saw_disable, .is_enabled = saw_is_enabled, }; static int __devinit saw_probe(struct platform_device *pdev) { struct regulator_init_data *init_data; struct saw_vreg *vreg; int rc = 0; if (!pdev->dev.platform_data) { pr_err("platform data required.\n"); return -EINVAL; } init_data = pdev->dev.platform_data; if (!init_data->constraints.name) { pr_err("regulator name must be specified in constraints.\n"); return -EINVAL; } vreg = kzalloc(sizeof(struct saw_vreg), GFP_KERNEL); if (!vreg) { pr_err("kzalloc failed.\n"); return -ENOMEM; } vreg->name = kstrdup(init_data->constraints.name, GFP_KERNEL); if (!vreg->name) { pr_err("kzalloc failed.\n"); rc = -ENOMEM; goto free_vreg; } vreg->desc.name = vreg->name; vreg->desc.id = pdev->id; vreg->desc.ops = &saw_ops; vreg->desc.type = REGULATOR_VOLTAGE; vreg->desc.owner = THIS_MODULE; vreg->uV = INVALID_VOLTAGE; vreg->last_set_uV = MIN_CORE_VOLTAGE; vreg->rdev = regulator_register(&vreg->desc, &pdev->dev, init_data, vreg, NULL); if (IS_ERR(vreg->rdev)) { rc = PTR_ERR(vreg->rdev); pr_err("regulator_register failed, rc=%d.\n", rc); goto free_name; } platform_set_drvdata(pdev, vreg); pr_info("id=%d, name=%s\n", pdev->id, vreg->name); return rc; free_name: kfree(vreg->name); free_vreg: kfree(vreg); return rc; } static int __devexit saw_remove(struct platform_device *pdev) { struct saw_vreg *vreg = platform_get_drvdata(pdev); regulator_unregister(vreg->rdev); kfree(vreg->name); kfree(vreg); platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver saw_driver = { .probe = saw_probe, .remove = __devexit_p(saw_remove), .driver = { .name = "saw-regulator", .owner = THIS_MODULE, }, }; static int __init saw_init(void) { return platform_driver_register(&saw_driver); } static void __exit saw_exit(void) { platform_driver_unregister(&saw_driver); } postcore_initcall(saw_init); module_exit(saw_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SAW regulator driver"); MODULE_ALIAS("platform:saw-regulator");