Commit 7429b9623b9d636d6fc686db3af29cc79d901bc6
Committed by
Tom Warren
1 parent
651827c0fc
Exists in
v2017.01-smarct4x
and in
30 other branches
tegra: pwm: Add a driver for the tegra PWM
This PWM supports four channels. The driver always uses the 32KHz clock, and adjusts the duty cycle accordingly. Signed-off-by: Simon Glass <sjg@chromium.org> Signed-off-by: Tom Warren <twarren@nvidia.com>
Showing 3 changed files with 90 additions and 0 deletions Side-by-side Diff
arch/arm/include/asm/arch-tegra/pwm.h
... | ... | @@ -27,6 +27,7 @@ |
27 | 27 | #define PWM_DIVIDER_SHIFT 0 |
28 | 28 | #define PWM_DIVIDER_MASK (0x1FFF << PWM_DIVIDER_SHIFT) |
29 | 29 | |
30 | +#ifndef CONFIG_PWM | |
30 | 31 | /** |
31 | 32 | * Program the PWM with the given parameters. |
32 | 33 | * |
... | ... | @@ -56,6 +57,7 @@ |
56 | 57 | * @return 0 if ok, -1 if the device tree node was not found or invalid. |
57 | 58 | */ |
58 | 59 | int pwm_init(const void *blob); |
60 | +#endif | |
59 | 61 | |
60 | 62 | #endif /* __ASM_ARCH_TEGRA_PWM_H */ |
drivers/pwm/Makefile
drivers/pwm/tegra_pwm.c
1 | +/* | |
2 | + * Copyright 2016 Google Inc. | |
3 | + * | |
4 | + * SPDX-License-Identifier: GPL-2.0+ | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <dm.h> | |
9 | +#include <pwm.h> | |
10 | +#include <asm/io.h> | |
11 | +#include <asm/arch/clock.h> | |
12 | +#include <asm/arch/pwm.h> | |
13 | + | |
14 | +DECLARE_GLOBAL_DATA_PTR; | |
15 | + | |
16 | +struct tegra_pwm_priv { | |
17 | + struct pwm_ctlr *regs; | |
18 | +}; | |
19 | + | |
20 | +static int tegra_pwm_set_config(struct udevice *dev, uint channel, | |
21 | + uint period_ns, uint duty_ns) | |
22 | +{ | |
23 | + struct tegra_pwm_priv *priv = dev_get_priv(dev); | |
24 | + struct pwm_ctlr *regs = priv->regs; | |
25 | + uint pulse_width; | |
26 | + u32 reg; | |
27 | + | |
28 | + if (channel >= 4) | |
29 | + return -EINVAL; | |
30 | + debug("%s: Configure '%s' channel %u\n", __func__, dev->name, channel); | |
31 | + /* We ignore the period here and just use 32KHz */ | |
32 | + clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, 32768); | |
33 | + | |
34 | + pulse_width = duty_ns * 255 / period_ns; | |
35 | + | |
36 | + reg = pulse_width << PWM_WIDTH_SHIFT; | |
37 | + reg |= 1 << PWM_DIVIDER_SHIFT; | |
38 | + writel(reg, ®s[channel].control); | |
39 | + debug("%s: pulse_width=%u\n", __func__, pulse_width); | |
40 | + | |
41 | + return 0; | |
42 | +} | |
43 | + | |
44 | +static int tegra_pwm_set_enable(struct udevice *dev, uint channel, bool enable) | |
45 | +{ | |
46 | + struct tegra_pwm_priv *priv = dev_get_priv(dev); | |
47 | + struct pwm_ctlr *regs = priv->regs; | |
48 | + | |
49 | + if (channel >= 4) | |
50 | + return -EINVAL; | |
51 | + debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel); | |
52 | + clrsetbits_le32(®s[channel].control, PWM_ENABLE_MASK, | |
53 | + enable ? PWM_ENABLE_MASK : 0); | |
54 | + | |
55 | + return 0; | |
56 | +} | |
57 | + | |
58 | +static int tegra_pwm_ofdata_to_platdata(struct udevice *dev) | |
59 | +{ | |
60 | + struct tegra_pwm_priv *priv = dev_get_priv(dev); | |
61 | + | |
62 | + priv->regs = (struct pwm_ctlr *)dev_get_addr(dev); | |
63 | + | |
64 | + return 0; | |
65 | +} | |
66 | + | |
67 | +static const struct pwm_ops tegra_pwm_ops = { | |
68 | + .set_config = tegra_pwm_set_config, | |
69 | + .set_enable = tegra_pwm_set_enable, | |
70 | +}; | |
71 | + | |
72 | +static const struct udevice_id tegra_pwm_ids[] = { | |
73 | + { .compatible = "nvidia,tegra124-pwm" }, | |
74 | + { .compatible = "nvidia,tegra20-pwm" }, | |
75 | + { } | |
76 | +}; | |
77 | + | |
78 | +U_BOOT_DRIVER(tegra_pwm) = { | |
79 | + .name = "tegra_pwm", | |
80 | + .id = UCLASS_PWM, | |
81 | + .of_match = tegra_pwm_ids, | |
82 | + .ops = &tegra_pwm_ops, | |
83 | + .ofdata_to_platdata = tegra_pwm_ofdata_to_platdata, | |
84 | + .priv_auto_alloc_size = sizeof(struct tegra_pwm_priv), | |
85 | +}; |