1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
25c2dd4cdSSimon Glass /*
35c2dd4cdSSimon Glass * Copyright 2016 Google Inc.
45c2dd4cdSSimon Glass */
55c2dd4cdSSimon Glass
65c2dd4cdSSimon Glass #include <common.h>
75c2dd4cdSSimon Glass #include <dm.h>
85c2dd4cdSSimon Glass #include <pwm.h>
95c2dd4cdSSimon Glass #include <asm/io.h>
105c2dd4cdSSimon Glass #include <asm/arch/clk.h>
115c2dd4cdSSimon Glass #include <asm/arch/clock.h>
125c2dd4cdSSimon Glass #include <asm/arch/pwm.h>
135c2dd4cdSSimon Glass
145c2dd4cdSSimon Glass struct exynos_pwm_priv {
155c2dd4cdSSimon Glass struct s5p_timer *regs;
165c2dd4cdSSimon Glass };
175c2dd4cdSSimon Glass
exynos_pwm_set_config(struct udevice * dev,uint channel,uint period_ns,uint duty_ns)185c2dd4cdSSimon Glass static int exynos_pwm_set_config(struct udevice *dev, uint channel,
195c2dd4cdSSimon Glass uint period_ns, uint duty_ns)
205c2dd4cdSSimon Glass {
215c2dd4cdSSimon Glass struct exynos_pwm_priv *priv = dev_get_priv(dev);
225c2dd4cdSSimon Glass struct s5p_timer *regs = priv->regs;
235c2dd4cdSSimon Glass unsigned int offset, prescaler;
245c2dd4cdSSimon Glass uint div = 4, rate, rate_ns;
255c2dd4cdSSimon Glass u32 val;
265c2dd4cdSSimon Glass u32 tcnt, tcmp, tcon;
275c2dd4cdSSimon Glass
285c2dd4cdSSimon Glass if (channel >= 5)
295c2dd4cdSSimon Glass return -EINVAL;
305c2dd4cdSSimon Glass debug("%s: Configure '%s' channel %u, period_ns %u, duty_ns %u\n",
315c2dd4cdSSimon Glass __func__, dev->name, channel, period_ns, duty_ns);
325c2dd4cdSSimon Glass
335c2dd4cdSSimon Glass val = readl(®s->tcfg0);
345c2dd4cdSSimon Glass prescaler = (channel < 2 ? val : (val >> 8)) & 0xff;
355c2dd4cdSSimon Glass div = (readl(®s->tcfg1) >> MUX_DIV_SHIFT(channel)) & 0xf;
365c2dd4cdSSimon Glass
375c2dd4cdSSimon Glass rate = get_pwm_clk() / ((prescaler + 1) * (1 << div));
385c2dd4cdSSimon Glass debug("%s: pwm_clk %lu, rate %u\n", __func__, get_pwm_clk(), rate);
395c2dd4cdSSimon Glass
405c2dd4cdSSimon Glass if (channel < 4) {
415c2dd4cdSSimon Glass rate_ns = 1000000000 / rate;
425c2dd4cdSSimon Glass tcnt = period_ns / rate_ns;
435c2dd4cdSSimon Glass tcmp = duty_ns / rate_ns;
445c2dd4cdSSimon Glass debug("%s: tcnt %u, tcmp %u\n", __func__, tcnt, tcmp);
455c2dd4cdSSimon Glass offset = channel * 3;
465c2dd4cdSSimon Glass writel(tcnt, ®s->tcntb0 + offset);
475c2dd4cdSSimon Glass writel(tcmp, ®s->tcmpb0 + offset);
485c2dd4cdSSimon Glass }
495c2dd4cdSSimon Glass
505c2dd4cdSSimon Glass tcon = readl(®s->tcon);
515c2dd4cdSSimon Glass tcon |= TCON_UPDATE(channel);
525c2dd4cdSSimon Glass if (channel < 4)
535c2dd4cdSSimon Glass tcon |= TCON_AUTO_RELOAD(channel);
545c2dd4cdSSimon Glass else
555c2dd4cdSSimon Glass tcon |= TCON4_AUTO_RELOAD;
565c2dd4cdSSimon Glass writel(tcon, ®s->tcon);
575c2dd4cdSSimon Glass
585c2dd4cdSSimon Glass tcon &= ~TCON_UPDATE(channel);
595c2dd4cdSSimon Glass writel(tcon, ®s->tcon);
605c2dd4cdSSimon Glass
615c2dd4cdSSimon Glass return 0;
625c2dd4cdSSimon Glass }
635c2dd4cdSSimon Glass
exynos_pwm_set_enable(struct udevice * dev,uint channel,bool enable)645c2dd4cdSSimon Glass static int exynos_pwm_set_enable(struct udevice *dev, uint channel,
655c2dd4cdSSimon Glass bool enable)
665c2dd4cdSSimon Glass {
675c2dd4cdSSimon Glass struct exynos_pwm_priv *priv = dev_get_priv(dev);
685c2dd4cdSSimon Glass struct s5p_timer *regs = priv->regs;
695c2dd4cdSSimon Glass u32 mask;
705c2dd4cdSSimon Glass
715c2dd4cdSSimon Glass if (channel >= 4)
725c2dd4cdSSimon Glass return -EINVAL;
735c2dd4cdSSimon Glass debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel);
745c2dd4cdSSimon Glass mask = TCON_START(channel);
755c2dd4cdSSimon Glass clrsetbits_le32(®s->tcon, mask, enable ? mask : 0);
765c2dd4cdSSimon Glass
775c2dd4cdSSimon Glass return 0;
785c2dd4cdSSimon Glass }
795c2dd4cdSSimon Glass
exynos_pwm_probe(struct udevice * dev)805c2dd4cdSSimon Glass static int exynos_pwm_probe(struct udevice *dev)
815c2dd4cdSSimon Glass {
825c2dd4cdSSimon Glass struct exynos_pwm_priv *priv = dev_get_priv(dev);
835c2dd4cdSSimon Glass struct s5p_timer *regs = priv->regs;
845c2dd4cdSSimon Glass
855c2dd4cdSSimon Glass writel(PRESCALER_0 | PRESCALER_1 << 8, ®s->tcfg0);
865c2dd4cdSSimon Glass
875c2dd4cdSSimon Glass return 0;
885c2dd4cdSSimon Glass }
895c2dd4cdSSimon Glass
exynos_pwm_ofdata_to_platdata(struct udevice * dev)905c2dd4cdSSimon Glass static int exynos_pwm_ofdata_to_platdata(struct udevice *dev)
915c2dd4cdSSimon Glass {
925c2dd4cdSSimon Glass struct exynos_pwm_priv *priv = dev_get_priv(dev);
935c2dd4cdSSimon Glass
94a821c4afSSimon Glass priv->regs = (struct s5p_timer *)devfdt_get_addr(dev);
955c2dd4cdSSimon Glass
965c2dd4cdSSimon Glass return 0;
975c2dd4cdSSimon Glass }
985c2dd4cdSSimon Glass
995c2dd4cdSSimon Glass static const struct pwm_ops exynos_pwm_ops = {
1005c2dd4cdSSimon Glass .set_config = exynos_pwm_set_config,
1015c2dd4cdSSimon Glass .set_enable = exynos_pwm_set_enable,
1025c2dd4cdSSimon Glass };
1035c2dd4cdSSimon Glass
1045c2dd4cdSSimon Glass static const struct udevice_id exynos_channels[] = {
1055c2dd4cdSSimon Glass { .compatible = "samsung,exynos4210-pwm" },
1065c2dd4cdSSimon Glass { }
1075c2dd4cdSSimon Glass };
1085c2dd4cdSSimon Glass
1095c2dd4cdSSimon Glass U_BOOT_DRIVER(exynos_pwm) = {
1105c2dd4cdSSimon Glass .name = "exynos_pwm",
1115c2dd4cdSSimon Glass .id = UCLASS_PWM,
1125c2dd4cdSSimon Glass .of_match = exynos_channels,
1135c2dd4cdSSimon Glass .ops = &exynos_pwm_ops,
1145c2dd4cdSSimon Glass .probe = exynos_pwm_probe,
1155c2dd4cdSSimon Glass .ofdata_to_platdata = exynos_pwm_ofdata_to_platdata,
1165c2dd4cdSSimon Glass .priv_auto_alloc_size = sizeof(struct exynos_pwm_priv),
1175c2dd4cdSSimon Glass };
118