18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PWM framework driver for Cirrus Logic EP93xx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * EP9301/02 have only one channel: 98c2ecf20Sopenharmony_ci * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * EP9307 has only one channel: 128c2ecf20Sopenharmony_ci * platform device ep93xx-pwm.0 - PWMOUT 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * EP9312/15 have two channels: 158c2ecf20Sopenharmony_ci * platform device ep93xx-pwm.0 - PWMOUT 168c2ecf20Sopenharmony_ci * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/clk.h> 238c2ecf20Sopenharmony_ci#include <linux/err.h> 248c2ecf20Sopenharmony_ci#include <linux/io.h> 258c2ecf20Sopenharmony_ci#include <linux/pwm.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/div64.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/soc/cirrus/ep93xx.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define EP93XX_PWMx_TERM_COUNT 0x00 328c2ecf20Sopenharmony_ci#define EP93XX_PWMx_DUTY_CYCLE 0x04 338c2ecf20Sopenharmony_ci#define EP93XX_PWMx_ENABLE 0x08 348c2ecf20Sopenharmony_ci#define EP93XX_PWMx_INVERT 0x0c 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct ep93xx_pwm { 378c2ecf20Sopenharmony_ci void __iomem *base; 388c2ecf20Sopenharmony_ci struct clk *clk; 398c2ecf20Sopenharmony_ci struct pwm_chip chip; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return container_of(chip, struct ep93xx_pwm, chip); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(chip->dev); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return ep93xx_pwm_acquire_gpio(pdev); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(chip->dev); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ep93xx_pwm_release_gpio(pdev); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 628c2ecf20Sopenharmony_ci int duty_ns, int period_ns) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); 658c2ecf20Sopenharmony_ci void __iomem *base = ep93xx_pwm->base; 668c2ecf20Sopenharmony_ci unsigned long long c; 678c2ecf20Sopenharmony_ci unsigned long period_cycles; 688c2ecf20Sopenharmony_ci unsigned long duty_cycles; 698c2ecf20Sopenharmony_ci unsigned long term; 708c2ecf20Sopenharmony_ci int ret = 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * The clock needs to be enabled to access the PWM registers. 748c2ecf20Sopenharmony_ci * Configuration can be changed at any time. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci if (!pwm_is_enabled(pwm)) { 778c2ecf20Sopenharmony_ci ret = clk_enable(ep93xx_pwm->clk); 788c2ecf20Sopenharmony_ci if (ret) 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci c = clk_get_rate(ep93xx_pwm->clk); 838c2ecf20Sopenharmony_ci c *= period_ns; 848c2ecf20Sopenharmony_ci do_div(c, 1000000000); 858c2ecf20Sopenharmony_ci period_cycles = c; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci c = period_cycles; 888c2ecf20Sopenharmony_ci c *= duty_ns; 898c2ecf20Sopenharmony_ci do_div(c, period_ns); 908c2ecf20Sopenharmony_ci duty_cycles = c; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (period_cycles < 0x10000 && duty_cycles < 0x10000) { 938c2ecf20Sopenharmony_ci term = readw(base + EP93XX_PWMx_TERM_COUNT); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Order is important if PWM is running */ 968c2ecf20Sopenharmony_ci if (period_cycles > term) { 978c2ecf20Sopenharmony_ci writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); 988c2ecf20Sopenharmony_ci writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); 1018c2ecf20Sopenharmony_ci writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } else { 1048c2ecf20Sopenharmony_ci ret = -EINVAL; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!pwm_is_enabled(pwm)) 1088c2ecf20Sopenharmony_ci clk_disable(ep93xx_pwm->clk); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm, 1148c2ecf20Sopenharmony_ci enum pwm_polarity polarity) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * The clock needs to be enabled to access the PWM registers. 1218c2ecf20Sopenharmony_ci * Polarity can only be changed when the PWM is disabled. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci ret = clk_enable(ep93xx_pwm->clk); 1248c2ecf20Sopenharmony_ci if (ret) 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (polarity == PWM_POLARITY_INVERSED) 1288c2ecf20Sopenharmony_ci writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci clk_disable(ep93xx_pwm->clk); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = clk_enable(ep93xx_pwm->clk); 1438c2ecf20Sopenharmony_ci if (ret) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); 1568c2ecf20Sopenharmony_ci clk_disable(ep93xx_pwm->clk); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct pwm_ops ep93xx_pwm_ops = { 1608c2ecf20Sopenharmony_ci .request = ep93xx_pwm_request, 1618c2ecf20Sopenharmony_ci .free = ep93xx_pwm_free, 1628c2ecf20Sopenharmony_ci .config = ep93xx_pwm_config, 1638c2ecf20Sopenharmony_ci .set_polarity = ep93xx_pwm_polarity, 1648c2ecf20Sopenharmony_ci .enable = ep93xx_pwm_enable, 1658c2ecf20Sopenharmony_ci .disable = ep93xx_pwm_disable, 1668c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int ep93xx_pwm_probe(struct platform_device *pdev) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm; 1728c2ecf20Sopenharmony_ci struct resource *res; 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (!ep93xx_pwm) 1778c2ecf20Sopenharmony_ci return -ENOMEM; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1808c2ecf20Sopenharmony_ci ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); 1818c2ecf20Sopenharmony_ci if (IS_ERR(ep93xx_pwm->base)) 1828c2ecf20Sopenharmony_ci return PTR_ERR(ep93xx_pwm->base); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); 1858c2ecf20Sopenharmony_ci if (IS_ERR(ep93xx_pwm->clk)) 1868c2ecf20Sopenharmony_ci return PTR_ERR(ep93xx_pwm->clk); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ep93xx_pwm->chip.dev = &pdev->dev; 1898c2ecf20Sopenharmony_ci ep93xx_pwm->chip.ops = &ep93xx_pwm_ops; 1908c2ecf20Sopenharmony_ci ep93xx_pwm->chip.base = -1; 1918c2ecf20Sopenharmony_ci ep93xx_pwm->chip.npwm = 1; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = pwmchip_add(&ep93xx_pwm->chip); 1948c2ecf20Sopenharmony_ci if (ret < 0) 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ep93xx_pwm); 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int ep93xx_pwm_remove(struct platform_device *pdev) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return pwmchip_remove(&ep93xx_pwm->chip); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic struct platform_driver ep93xx_pwm_driver = { 2098c2ecf20Sopenharmony_ci .driver = { 2108c2ecf20Sopenharmony_ci .name = "ep93xx-pwm", 2118c2ecf20Sopenharmony_ci }, 2128c2ecf20Sopenharmony_ci .probe = ep93xx_pwm_probe, 2138c2ecf20Sopenharmony_ci .remove = ep93xx_pwm_remove, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_cimodule_platform_driver(ep93xx_pwm_driver); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver"); 2188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); 2198c2ecf20Sopenharmony_ciMODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 2208c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-pwm"); 2218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 222