mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-18 10:48:51 +00:00
279 lines
6.5 KiB
C
279 lines
6.5 KiB
C
|
// SPDX-License-Identifier: GPL-2.0+
|
||
|
/*
|
||
|
* Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
|
||
|
* Copyright (C) 2021 Linaro
|
||
|
*/
|
||
|
|
||
|
#include <common.h>
|
||
|
#include <dm.h>
|
||
|
#include <errno.h>
|
||
|
#include <log.h>
|
||
|
#include <regmap.h>
|
||
|
#include <syscon.h>
|
||
|
#include <dm/device-internal.h>
|
||
|
#include <dm/device_compat.h>
|
||
|
#include <linux/bitops.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/ioport.h>
|
||
|
#include <power/pmic.h>
|
||
|
#include <power/regulator.h>
|
||
|
|
||
|
#define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */
|
||
|
#define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */
|
||
|
|
||
|
#define LDO_POWER_GATE 0x00
|
||
|
#define LDO_FET_FULL_ON 0x1f
|
||
|
|
||
|
#define BIT_WIDTH_MAX 32
|
||
|
|
||
|
#define ANATOP_REGULATOR_STEP 25000
|
||
|
#define MIN_DROPOUT_UV 125000
|
||
|
|
||
|
struct anatop_regulator {
|
||
|
const char *name;
|
||
|
struct regmap *regmap;
|
||
|
struct udevice *supply;
|
||
|
u32 control_reg;
|
||
|
u32 vol_bit_shift;
|
||
|
u32 vol_bit_width;
|
||
|
u32 min_bit_val;
|
||
|
u32 min_voltage;
|
||
|
u32 max_voltage;
|
||
|
u32 delay_reg;
|
||
|
u32 delay_bit_shift;
|
||
|
u32 delay_bit_width;
|
||
|
};
|
||
|
|
||
|
static u32 anatop_get_bits(struct udevice *dev, u32 addr, int bit_shift,
|
||
|
int bit_width)
|
||
|
{
|
||
|
const struct anatop_regulator *anatop_reg = dev_get_plat(dev);
|
||
|
int err;
|
||
|
u32 val, mask;
|
||
|
|
||
|
if (bit_width == BIT_WIDTH_MAX)
|
||
|
mask = ~0;
|
||
|
else
|
||
|
mask = (1 << bit_width) - 1;
|
||
|
|
||
|
err = regmap_read(anatop_reg->regmap, addr, &val);
|
||
|
if (err) {
|
||
|
dev_dbg(dev, "cannot read reg (%d)\n", err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
val = (val >> bit_shift) & mask;
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static int anatop_set_bits(struct udevice *dev, u32 addr, int bit_shift,
|
||
|
int bit_width, u32 data)
|
||
|
{
|
||
|
const struct anatop_regulator *anatop_reg = dev_get_plat(dev);
|
||
|
int err;
|
||
|
u32 val, mask;
|
||
|
|
||
|
if (bit_width == 32)
|
||
|
mask = ~0;
|
||
|
else
|
||
|
mask = (1 << bit_width) - 1;
|
||
|
|
||
|
err = regmap_read(anatop_reg->regmap, addr, &val);
|
||
|
if (err) {
|
||
|
dev_dbg(dev, "cannot read reg (%d)\n", err);
|
||
|
return err;
|
||
|
}
|
||
|
val = val & ~(mask << bit_shift);
|
||
|
err = regmap_write(anatop_reg->regmap,
|
||
|
addr, (data << bit_shift) | val);
|
||
|
if (err) {
|
||
|
dev_dbg(dev, "cannot write reg (%d)\n", err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int anatop_get_voltage(struct udevice *dev)
|
||
|
{
|
||
|
const struct anatop_regulator *anatop_reg = dev_get_plat(dev);
|
||
|
u32 sel;
|
||
|
u32 val;
|
||
|
|
||
|
if (!anatop_reg->control_reg)
|
||
|
return -ENOSYS;
|
||
|
|
||
|
val = anatop_get_bits(dev,
|
||
|
anatop_reg->control_reg,
|
||
|
anatop_reg->vol_bit_shift,
|
||
|
anatop_reg->vol_bit_width);
|
||
|
|
||
|
sel = val - anatop_reg->min_bit_val;
|
||
|
|
||
|
return sel * ANATOP_REGULATOR_STEP + anatop_reg->min_voltage;
|
||
|
}
|
||
|
|
||
|
static int anatop_set_voltage(struct udevice *dev, int uV)
|
||
|
{
|
||
|
const struct anatop_regulator *anatop_reg = dev_get_plat(dev);
|
||
|
u32 val;
|
||
|
u32 sel;
|
||
|
int ret;
|
||
|
|
||
|
dev_dbg(dev, "uv %d, min %d, max %d\n", uV, anatop_reg->min_voltage,
|
||
|
anatop_reg->max_voltage);
|
||
|
|
||
|
if (uV < anatop_reg->min_voltage)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (!anatop_reg->control_reg)
|
||
|
return -ENOSYS;
|
||
|
|
||
|
sel = DIV_ROUND_UP(uV - anatop_reg->min_voltage,
|
||
|
ANATOP_REGULATOR_STEP);
|
||
|
if (sel * ANATOP_REGULATOR_STEP + anatop_reg->min_voltage >
|
||
|
anatop_reg->max_voltage)
|
||
|
return -EINVAL;
|
||
|
val = anatop_reg->min_bit_val + sel;
|
||
|
dev_dbg(dev, "calculated val %d\n", val);
|
||
|
|
||
|
if (anatop_reg->supply) {
|
||
|
ret = regulator_set_value(anatop_reg->supply,
|
||
|
uV + MIN_DROPOUT_UV);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = anatop_set_bits(dev,
|
||
|
anatop_reg->control_reg,
|
||
|
anatop_reg->vol_bit_shift,
|
||
|
anatop_reg->vol_bit_width,
|
||
|
val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct dm_regulator_ops anatop_regulator_ops = {
|
||
|
.set_value = anatop_set_voltage,
|
||
|
.get_value = anatop_get_voltage,
|
||
|
};
|
||
|
|
||
|
static int anatop_regulator_probe(struct udevice *dev)
|
||
|
{
|
||
|
struct anatop_regulator *anatop_reg;
|
||
|
struct dm_regulator_uclass_plat *uc_pdata;
|
||
|
struct udevice *syscon;
|
||
|
int ret = 0;
|
||
|
u32 val;
|
||
|
|
||
|
anatop_reg = dev_get_plat(dev);
|
||
|
uc_pdata = dev_get_uclass_plat(dev);
|
||
|
|
||
|
anatop_reg->name = ofnode_read_string(dev_ofnode(dev),
|
||
|
"regulator-name");
|
||
|
if (!anatop_reg->name)
|
||
|
return log_msg_ret("regulator-name", -EINVAL);
|
||
|
|
||
|
ret = device_get_supply_regulator(dev, "vin-supply",
|
||
|
&anatop_reg->supply);
|
||
|
if (ret != -ENODEV) {
|
||
|
if (ret)
|
||
|
return log_msg_ret("get vin-supply", ret);
|
||
|
|
||
|
ret = regulator_set_enable(anatop_reg->supply, true);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = dev_read_u32(dev,
|
||
|
"anatop-reg-offset",
|
||
|
&anatop_reg->control_reg);
|
||
|
if (ret)
|
||
|
return log_msg_ret("anatop-reg-offset", ret);
|
||
|
|
||
|
ret = dev_read_u32(dev,
|
||
|
"anatop-vol-bit-width",
|
||
|
&anatop_reg->vol_bit_width);
|
||
|
if (ret)
|
||
|
return log_msg_ret("anatop-vol-bit-width", ret);
|
||
|
|
||
|
ret = dev_read_u32(dev,
|
||
|
"anatop-vol-bit-shift",
|
||
|
&anatop_reg->vol_bit_shift);
|
||
|
if (ret)
|
||
|
return log_msg_ret("anatop-vol-bit-shift", ret);
|
||
|
|
||
|
ret = dev_read_u32(dev,
|
||
|
"anatop-min-bit-val",
|
||
|
&anatop_reg->min_bit_val);
|
||
|
if (ret)
|
||
|
return log_msg_ret("anatop-min-bit-val", ret);
|
||
|
|
||
|
ret = dev_read_u32(dev,
|
||
|
"anatop-min-voltage",
|
||
|
&anatop_reg->min_voltage);
|
||
|
if (ret)
|
||
|
return log_msg_ret("anatop-min-voltage", ret);
|
||
|
|
||
|
ret = dev_read_u32(dev,
|
||
|
"anatop-max-voltage",
|
||
|
&anatop_reg->max_voltage);
|
||
|
if (ret)
|
||
|
return log_msg_ret("anatop-max-voltage", ret);
|
||
|
|
||
|
/* read LDO ramp up setting, only for core reg */
|
||
|
dev_read_u32(dev, "anatop-delay-reg-offset",
|
||
|
&anatop_reg->delay_reg);
|
||
|
dev_read_u32(dev, "anatop-delay-bit-width",
|
||
|
&anatop_reg->delay_bit_width);
|
||
|
dev_read_u32(dev, "anatop-delay-bit-shift",
|
||
|
&anatop_reg->delay_bit_shift);
|
||
|
|
||
|
syscon = dev_get_parent(dev);
|
||
|
if (!syscon) {
|
||
|
dev_dbg(dev, "unable to find syscon device\n");
|
||
|
return -ENOENT;
|
||
|
}
|
||
|
|
||
|
anatop_reg->regmap = syscon_get_regmap(syscon);
|
||
|
if (IS_ERR(anatop_reg->regmap)) {
|
||
|
dev_dbg(dev, "unable to find regmap (%ld)\n",
|
||
|
PTR_ERR(anatop_reg->regmap));
|
||
|
return -ENOENT;
|
||
|
}
|
||
|
|
||
|
/* check whether need to care about LDO ramp up speed */
|
||
|
if (anatop_reg->delay_bit_width) {
|
||
|
/*
|
||
|
* the delay for LDO ramp up time is
|
||
|
* based on the register setting, we need
|
||
|
* to calculate how many steps LDO need to
|
||
|
* ramp up, and how much delay needed. (us)
|
||
|
*/
|
||
|
val = anatop_get_bits(dev,
|
||
|
anatop_reg->delay_reg,
|
||
|
anatop_reg->delay_bit_shift,
|
||
|
anatop_reg->delay_bit_width);
|
||
|
uc_pdata->ramp_delay = (LDO_RAMP_UP_UNIT_IN_CYCLES << val)
|
||
|
/ LDO_RAMP_UP_FREQ_IN_MHZ + 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct udevice_id of_anatop_regulator_match_tbl[] = {
|
||
|
{ .compatible = "fsl,anatop-regulator", },
|
||
|
{ /* end */ }
|
||
|
};
|
||
|
|
||
|
U_BOOT_DRIVER(anatop_regulator) = {
|
||
|
.name = "anatop_regulator",
|
||
|
.id = UCLASS_REGULATOR,
|
||
|
.ops = &anatop_regulator_ops,
|
||
|
.of_match = of_anatop_regulator_match_tbl,
|
||
|
.plat_auto = sizeof(struct anatop_regulator),
|
||
|
.probe = anatop_regulator_probe,
|
||
|
};
|