mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-06 13:14:27 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
245 lines
6.2 KiB
C
245 lines
6.2 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) 2018 Theobroma Systems Design und Consulting GmbH
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <bitfield.h>
|
|
#include <errno.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#include <i2c.h>
|
|
#include <log.h>
|
|
#include <asm/gpio.h>
|
|
#include <linux/bitops.h>
|
|
#include <power/fan53555.h>
|
|
#include <power/pmic.h>
|
|
#include <power/regulator.h>
|
|
|
|
/**
|
|
* struct ic_types - definition of fan53555-family devices
|
|
*
|
|
* @die_id: Identifies the DIE_ID (lower nibble of the ID1 register)
|
|
* @die_rev: Identifies the DIE_REV (lower nibble of the ID2 register)
|
|
* @vsel_min: starting voltage (step 0) in uV
|
|
* @vsel_step: increment of the voltage in uV
|
|
*
|
|
* The voltage ramp (i.e. minimum voltage and step) is selected from the
|
|
* combination of 2 nibbles: DIE_ID and DIE_REV.
|
|
*
|
|
* See http://www.onsemi.com/pub/Collateral/FAN53555-D.pdf for details.
|
|
*/
|
|
static const struct {
|
|
unsigned int vendor;
|
|
u8 die_id;
|
|
u8 die_rev;
|
|
bool check_rev;
|
|
u32 vsel_min;
|
|
u32 vsel_step;
|
|
} ic_types[] = {
|
|
/* Option 00 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x0, 0x3, true, 600000, 10000 },
|
|
/* Option 13 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x0, 0xf, true, 800000, 10000 },
|
|
/* Option 23 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x0, 0xc, true, 600000, 12500 },
|
|
/* Option 01 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x1, 0x3, true, 600000, 10000 },
|
|
/* Option 03 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x3, 0x3, true, 600000, 10000 },
|
|
/* Option 04 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x4, 0xf, true, 603000, 12826 },
|
|
/* Option 05 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x5, 0x3, true, 600000, 10000 },
|
|
/* Option 08 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x8, 0x1, true, 600000, 10000 },
|
|
/* Option 08 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0x8, 0xf, true, 600000, 10000 },
|
|
/* Option 09 */
|
|
{ FAN53555_VENDOR_FAIRCHILD, 0xc, 0xf, true, 603000, 12826 },
|
|
/* SYL82X */
|
|
{ FAN53555_VENDOR_SILERGY, 0x8, 0x0, false, 712500, 12500 },
|
|
/* SYL83X */
|
|
{ FAN53555_VENDOR_SILERGY, 0x9, 0x0, false, 712500, 12500 },
|
|
};
|
|
|
|
/* I2C-accessible byte-sized registers */
|
|
enum {
|
|
/* Voltage setting */
|
|
FAN53555_VSEL0 = 0x00,
|
|
FAN53555_VSEL1,
|
|
/* Control register */
|
|
FAN53555_CONTROL,
|
|
/* IC Type */
|
|
FAN53555_ID1,
|
|
/* IC mask version */
|
|
FAN53555_ID2,
|
|
/* Monitor register */
|
|
FAN53555_MONITOR,
|
|
};
|
|
|
|
struct fan53555_platdata {
|
|
/* Voltage setting register */
|
|
unsigned int vol_reg;
|
|
unsigned int sleep_reg;
|
|
|
|
};
|
|
|
|
struct fan53555_priv {
|
|
/* IC Vendor */
|
|
unsigned int vendor;
|
|
/* IC Type and Rev */
|
|
unsigned int die_id;
|
|
unsigned int die_rev;
|
|
/* Voltage range and step(linear) */
|
|
unsigned int vsel_min;
|
|
unsigned int vsel_step;
|
|
/* Voltage slew rate limiting */
|
|
unsigned int slew_rate;
|
|
/* Sleep voltage cache */
|
|
unsigned int sleep_vol_cache;
|
|
};
|
|
|
|
static int fan53555_regulator_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct fan53555_platdata *dev_pdata = dev_get_platdata(dev);
|
|
struct dm_regulator_uclass_platdata *uc_pdata =
|
|
dev_get_uclass_platdata(dev);
|
|
u32 sleep_vsel;
|
|
|
|
/* This is a buck regulator */
|
|
uc_pdata->type = REGULATOR_TYPE_BUCK;
|
|
|
|
sleep_vsel = dev_read_u32_default(dev, "fcs,suspend-voltage-selector",
|
|
FAN53555_VSEL1);
|
|
|
|
/*
|
|
* Depending on the device-tree settings, the 'normal mode'
|
|
* voltage is either controlled by VSEL0 or VSEL1.
|
|
*/
|
|
switch (sleep_vsel) {
|
|
case FAN53555_VSEL0:
|
|
dev_pdata->sleep_reg = FAN53555_VSEL0;
|
|
dev_pdata->vol_reg = FAN53555_VSEL1;
|
|
break;
|
|
case FAN53555_VSEL1:
|
|
dev_pdata->sleep_reg = FAN53555_VSEL1;
|
|
dev_pdata->vol_reg = FAN53555_VSEL0;
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid vsel id %d\n", dev->name, sleep_vsel);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fan53555_regulator_get_value(struct udevice *dev)
|
|
{
|
|
struct fan53555_platdata *pdata = dev_get_platdata(dev);
|
|
struct fan53555_priv *priv = dev_get_priv(dev);
|
|
int reg;
|
|
int voltage;
|
|
|
|
/* We only support a single voltage selector (i.e. 'normal' mode). */
|
|
reg = pmic_reg_read(dev->parent, pdata->vol_reg);
|
|
if (reg < 0)
|
|
return reg;
|
|
voltage = priv->vsel_min + (reg & 0x3f) * priv->vsel_step;
|
|
|
|
debug("%s: %d uV\n", __func__, voltage);
|
|
return voltage;
|
|
}
|
|
|
|
static int fan53555_regulator_set_value(struct udevice *dev, int uV)
|
|
{
|
|
struct fan53555_platdata *pdata = dev_get_platdata(dev);
|
|
struct fan53555_priv *priv = dev_get_priv(dev);
|
|
u8 vol;
|
|
|
|
vol = (uV - priv->vsel_min) / priv->vsel_step;
|
|
debug("%s: uV=%d; writing volume %d: %02x\n",
|
|
__func__, uV, pdata->vol_reg, vol);
|
|
|
|
return pmic_clrsetbits(dev->parent, pdata->vol_reg, GENMASK(6, 0), vol);
|
|
}
|
|
|
|
static int fan53555_voltages_setup(struct udevice *dev)
|
|
{
|
|
struct fan53555_priv *priv = dev_get_priv(dev);
|
|
int i;
|
|
|
|
/* Init voltage range and step */
|
|
for (i = 0; i < ARRAY_SIZE(ic_types); ++i) {
|
|
if (ic_types[i].vendor != priv->vendor)
|
|
continue;
|
|
|
|
if (ic_types[i].die_id != priv->die_id)
|
|
continue;
|
|
|
|
if (ic_types[i].check_rev &&
|
|
ic_types[i].die_rev != priv->die_rev)
|
|
continue;
|
|
|
|
priv->vsel_min = ic_types[i].vsel_min;
|
|
priv->vsel_step = ic_types[i].vsel_step;
|
|
|
|
return 0;
|
|
}
|
|
|
|
pr_err("%s: %s: die id %d rev %d not supported!\n",
|
|
dev->name, __func__, priv->die_id, priv->die_rev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
enum {
|
|
DIE_ID_SHIFT = 0,
|
|
DIE_ID_WIDTH = 4,
|
|
DIE_REV_SHIFT = 0,
|
|
DIE_REV_WIDTH = 4,
|
|
};
|
|
|
|
static int fan53555_probe(struct udevice *dev)
|
|
{
|
|
struct fan53555_priv *priv = dev_get_priv(dev);
|
|
int ID1, ID2;
|
|
|
|
debug("%s\n", __func__);
|
|
|
|
/* read chip ID1 and ID2 (two registers, starting at ID1) */
|
|
ID1 = pmic_reg_read(dev->parent, FAN53555_ID1);
|
|
if (ID1 < 0)
|
|
return ID1;
|
|
|
|
ID2 = pmic_reg_read(dev->parent, FAN53555_ID2);
|
|
if (ID2 < 0)
|
|
return ID2;
|
|
|
|
/* extract vendor, die_id and die_rev */
|
|
priv->vendor = dev->driver_data;
|
|
priv->die_id = ID1 & GENMASK(3, 0);
|
|
priv->die_rev = ID2 & GENMASK(3, 0);
|
|
|
|
if (fan53555_voltages_setup(dev) < 0)
|
|
return -ENODATA;
|
|
|
|
debug("%s: FAN53555 option %d rev %d detected\n",
|
|
__func__, priv->die_id, priv->die_rev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_regulator_ops fan53555_regulator_ops = {
|
|
.get_value = fan53555_regulator_get_value,
|
|
.set_value = fan53555_regulator_set_value,
|
|
};
|
|
|
|
U_BOOT_DRIVER(fan53555_regulator) = {
|
|
.name = "fan53555_regulator",
|
|
.id = UCLASS_REGULATOR,
|
|
.ops = &fan53555_regulator_ops,
|
|
.ofdata_to_platdata = fan53555_regulator_ofdata_to_platdata,
|
|
.platdata_auto = sizeof(struct fan53555_platdata),
|
|
.priv_auto = sizeof(struct fan53555_priv),
|
|
.probe = fan53555_probe,
|
|
};
|