mirror of
https://github.com/torvalds/linux.git
synced 2024-11-13 23:51:39 +00:00
4d7071f1fd
Since the MFD core for this device and the regulator drivers for these devices can be built modular we should also support modular build of the shared code for the regulator drivers, otherwise we try to link built in code against modular code with unfortunate results. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
242 lines
7.3 KiB
C
242 lines
7.3 KiB
C
/*
|
|
* Regulator Driver for Freescale MC13xxx PMIC
|
|
*
|
|
* Copyright 2010 Yong Shen <yong.shen@linaro.org>
|
|
*
|
|
* Based on mc13783 regulator driver :
|
|
* Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
|
|
* Copyright 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Regs infos taken from mc13xxx drivers from freescale and mc13xxx.pdf file
|
|
* from freescale
|
|
*/
|
|
|
|
#include <linux/mfd/mc13xxx.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/err.h>
|
|
#include "mc13xxx.h"
|
|
|
|
static int mc13xxx_regulator_enable(struct regulator_dev *rdev)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int id = rdev_get_id(rdev);
|
|
int ret;
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
|
|
|
|
mc13xxx_lock(priv->mc13xxx);
|
|
ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg,
|
|
mc13xxx_regulators[id].enable_bit,
|
|
mc13xxx_regulators[id].enable_bit);
|
|
mc13xxx_unlock(priv->mc13xxx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mc13xxx_regulator_disable(struct regulator_dev *rdev)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int id = rdev_get_id(rdev);
|
|
int ret;
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
|
|
|
|
mc13xxx_lock(priv->mc13xxx);
|
|
ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg,
|
|
mc13xxx_regulators[id].enable_bit, 0);
|
|
mc13xxx_unlock(priv->mc13xxx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mc13xxx_regulator_is_enabled(struct regulator_dev *rdev)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int ret, id = rdev_get_id(rdev);
|
|
unsigned int val;
|
|
|
|
mc13xxx_lock(priv->mc13xxx);
|
|
ret = mc13xxx_reg_read(priv->mc13xxx, mc13xxx_regulators[id].reg, &val);
|
|
mc13xxx_unlock(priv->mc13xxx);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return (val & mc13xxx_regulators[id].enable_bit) != 0;
|
|
}
|
|
|
|
int mc13xxx_regulator_list_voltage(struct regulator_dev *rdev,
|
|
unsigned selector)
|
|
{
|
|
int id = rdev_get_id(rdev);
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
|
|
if (selector >= mc13xxx_regulators[id].desc.n_voltages)
|
|
return -EINVAL;
|
|
|
|
return mc13xxx_regulators[id].voltages[selector];
|
|
}
|
|
EXPORT_SYMBOL_GPL(mc13xxx_regulator_list_voltage);
|
|
|
|
int mc13xxx_get_best_voltage_index(struct regulator_dev *rdev,
|
|
int min_uV, int max_uV)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int reg_id = rdev_get_id(rdev);
|
|
int i;
|
|
int bestmatch;
|
|
int bestindex;
|
|
|
|
/*
|
|
* Locate the minimum voltage fitting the criteria on
|
|
* this regulator. The switchable voltages are not
|
|
* in strict falling order so we need to check them
|
|
* all for the best match.
|
|
*/
|
|
bestmatch = INT_MAX;
|
|
bestindex = -1;
|
|
for (i = 0; i < mc13xxx_regulators[reg_id].desc.n_voltages; i++) {
|
|
if (mc13xxx_regulators[reg_id].voltages[i] >= min_uV &&
|
|
mc13xxx_regulators[reg_id].voltages[i] < bestmatch) {
|
|
bestmatch = mc13xxx_regulators[reg_id].voltages[i];
|
|
bestindex = i;
|
|
}
|
|
}
|
|
|
|
if (bestindex < 0 || bestmatch > max_uV) {
|
|
dev_warn(&rdev->dev, "no possible value for %d<=x<=%d uV\n",
|
|
min_uV, max_uV);
|
|
return -EINVAL;
|
|
}
|
|
return bestindex;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mc13xxx_get_best_voltage_index);
|
|
|
|
static int mc13xxx_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
|
|
int max_uV, unsigned *selector)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int value, id = rdev_get_id(rdev);
|
|
int ret;
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
|
|
__func__, id, min_uV, max_uV);
|
|
|
|
/* Find the best index */
|
|
value = mc13xxx_get_best_voltage_index(rdev, min_uV, max_uV);
|
|
dev_dbg(rdev_get_dev(rdev), "%s best value: %d\n", __func__, value);
|
|
if (value < 0)
|
|
return value;
|
|
|
|
mc13xxx_lock(priv->mc13xxx);
|
|
ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].vsel_reg,
|
|
mc13xxx_regulators[id].vsel_mask,
|
|
value << mc13xxx_regulators[id].vsel_shift);
|
|
mc13xxx_unlock(priv->mc13xxx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mc13xxx_regulator_get_voltage(struct regulator_dev *rdev)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int ret, id = rdev_get_id(rdev);
|
|
unsigned int val;
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
|
|
|
|
mc13xxx_lock(priv->mc13xxx);
|
|
ret = mc13xxx_reg_read(priv->mc13xxx,
|
|
mc13xxx_regulators[id].vsel_reg, &val);
|
|
mc13xxx_unlock(priv->mc13xxx);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
val = (val & mc13xxx_regulators[id].vsel_mask)
|
|
>> mc13xxx_regulators[id].vsel_shift;
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
|
|
|
|
BUG_ON(val < 0 || val > mc13xxx_regulators[id].desc.n_voltages);
|
|
|
|
return mc13xxx_regulators[id].voltages[val];
|
|
}
|
|
|
|
struct regulator_ops mc13xxx_regulator_ops = {
|
|
.enable = mc13xxx_regulator_enable,
|
|
.disable = mc13xxx_regulator_disable,
|
|
.is_enabled = mc13xxx_regulator_is_enabled,
|
|
.list_voltage = mc13xxx_regulator_list_voltage,
|
|
.set_voltage = mc13xxx_regulator_set_voltage,
|
|
.get_voltage = mc13xxx_regulator_get_voltage,
|
|
};
|
|
EXPORT_SYMBOL_GPL(mc13xxx_regulator_ops);
|
|
|
|
int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
|
|
int max_uV, unsigned *selector)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int id = rdev_get_id(rdev);
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
|
|
__func__, id, min_uV, max_uV);
|
|
|
|
if (min_uV >= mc13xxx_regulators[id].voltages[0] &&
|
|
max_uV <= mc13xxx_regulators[id].voltages[0])
|
|
return 0;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage);
|
|
|
|
int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev)
|
|
{
|
|
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
|
|
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
|
|
int id = rdev_get_id(rdev);
|
|
|
|
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
|
|
|
|
return mc13xxx_regulators[id].voltages[0];
|
|
}
|
|
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_get_voltage);
|
|
|
|
struct regulator_ops mc13xxx_fixed_regulator_ops = {
|
|
.enable = mc13xxx_regulator_enable,
|
|
.disable = mc13xxx_regulator_disable,
|
|
.is_enabled = mc13xxx_regulator_is_enabled,
|
|
.list_voltage = mc13xxx_regulator_list_voltage,
|
|
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
|
|
.get_voltage = mc13xxx_fixed_regulator_get_voltage,
|
|
};
|
|
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops);
|
|
|
|
int mc13xxx_sw_regulator_is_enabled(struct regulator_dev *rdev)
|
|
{
|
|
return 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mc13xxx_sw_regulator_is_enabled);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Yong Shen <yong.shen@linaro.org>");
|
|
MODULE_DESCRIPTION("Regulator Driver for Freescale MC13xxx PMIC");
|
|
MODULE_ALIAS("mc13xxx-regulator-core");
|