/* * pwm-regulator: PWM based regulator configuration. * * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. * * Author: Laxman Dewangan * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct pwm_regulator { struct device *dev; struct regulator_desc desc; struct regulator_dev *rdev; struct pwm_device *pwm; struct regulator_init_data *rinit_data; unsigned int period; unsigned int pulse_time; unsigned int min_uV; unsigned int max_uV; unsigned int uV_step; unsigned int n_voltages; unsigned int curr_selector; int enable_gpio; int idle_gpio; int standby_gpio; unsigned int voltage_time_sel; }; static int pwm_regulator_set_voltage_sel( struct regulator_dev *rdev, unsigned selector) { struct pwm_regulator *preg = rdev_get_drvdata(rdev); int ret; int duty_cycle; duty_cycle = selector * preg->pulse_time; ret = pwm_config(preg->pwm, duty_cycle, preg->period); if (ret < 0) { dev_err(preg->dev, "pwm config failed: %d\n", ret); return ret; } if (duty_cycle) { ret = pwm_enable(preg->pwm); if (ret < 0) { dev_err(preg->dev, "pwm enable failed: %d\n", ret); return ret; } } else { pwm_disable(preg->pwm); } preg->curr_selector = selector; return ret; } static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev) { struct pwm_regulator *preg = rdev_get_drvdata(rdev); return preg->curr_selector; } static int pwm_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct pwm_regulator *preg = rdev_get_drvdata(rdev); if (!gpio_is_valid(preg->idle_gpio)) return -EINVAL; switch (mode) { case REGULATOR_MODE_IDLE: gpio_set_value(preg->idle_gpio, 0); break; case REGULATOR_MODE_NORMAL: gpio_set_value(preg->idle_gpio, 1); break; default: return -EINVAL; } return 0; } static unsigned int pwm_regulator_get_mode(struct regulator_dev *rdev) { struct pwm_regulator *preg = rdev_get_drvdata(rdev); if (!gpio_is_valid(preg->idle_gpio)) return 0; if (gpio_get_value(preg->idle_gpio)) return REGULATOR_MODE_NORMAL; else return REGULATOR_MODE_IDLE; } static int pwm_regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { struct pwm_regulator *preg = rdev_get_drvdata(rdev); if (preg->voltage_time_sel) return preg->voltage_time_sel; return regulator_set_voltage_time_sel(rdev, old_selector, new_selector); } static struct regulator_ops pwm_regulator_ops = { .set_voltage_sel = pwm_regulator_set_voltage_sel, .get_voltage_sel = pwm_regulator_get_voltage_sel, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .set_voltage_time_sel = pwm_regulator_set_voltage_time_sel, .set_mode = pwm_regulator_set_mode, .get_mode = pwm_regulator_get_mode, }; static int pwm_regulator_parse_dt(struct device *dev, struct pwm_regulator *preg) { struct device_node *node = dev->of_node; u32 pval; int ret; preg->rinit_data = of_get_regulator_init_data(dev, dev->of_node); if (!preg->rinit_data) { dev_err(dev, "Not able to get OF regulator init data\n"); return -EINVAL; } ret = of_property_read_u32(node, "regulator-n-voltages", &pval); if (!ret) { preg->n_voltages = pval; } else { dev_err(dev, "Number of voltages is missing\n"); return ret; } ret = of_property_read_u32(node, "voltage-time-sel", &pval); if (!ret) preg->voltage_time_sel = pval; preg->enable_gpio = of_get_named_gpio(node, "enable-gpio", 0); if ((preg->enable_gpio < 0) && (preg->enable_gpio != -ENOENT)) { dev_err(dev, "Enable gpio not available, %d\n", preg->enable_gpio); return preg->enable_gpio; } preg->idle_gpio = of_get_named_gpio(node, "idle-gpio", 0); if ((preg->idle_gpio < 0) && (preg->idle_gpio != -ENOENT)) { dev_err(dev, "Idle gpio not available, %d\n", preg->idle_gpio); return preg->idle_gpio; } preg->standby_gpio = of_get_named_gpio(node, "standby-gpio", 0); if ((preg->standby_gpio < 0) && (preg->standby_gpio != -ENOENT)) { dev_err(dev, "Standby gpio not available, %d\n", preg->standby_gpio); return preg->standby_gpio; } preg->min_uV = preg->rinit_data->constraints.min_uV; preg->max_uV = preg->rinit_data->constraints.max_uV; return 0; } static int pwm_regulator_verify_patform_data(struct pwm_regulator *preg) { struct device *dev = preg->dev; preg->period = pwm_get_period(preg->pwm); if (!preg->period) return -EINVAL; if (preg->n_voltages < 2) { dev_err(dev, "Number of volatges is not correct\n"); return -EINVAL; } if (preg->period % (preg->n_voltages - 1)) { dev_err(dev, "PWM Period must multiple of n_voltages\n"); return -EINVAL; } preg->pulse_time = preg->period / (preg->n_voltages - 1); if (!preg->pulse_time) { dev_err(dev, "Pulse time is invalid\n"); return -EINVAL; } if ((preg->max_uV - preg->min_uV) % (preg->n_voltages - 1)) { dev_err(dev, "Min/Max is not proper to get step voltage\n"); return -EINVAL; } preg->uV_step = (preg->max_uV - preg->min_uV) / (preg->n_voltages - 1); return 0; } static int pwm_regulator_probe(struct platform_device *pdev) { struct pwm_regulator *preg; struct regulator_config config = { }; int ret; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "Not DT registartion\n"); return -ENODEV; } preg = devm_kzalloc(&pdev->dev, sizeof(*preg), GFP_KERNEL); if (!preg) { dev_err(&pdev->dev, "Memory allocation failed\n"); return -ENOMEM; } preg->dev = &pdev->dev; ret = pwm_regulator_parse_dt(&pdev->dev, preg); if (ret < 0) { dev_err(&pdev->dev, "DT parsing is failed: %d\n", ret); return ret; } preg->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(preg->pwm)) { ret = PTR_ERR(preg->pwm); if (ret == -EPROBE_DEFER) dev_info(&pdev->dev, "PWM request deferred\n"); else dev_err(&pdev->dev, "PWM request failed: %d\n", ret); return ret; } ret = pwm_regulator_verify_patform_data(preg); if (ret < 0) { dev_err(&pdev->dev, "PWM regulator param verification failed: %d\n", ret); return ret; } if (gpio_is_valid(preg->idle_gpio)) { ret = devm_gpio_request_one(&pdev->dev, preg->idle_gpio, GPIOF_OUT_INIT_HIGH, "pwm-reg-idle-gpio"); if (ret < 0) { dev_err(&pdev->dev, "Idle gpio request failed\n"); return ret; } preg->rinit_data->constraints.valid_modes_mask |= REGULATOR_MODE_IDLE | REGULATOR_MODE_NORMAL; preg->rinit_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE; } preg->desc.name = "regulator-pwm"; preg->desc.id = -1; preg->desc.ops = &pwm_regulator_ops; preg->desc.type = REGULATOR_VOLTAGE; preg->desc.owner = THIS_MODULE; preg->desc.min_uV = preg->min_uV; preg->desc.uV_step = preg->uV_step; preg->desc.linear_min_sel = 0; preg->desc.n_voltages = preg->n_voltages; if (gpio_is_valid(preg->enable_gpio)) { config.ena_gpio = preg->enable_gpio; if (preg->rinit_data->constraints.always_on || preg->rinit_data->constraints.boot_on) config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; } config.dev = &pdev->dev; config.init_data = preg->rinit_data; config.driver_data = preg; preg->rdev = devm_regulator_register(&pdev->dev, &preg->desc, &config); if (IS_ERR(preg->rdev)) { dev_err(preg->dev, "failed to register regulator %s\n", preg->desc.name); return PTR_ERR(preg->rdev); } platform_set_drvdata(pdev, preg); return 0; } static const struct of_device_id pwm_regulator_of_match[] = { { .compatible = "regulator-pwm", }, {}, }; MODULE_DEVICE_TABLE(of, pwm_regulator_of_match); static struct platform_driver pwm_regulator_driver = { .driver = { .name = "regulator-pwm", .owner = THIS_MODULE, .of_match_table = pwm_regulator_of_match, }, .probe = pwm_regulator_probe, }; static int __init pwm_regulator_init(void) { return platform_driver_register(&pwm_regulator_driver); } subsys_initcall_sync(pwm_regulator_init); static void __exit pwm_regulator_exit(void) { platform_driver_unregister(&pwm_regulator_driver); } module_exit(pwm_regulator_exit); MODULE_DESCRIPTION("PWM based regulator Driver"); MODULE_ALIAS("platform:regulator-pwm"); MODULE_AUTHOR("Laxman Dewangan "); MODULE_LICENSE("GPL v2");