1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Qualcomm generic pmic driver
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 */
7#include <common.h>
8#include <dm.h>
9#include <power/pmic.h>
10#include <spmi/spmi.h>
11
12#define PID_SHIFT 8
13#define PID_MASK (0xFF << PID_SHIFT)
14#define REG_MASK 0xFF
15
16struct pmic_qcom_priv {
17	uint32_t usid; /* Slave ID on SPMI bus */
18};
19
20static int pmic_qcom_reg_count(struct udevice *dev)
21{
22	return 0xFFFF;
23}
24
25static int pmic_qcom_write(struct udevice *dev, uint reg, const uint8_t *buff,
26			   int len)
27{
28	struct pmic_qcom_priv *priv = dev_get_priv(dev);
29
30	if (len != 1)
31		return -EINVAL;
32
33	return spmi_reg_write(dev->parent, priv->usid,
34			      (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK,
35			      *buff);
36}
37
38static int pmic_qcom_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
39{
40	struct pmic_qcom_priv *priv = dev_get_priv(dev);
41	int val;
42
43	if (len != 1)
44		return -EINVAL;
45
46	val = spmi_reg_read(dev->parent, priv->usid,
47			    (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK);
48
49	if (val < 0)
50		return val;
51	*buff = val;
52	return 0;
53}
54
55static struct dm_pmic_ops pmic_qcom_ops = {
56	.reg_count = pmic_qcom_reg_count,
57	.read = pmic_qcom_read,
58	.write = pmic_qcom_write,
59};
60
61static const struct udevice_id pmic_qcom_ids[] = {
62	{ .compatible = "qcom,spmi-pmic" },
63	{ }
64};
65
66static int pmic_qcom_probe(struct udevice *dev)
67{
68	struct pmic_qcom_priv *priv = dev_get_priv(dev);
69	int ret;
70
71	/*
72	 * dev_read_addr() can't be used here because the reg property actually
73	 * contains two discrete values, not a single 64-bit address.
74	 * The address is the first value.
75	 */
76	ret = ofnode_read_u32_index(dev_ofnode(dev), "reg", 0, &priv->usid);
77	if (ret < 0)
78		return -EINVAL;
79
80	debug("usid: %d\n", priv->usid);
81
82	return 0;
83}
84
85U_BOOT_DRIVER(pmic_qcom) = {
86	.name = "pmic_qcom",
87	.id = UCLASS_PMIC,
88	.of_match = pmic_qcom_ids,
89	.bind = dm_scan_fdt_dev,
90	.probe = pmic_qcom_probe,
91	.ops = &pmic_qcom_ops,
92	.priv_auto	= sizeof(struct pmic_qcom_priv),
93};
94