18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/pwm/pwm-vt8500.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/pwm.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/clk.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/div64.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_device.h>
238c2ecf20Sopenharmony_ci#include <linux/of_address.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * SoC architecture allocates register space for 4 PWMs but only
278c2ecf20Sopenharmony_ci * 2 are currently implemented.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define VT8500_NR_PWMS	2
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define REG_CTRL(pwm)		(((pwm) << 4) + 0x00)
328c2ecf20Sopenharmony_ci#define REG_SCALAR(pwm)		(((pwm) << 4) + 0x04)
338c2ecf20Sopenharmony_ci#define REG_PERIOD(pwm)		(((pwm) << 4) + 0x08)
348c2ecf20Sopenharmony_ci#define REG_DUTY(pwm)		(((pwm) << 4) + 0x0C)
358c2ecf20Sopenharmony_ci#define REG_STATUS		0x40
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define CTRL_ENABLE		BIT(0)
388c2ecf20Sopenharmony_ci#define CTRL_INVERT		BIT(1)
398c2ecf20Sopenharmony_ci#define CTRL_AUTOLOAD		BIT(2)
408c2ecf20Sopenharmony_ci#define CTRL_STOP_IMM		BIT(3)
418c2ecf20Sopenharmony_ci#define CTRL_LOAD_PRESCALE	BIT(4)
428c2ecf20Sopenharmony_ci#define CTRL_LOAD_PERIOD	BIT(5)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define STATUS_CTRL_UPDATE	BIT(0)
458c2ecf20Sopenharmony_ci#define STATUS_SCALAR_UPDATE	BIT(1)
468c2ecf20Sopenharmony_ci#define STATUS_PERIOD_UPDATE	BIT(2)
478c2ecf20Sopenharmony_ci#define STATUS_DUTY_UPDATE	BIT(3)
488c2ecf20Sopenharmony_ci#define STATUS_ALL_UPDATE	0x0F
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct vt8500_chip {
518c2ecf20Sopenharmony_ci	struct pwm_chip chip;
528c2ecf20Sopenharmony_ci	void __iomem *base;
538c2ecf20Sopenharmony_ci	struct clk *clk;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define to_vt8500_chip(chip)	container_of(chip, struct vt8500_chip, chip)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
598c2ecf20Sopenharmony_cistatic inline void pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	int loops = msecs_to_loops(10);
628c2ecf20Sopenharmony_ci	u32 mask = bitmask << (nr << 8);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	while ((readl(vt8500->base + REG_STATUS) & mask) && --loops)
658c2ecf20Sopenharmony_ci		cpu_relax();
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (unlikely(!loops))
688c2ecf20Sopenharmony_ci		dev_warn(vt8500->chip.dev, "Waiting for status bits 0x%x to clear timed out\n",
698c2ecf20Sopenharmony_ci			 mask);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
738c2ecf20Sopenharmony_ci		int duty_ns, int period_ns)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
768c2ecf20Sopenharmony_ci	unsigned long long c;
778c2ecf20Sopenharmony_ci	unsigned long period_cycles, prescale, pv, dc;
788c2ecf20Sopenharmony_ci	int err;
798c2ecf20Sopenharmony_ci	u32 val;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	err = clk_enable(vt8500->clk);
828c2ecf20Sopenharmony_ci	if (err < 0) {
838c2ecf20Sopenharmony_ci		dev_err(chip->dev, "failed to enable clock\n");
848c2ecf20Sopenharmony_ci		return err;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	c = clk_get_rate(vt8500->clk);
888c2ecf20Sopenharmony_ci	c = c * period_ns;
898c2ecf20Sopenharmony_ci	do_div(c, 1000000000);
908c2ecf20Sopenharmony_ci	period_cycles = c;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (period_cycles < 1)
938c2ecf20Sopenharmony_ci		period_cycles = 1;
948c2ecf20Sopenharmony_ci	prescale = (period_cycles - 1) / 4096;
958c2ecf20Sopenharmony_ci	pv = period_cycles / (prescale + 1) - 1;
968c2ecf20Sopenharmony_ci	if (pv > 4095)
978c2ecf20Sopenharmony_ci		pv = 4095;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (prescale > 1023) {
1008c2ecf20Sopenharmony_ci		clk_disable(vt8500->clk);
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	c = (unsigned long long)pv * duty_ns;
1058c2ecf20Sopenharmony_ci	do_div(c, period_ns);
1068c2ecf20Sopenharmony_ci	dc = c;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm));
1098c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm));
1128c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm));
1158c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1188c2ecf20Sopenharmony_ci	val |= CTRL_AUTOLOAD;
1198c2ecf20Sopenharmony_ci	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
1208c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	clk_disable(vt8500->clk);
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
1298c2ecf20Sopenharmony_ci	int err;
1308c2ecf20Sopenharmony_ci	u32 val;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	err = clk_enable(vt8500->clk);
1338c2ecf20Sopenharmony_ci	if (err < 0) {
1348c2ecf20Sopenharmony_ci		dev_err(chip->dev, "failed to enable clock\n");
1358c2ecf20Sopenharmony_ci		return err;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1398c2ecf20Sopenharmony_ci	val |= CTRL_ENABLE;
1408c2ecf20Sopenharmony_ci	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
1418c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
1498c2ecf20Sopenharmony_ci	u32 val;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1528c2ecf20Sopenharmony_ci	val &= ~CTRL_ENABLE;
1538c2ecf20Sopenharmony_ci	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
1548c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	clk_disable(vt8500->clk);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int vt8500_pwm_set_polarity(struct pwm_chip *chip,
1608c2ecf20Sopenharmony_ci				   struct pwm_device *pwm,
1618c2ecf20Sopenharmony_ci				   enum pwm_polarity polarity)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
1648c2ecf20Sopenharmony_ci	u32 val;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (polarity == PWM_POLARITY_INVERSED)
1698c2ecf20Sopenharmony_ci		val |= CTRL_INVERT;
1708c2ecf20Sopenharmony_ci	else
1718c2ecf20Sopenharmony_ci		val &= ~CTRL_INVERT;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
1748c2ecf20Sopenharmony_ci	pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic const struct pwm_ops vt8500_pwm_ops = {
1808c2ecf20Sopenharmony_ci	.enable = vt8500_pwm_enable,
1818c2ecf20Sopenharmony_ci	.disable = vt8500_pwm_disable,
1828c2ecf20Sopenharmony_ci	.config = vt8500_pwm_config,
1838c2ecf20Sopenharmony_ci	.set_polarity = vt8500_pwm_set_polarity,
1848c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic const struct of_device_id vt8500_pwm_dt_ids[] = {
1888c2ecf20Sopenharmony_ci	{ .compatible = "via,vt8500-pwm", },
1898c2ecf20Sopenharmony_ci	{ /* Sentinel */ }
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int vt8500_pwm_probe(struct platform_device *pdev)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct vt8500_chip *chip;
1968c2ecf20Sopenharmony_ci	struct resource *r;
1978c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
1988c2ecf20Sopenharmony_ci	int ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (!np) {
2018c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid devicetree node\n");
2028c2ecf20Sopenharmony_ci		return -EINVAL;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
2068c2ecf20Sopenharmony_ci	if (chip == NULL)
2078c2ecf20Sopenharmony_ci		return -ENOMEM;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	chip->chip.dev = &pdev->dev;
2108c2ecf20Sopenharmony_ci	chip->chip.ops = &vt8500_pwm_ops;
2118c2ecf20Sopenharmony_ci	chip->chip.of_xlate = of_pwm_xlate_with_flags;
2128c2ecf20Sopenharmony_ci	chip->chip.of_pwm_n_cells = 3;
2138c2ecf20Sopenharmony_ci	chip->chip.base = -1;
2148c2ecf20Sopenharmony_ci	chip->chip.npwm = VT8500_NR_PWMS;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	chip->clk = devm_clk_get(&pdev->dev, NULL);
2178c2ecf20Sopenharmony_ci	if (IS_ERR(chip->clk)) {
2188c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "clock source not specified\n");
2198c2ecf20Sopenharmony_ci		return PTR_ERR(chip->clk);
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2238c2ecf20Sopenharmony_ci	chip->base = devm_ioremap_resource(&pdev->dev, r);
2248c2ecf20Sopenharmony_ci	if (IS_ERR(chip->base))
2258c2ecf20Sopenharmony_ci		return PTR_ERR(chip->base);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	ret = clk_prepare(chip->clk);
2288c2ecf20Sopenharmony_ci	if (ret < 0) {
2298c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to prepare clock\n");
2308c2ecf20Sopenharmony_ci		return ret;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ret = pwmchip_add(&chip->chip);
2348c2ecf20Sopenharmony_ci	if (ret < 0) {
2358c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to add PWM chip\n");
2368c2ecf20Sopenharmony_ci		clk_unprepare(chip->clk);
2378c2ecf20Sopenharmony_ci		return ret;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, chip);
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int vt8500_pwm_remove(struct platform_device *pdev)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct vt8500_chip *chip;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	chip = platform_get_drvdata(pdev);
2498c2ecf20Sopenharmony_ci	if (chip == NULL)
2508c2ecf20Sopenharmony_ci		return -ENODEV;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	clk_unprepare(chip->clk);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return pwmchip_remove(&chip->chip);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic struct platform_driver vt8500_pwm_driver = {
2588c2ecf20Sopenharmony_ci	.probe		= vt8500_pwm_probe,
2598c2ecf20Sopenharmony_ci	.remove		= vt8500_pwm_remove,
2608c2ecf20Sopenharmony_ci	.driver		= {
2618c2ecf20Sopenharmony_ci		.name	= "vt8500-pwm",
2628c2ecf20Sopenharmony_ci		.of_match_table = vt8500_pwm_dt_ids,
2638c2ecf20Sopenharmony_ci	},
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_cimodule_platform_driver(vt8500_pwm_driver);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VT8500 PWM Driver");
2688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
2698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
270