summaryrefslogtreecommitdiff
path: root/drivers/power/regulator/pmbus_generic.c
blob: 6ba50ce08f85e0d86b16509c61f8b6e43d5957c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2026 Free Mobile, Vincent Jardin
 *
 * Generic PMBus 1.x compatible voltage regulator driver.
 *
 * Catch all driver bound to compatible = "pmbus" for chips that have
 * no per chip driver under drivers/power/regulator/. The probe path
 * detects the VOUT numeric format from VOUT_MODE bits[7:5]:
 *
 *   - 0  LINEAR16 with the exponent supplied via VOUT_MODE bits[4:0]
 *   - 1  VID; mapped to pmbus_fmt_vid (decoder returns 0 today; per
 *        chip driver still required to plug a VID table)
 *   - 2  DIRECT; default coefficients m=1, b=0, R=0 (per chip
 *        coefficients arrive via PMBUS_QUERY / PMBUS_COEFFICIENTS,
 *        not yet consumed by U-Boot; values may need a per chip
 *        driver if telemetry numbers are wrong)
 *   - 3  IEEE754; mapped to pmbus_fmt_ieee754 (decoder returns 0
 *        today; per chip driver required)
 *
 * Other sensor classes (VIN, IIN, IOUT, TEMPERATURE) default to
 * LINEAR which is the spec baseline for compliant chips. If an
 * operator sees wrong telemetry numbers on this driver, the answer
 * is to write a per chip driver with the correct format[] / m / b / R.
 *
 * Adapted in spirit from linux/drivers/hwmon/pmbus/pmbus.c (the
 * kernel's generic probe driver). The U-Boot version drops the
 * page count auto detection (most generic compliant parts are
 * single rail; multi rail chips are quirky enough to need a per
 * chip driver) and the kernel hwmon publication layers.
 */

#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <pmbus.h>
#include <power/regulator.h>

#include "pmbus_helper.h"

struct pmbus_generic_priv {
	struct pmbus_regulator_priv base;	/* must be first */
	struct pmbus_driver_info info;
};

static int pmbus_generic_probe(struct udevice *dev)
{
	struct pmbus_generic_priv *gpriv = dev_get_priv(dev);
	struct pmbus_driver_info *info = &gpriv->info;
	enum pmbus_sensor_classes c;
	int ret;

	info->pages = 1;
	for (c = 0; c < PSC_NUM_CLASSES; c++) {
		info->format[c] = pmbus_fmt_linear;
		info->m[c] = 0;
		info->b[c] = 0;
		info->R[c] = 0;
	}

	ret = pmbus_regulator_probe_common(dev, info, 0);
	if (ret)
		return ret;

	/*
	 * Avoid reading non supported pages to avoid device's sticky
	 * status.
	 */
	info->pages = dev_read_u32_default(dev, "pmbus,num-pages", 1);
	if (info->pages < 1)
		info->pages = 1;

	pmbus_regulator_identify_vout(gpriv->base.i2c_dev, info);

	return 0;
}

static const struct udevice_id pmbus_generic_ids[] = {
	{ .compatible = "pmbus" },
	{ }
};

U_BOOT_DRIVER(pmbus_generic_regulator) = {
	.name      = "pmbus_generic_regulator",
	.id        = UCLASS_REGULATOR,
	.of_match  = pmbus_generic_ids,
	.probe     = pmbus_generic_probe,
	.ops       = &pmbus_regulator_ops,
	.priv_auto = sizeof(struct pmbus_generic_priv),
};