18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2016 Broadcom 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/math64.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pwm.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define IPROC_PWM_CTRL_OFFSET 0x00 258c2ecf20Sopenharmony_ci#define IPROC_PWM_CTRL_TYPE_SHIFT(ch) (15 + (ch)) 268c2ecf20Sopenharmony_ci#define IPROC_PWM_CTRL_POLARITY_SHIFT(ch) (8 + (ch)) 278c2ecf20Sopenharmony_ci#define IPROC_PWM_CTRL_EN_SHIFT(ch) (ch) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define IPROC_PWM_PERIOD_OFFSET(ch) (0x04 + ((ch) << 3)) 308c2ecf20Sopenharmony_ci#define IPROC_PWM_PERIOD_MIN 0x02 318c2ecf20Sopenharmony_ci#define IPROC_PWM_PERIOD_MAX 0xffff 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define IPROC_PWM_DUTY_CYCLE_OFFSET(ch) (0x08 + ((ch) << 3)) 348c2ecf20Sopenharmony_ci#define IPROC_PWM_DUTY_CYCLE_MIN 0x00 358c2ecf20Sopenharmony_ci#define IPROC_PWM_DUTY_CYCLE_MAX 0xffff 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define IPROC_PWM_PRESCALE_OFFSET 0x24 388c2ecf20Sopenharmony_ci#define IPROC_PWM_PRESCALE_BITS 0x06 398c2ecf20Sopenharmony_ci#define IPROC_PWM_PRESCALE_SHIFT(ch) ((3 - (ch)) * \ 408c2ecf20Sopenharmony_ci IPROC_PWM_PRESCALE_BITS) 418c2ecf20Sopenharmony_ci#define IPROC_PWM_PRESCALE_MASK(ch) (IPROC_PWM_PRESCALE_MAX << \ 428c2ecf20Sopenharmony_ci IPROC_PWM_PRESCALE_SHIFT(ch)) 438c2ecf20Sopenharmony_ci#define IPROC_PWM_PRESCALE_MIN 0x00 448c2ecf20Sopenharmony_ci#define IPROC_PWM_PRESCALE_MAX 0x3f 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct iproc_pwmc { 478c2ecf20Sopenharmony_ci struct pwm_chip chip; 488c2ecf20Sopenharmony_ci void __iomem *base; 498c2ecf20Sopenharmony_ci struct clk *clk; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline struct iproc_pwmc *to_iproc_pwmc(struct pwm_chip *chip) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return container_of(chip, struct iproc_pwmc, chip); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void iproc_pwmc_enable(struct iproc_pwmc *ip, unsigned int channel) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci u32 value; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_CTRL_OFFSET); 628c2ecf20Sopenharmony_ci value |= 1 << IPROC_PWM_CTRL_EN_SHIFT(channel); 638c2ecf20Sopenharmony_ci writel(value, ip->base + IPROC_PWM_CTRL_OFFSET); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* must be a 400 ns delay between clearing and setting enable bit */ 668c2ecf20Sopenharmony_ci ndelay(400); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void iproc_pwmc_disable(struct iproc_pwmc *ip, unsigned int channel) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 value; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_CTRL_OFFSET); 748c2ecf20Sopenharmony_ci value &= ~(1 << IPROC_PWM_CTRL_EN_SHIFT(channel)); 758c2ecf20Sopenharmony_ci writel(value, ip->base + IPROC_PWM_CTRL_OFFSET); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* must be a 400 ns delay between clearing and setting enable bit */ 788c2ecf20Sopenharmony_ci ndelay(400); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 828c2ecf20Sopenharmony_ci struct pwm_state *state) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct iproc_pwmc *ip = to_iproc_pwmc(chip); 858c2ecf20Sopenharmony_ci u64 tmp, multi, rate; 868c2ecf20Sopenharmony_ci u32 value, prescale; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_CTRL_OFFSET); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (value & BIT(IPROC_PWM_CTRL_EN_SHIFT(pwm->hwpwm))) 918c2ecf20Sopenharmony_ci state->enabled = true; 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci state->enabled = false; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (value & BIT(IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm))) 968c2ecf20Sopenharmony_ci state->polarity = PWM_POLARITY_NORMAL; 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci state->polarity = PWM_POLARITY_INVERSED; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rate = clk_get_rate(ip->clk); 1018c2ecf20Sopenharmony_ci if (rate == 0) { 1028c2ecf20Sopenharmony_ci state->period = 0; 1038c2ecf20Sopenharmony_ci state->duty_cycle = 0; 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_PRESCALE_OFFSET); 1088c2ecf20Sopenharmony_ci prescale = value >> IPROC_PWM_PRESCALE_SHIFT(pwm->hwpwm); 1098c2ecf20Sopenharmony_ci prescale &= IPROC_PWM_PRESCALE_MAX; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci multi = NSEC_PER_SEC * (prescale + 1); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_PERIOD_OFFSET(pwm->hwpwm)); 1148c2ecf20Sopenharmony_ci tmp = (value & IPROC_PWM_PERIOD_MAX) * multi; 1158c2ecf20Sopenharmony_ci state->period = div64_u64(tmp, rate); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_DUTY_CYCLE_OFFSET(pwm->hwpwm)); 1188c2ecf20Sopenharmony_ci tmp = (value & IPROC_PWM_PERIOD_MAX) * multi; 1198c2ecf20Sopenharmony_ci state->duty_cycle = div64_u64(tmp, rate); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm, 1238c2ecf20Sopenharmony_ci const struct pwm_state *state) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned long prescale = IPROC_PWM_PRESCALE_MIN; 1268c2ecf20Sopenharmony_ci struct iproc_pwmc *ip = to_iproc_pwmc(chip); 1278c2ecf20Sopenharmony_ci u32 value, period, duty; 1288c2ecf20Sopenharmony_ci u64 rate; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rate = clk_get_rate(ip->clk); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * Find period count, duty count and prescale to suit duty_cycle and 1348c2ecf20Sopenharmony_ci * period. This is done according to formulas described below: 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE 1378c2ecf20Sopenharmony_ci * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) 1408c2ecf20Sopenharmony_ci * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci while (1) { 1438c2ecf20Sopenharmony_ci u64 value, div; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci div = NSEC_PER_SEC * (prescale + 1); 1468c2ecf20Sopenharmony_ci value = rate * state->period; 1478c2ecf20Sopenharmony_ci period = div64_u64(value, div); 1488c2ecf20Sopenharmony_ci value = rate * state->duty_cycle; 1498c2ecf20Sopenharmony_ci duty = div64_u64(value, div); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (period < IPROC_PWM_PERIOD_MIN) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (period <= IPROC_PWM_PERIOD_MAX && 1558c2ecf20Sopenharmony_ci duty <= IPROC_PWM_DUTY_CYCLE_MAX) 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Otherwise, increase prescale and recalculate counts */ 1598c2ecf20Sopenharmony_ci if (++prescale > IPROC_PWM_PRESCALE_MAX) 1608c2ecf20Sopenharmony_ci return -EINVAL; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci iproc_pwmc_disable(ip, pwm->hwpwm); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Set prescale */ 1668c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_PRESCALE_OFFSET); 1678c2ecf20Sopenharmony_ci value &= ~IPROC_PWM_PRESCALE_MASK(pwm->hwpwm); 1688c2ecf20Sopenharmony_ci value |= prescale << IPROC_PWM_PRESCALE_SHIFT(pwm->hwpwm); 1698c2ecf20Sopenharmony_ci writel(value, ip->base + IPROC_PWM_PRESCALE_OFFSET); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* set period and duty cycle */ 1728c2ecf20Sopenharmony_ci writel(period, ip->base + IPROC_PWM_PERIOD_OFFSET(pwm->hwpwm)); 1738c2ecf20Sopenharmony_ci writel(duty, ip->base + IPROC_PWM_DUTY_CYCLE_OFFSET(pwm->hwpwm)); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* set polarity */ 1768c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_CTRL_OFFSET); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (state->polarity == PWM_POLARITY_NORMAL) 1798c2ecf20Sopenharmony_ci value |= 1 << IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm); 1808c2ecf20Sopenharmony_ci else 1818c2ecf20Sopenharmony_ci value &= ~(1 << IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm)); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci writel(value, ip->base + IPROC_PWM_CTRL_OFFSET); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (state->enabled) 1868c2ecf20Sopenharmony_ci iproc_pwmc_enable(ip, pwm->hwpwm); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic const struct pwm_ops iproc_pwm_ops = { 1928c2ecf20Sopenharmony_ci .apply = iproc_pwmc_apply, 1938c2ecf20Sopenharmony_ci .get_state = iproc_pwmc_get_state, 1948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int iproc_pwmc_probe(struct platform_device *pdev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct iproc_pwmc *ip; 2008c2ecf20Sopenharmony_ci struct resource *res; 2018c2ecf20Sopenharmony_ci unsigned int i; 2028c2ecf20Sopenharmony_ci u32 value; 2038c2ecf20Sopenharmony_ci int ret; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ip = devm_kzalloc(&pdev->dev, sizeof(*ip), GFP_KERNEL); 2068c2ecf20Sopenharmony_ci if (!ip) 2078c2ecf20Sopenharmony_ci return -ENOMEM; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ip); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ip->chip.dev = &pdev->dev; 2128c2ecf20Sopenharmony_ci ip->chip.ops = &iproc_pwm_ops; 2138c2ecf20Sopenharmony_ci ip->chip.base = -1; 2148c2ecf20Sopenharmony_ci ip->chip.npwm = 4; 2158c2ecf20Sopenharmony_ci ip->chip.of_xlate = of_pwm_xlate_with_flags; 2168c2ecf20Sopenharmony_ci ip->chip.of_pwm_n_cells = 3; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2198c2ecf20Sopenharmony_ci ip->base = devm_ioremap_resource(&pdev->dev, res); 2208c2ecf20Sopenharmony_ci if (IS_ERR(ip->base)) 2218c2ecf20Sopenharmony_ci return PTR_ERR(ip->base); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ip->clk = devm_clk_get(&pdev->dev, NULL); 2248c2ecf20Sopenharmony_ci if (IS_ERR(ip->clk)) { 2258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock: %ld\n", 2268c2ecf20Sopenharmony_ci PTR_ERR(ip->clk)); 2278c2ecf20Sopenharmony_ci return PTR_ERR(ip->clk); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ip->clk); 2318c2ecf20Sopenharmony_ci if (ret < 0) { 2328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* Set full drive and normal polarity for all channels */ 2378c2ecf20Sopenharmony_ci value = readl(ip->base + IPROC_PWM_CTRL_OFFSET); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci for (i = 0; i < ip->chip.npwm; i++) { 2408c2ecf20Sopenharmony_ci value &= ~(1 << IPROC_PWM_CTRL_TYPE_SHIFT(i)); 2418c2ecf20Sopenharmony_ci value |= 1 << IPROC_PWM_CTRL_POLARITY_SHIFT(i); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci writel(value, ip->base + IPROC_PWM_CTRL_OFFSET); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ret = pwmchip_add(&ip->chip); 2478c2ecf20Sopenharmony_ci if (ret < 0) { 2488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); 2498c2ecf20Sopenharmony_ci clk_disable_unprepare(ip->clk); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int iproc_pwmc_remove(struct platform_device *pdev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct iproc_pwmc *ip = platform_get_drvdata(pdev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci clk_disable_unprepare(ip->clk); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return pwmchip_remove(&ip->chip); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const struct of_device_id bcm_iproc_pwmc_dt[] = { 2658c2ecf20Sopenharmony_ci { .compatible = "brcm,iproc-pwm" }, 2668c2ecf20Sopenharmony_ci { }, 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_iproc_pwmc_dt); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct platform_driver iproc_pwmc_driver = { 2718c2ecf20Sopenharmony_ci .driver = { 2728c2ecf20Sopenharmony_ci .name = "bcm-iproc-pwm", 2738c2ecf20Sopenharmony_ci .of_match_table = bcm_iproc_pwmc_dt, 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci .probe = iproc_pwmc_probe, 2768c2ecf20Sopenharmony_ci .remove = iproc_pwmc_remove, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_cimodule_platform_driver(iproc_pwmc_driver); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yendapally Reddy Dhananjaya Reddy <yendapally.reddy@broadcom.com>"); 2818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom iProc PWM driver"); 2828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 283