1// SPDX-License-Identifier: GPL-2.0+
2/*
3 *  Copyright (C) 2015 Samsung Electronics
4 *
5 *  Przemyslaw Marczak <p.marczak@samsung.com>
6 */
7
8#include <common.h>
9#include <clk.h>
10#include <errno.h>
11#include <dm.h>
12#include <linux/delay.h>
13#include <log.h>
14#include <asm/gpio.h>
15#include <power/pmic.h>
16#include <power/regulator.h>
17#include "regulator_common.h"
18
19#include "regulator_common.h"
20
21struct fixed_clock_regulator_plat {
22	struct clk *enable_clock;
23	unsigned int clk_enable_counter;
24};
25
26static int fixed_regulator_of_to_plat(struct udevice *dev)
27{
28	struct dm_regulator_uclass_plat *uc_pdata;
29	struct regulator_common_plat *plat;
30	bool gpios;
31
32	plat = dev_get_plat(dev);
33	uc_pdata = dev_get_uclass_plat(dev);
34	if (!uc_pdata)
35		return -ENXIO;
36
37	uc_pdata->type = REGULATOR_TYPE_FIXED;
38
39	gpios = dev_read_bool(dev, "gpios");
40	return regulator_common_of_to_plat(dev, plat, gpios ? "gpios" : "gpio");
41}
42
43static int fixed_regulator_get_value(struct udevice *dev)
44{
45	struct dm_regulator_uclass_plat *uc_pdata;
46
47	uc_pdata = dev_get_uclass_plat(dev);
48	if (!uc_pdata)
49		return -ENXIO;
50
51	if (uc_pdata->min_uV != uc_pdata->max_uV) {
52		debug("Invalid constraints for: %s\n", uc_pdata->name);
53		return -EINVAL;
54	}
55
56	return uc_pdata->min_uV;
57}
58
59static int fixed_regulator_get_current(struct udevice *dev)
60{
61	struct dm_regulator_uclass_plat *uc_pdata;
62
63	uc_pdata = dev_get_uclass_plat(dev);
64	if (!uc_pdata)
65		return -ENXIO;
66
67	if (uc_pdata->min_uA != uc_pdata->max_uA) {
68		debug("Invalid constraints for: %s\n", uc_pdata->name);
69		return -EINVAL;
70	}
71
72	return uc_pdata->min_uA;
73}
74
75static int fixed_regulator_get_enable(struct udevice *dev)
76{
77	return regulator_common_get_enable(dev, dev_get_plat(dev));
78}
79
80static int fixed_regulator_set_enable(struct udevice *dev, bool enable)
81{
82	return regulator_common_set_enable(dev, dev_get_plat(dev), enable);
83}
84
85static int fixed_clock_regulator_get_enable(struct udevice *dev)
86{
87	struct fixed_clock_regulator_plat *priv = dev_get_priv(dev);
88
89	return priv->clk_enable_counter > 0;
90}
91
92static int fixed_clock_regulator_set_enable(struct udevice *dev, bool enable)
93{
94	struct fixed_clock_regulator_plat *priv = dev_get_priv(dev);
95	struct regulator_common_plat *plat = dev_get_plat(dev);
96	int ret = 0;
97
98	if (enable) {
99		ret = clk_enable(priv->enable_clock);
100		priv->clk_enable_counter++;
101	} else {
102		ret = clk_disable(priv->enable_clock);
103		priv->clk_enable_counter--;
104	}
105	if (ret)
106		return ret;
107
108	if (enable && plat->startup_delay_us)
109		udelay(plat->startup_delay_us);
110
111	if (!enable && plat->off_on_delay_us)
112		udelay(plat->off_on_delay_us);
113
114	return ret;
115}
116
117static const struct dm_regulator_ops fixed_regulator_ops = {
118	.get_value	= fixed_regulator_get_value,
119	.get_current	= fixed_regulator_get_current,
120	.get_enable	= fixed_regulator_get_enable,
121	.set_enable	= fixed_regulator_set_enable,
122};
123
124static const struct dm_regulator_ops fixed_clock_regulator_ops = {
125	.get_enable	= fixed_clock_regulator_get_enable,
126	.set_enable	= fixed_clock_regulator_set_enable,
127};
128
129static const struct udevice_id fixed_regulator_ids[] = {
130	{ .compatible = "regulator-fixed" },
131	{ },
132};
133
134static const struct udevice_id fixed_clock_regulator_ids[] = {
135	{ .compatible = "regulator-fixed-clock" },
136	{ },
137};
138
139U_BOOT_DRIVER(regulator_fixed) = {
140	.name = "regulator_fixed",
141	.id = UCLASS_REGULATOR,
142	.ops = &fixed_regulator_ops,
143	.of_match = fixed_regulator_ids,
144	.of_to_plat = fixed_regulator_of_to_plat,
145	.plat_auto = sizeof(struct regulator_common_plat),
146};
147
148U_BOOT_DRIVER(regulator_fixed_clock) = {
149	.name = "regulator_fixed_clk",
150	.id = UCLASS_REGULATOR,
151	.ops = &fixed_clock_regulator_ops,
152	.of_match = fixed_clock_regulator_ids,
153	.of_to_plat = fixed_regulator_of_to_plat,
154	.plat_auto = sizeof(struct fixed_clock_regulator_plat),
155};
156