1// SPDX-License-Identifier: GPL-2.0+
2
3#include <cros_ec.h>
4#include <dm.h>
5#include <errno.h>
6#include <log.h>
7#include <pwm.h>
8
9struct cros_ec_pwm_priv {
10	bool enabled;
11	uint duty;
12};
13
14static int cros_ec_pwm_set_config(struct udevice *dev, uint channel,
15				  uint period_ns, uint duty_ns)
16{
17	struct cros_ec_pwm_priv *priv = dev_get_priv(dev);
18	uint duty;
19	int ret;
20
21	debug("%s: period_ns=%u, duty_ns=%u asked\n", __func__,
22	      period_ns, duty_ns);
23
24	/* No way to set the period, only a relative duty cycle */
25	duty = EC_PWM_MAX_DUTY * duty_ns / period_ns;
26	if (duty > EC_PWM_MAX_DUTY)
27		duty = EC_PWM_MAX_DUTY;
28
29	if (!priv->enabled) {
30		priv->duty = duty;
31		debug("%s: duty=%#x to-be-set\n", __func__, duty);
32		return 0;
33	}
34
35	ret = cros_ec_set_pwm_duty(dev->parent, channel, duty);
36	if (ret) {
37		debug("%s: duty=%#x failed\n", __func__, duty);
38		return ret;
39	}
40
41	priv->duty = duty;
42	debug("%s: duty=%#x set\n", __func__, duty);
43
44	return 0;
45}
46
47static int cros_ec_pwm_set_enable(struct udevice *dev, uint channel,
48				  bool enable)
49{
50	struct cros_ec_pwm_priv *priv = dev_get_priv(dev);
51	int ret;
52
53	ret = cros_ec_set_pwm_duty(dev->parent, channel,
54				   enable ? priv->duty : 0);
55	if (ret) {
56		debug("%s: enable=%d failed\n", __func__, enable);
57		return ret;
58	}
59
60	priv->enabled = enable;
61	debug("%s: enable=%d (duty=%#x) set\n", __func__,
62	      enable, priv->duty);
63
64	return 0;
65}
66
67static const struct pwm_ops cros_ec_pwm_ops = {
68	.set_config	= cros_ec_pwm_set_config,
69	.set_enable	= cros_ec_pwm_set_enable,
70};
71
72static const struct udevice_id cros_ec_pwm_ids[] = {
73	{ .compatible = "google,cros-ec-pwm" },
74	{ }
75};
76
77U_BOOT_DRIVER(cros_ec_pwm) = {
78	.name	= "cros_ec_pwm",
79	.id	= UCLASS_PWM,
80	.of_match = cros_ec_pwm_ids,
81	.ops	= &cros_ec_pwm_ops,
82	.priv_auto = sizeof(struct cros_ec_pwm_priv),
83};
84