162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PWM framework driver for Cirrus Logic EP93xx 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com> 662306a36Sopenharmony_ci * Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * EP9301/02 have only one channel: 962306a36Sopenharmony_ci * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * EP9307 has only one channel: 1262306a36Sopenharmony_ci * platform device ep93xx-pwm.0 - PWMOUT 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * EP9312/15 have two channels: 1562306a36Sopenharmony_ci * platform device ep93xx-pwm.0 - PWMOUT 1662306a36Sopenharmony_ci * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/err.h> 2462306a36Sopenharmony_ci#include <linux/io.h> 2562306a36Sopenharmony_ci#include <linux/pwm.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/div64.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/soc/cirrus/ep93xx.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define EP93XX_PWMx_TERM_COUNT 0x00 3262306a36Sopenharmony_ci#define EP93XX_PWMx_DUTY_CYCLE 0x04 3362306a36Sopenharmony_ci#define EP93XX_PWMx_ENABLE 0x08 3462306a36Sopenharmony_ci#define EP93XX_PWMx_INVERT 0x0c 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct ep93xx_pwm { 3762306a36Sopenharmony_ci void __iomem *base; 3862306a36Sopenharmony_ci struct clk *clk; 3962306a36Sopenharmony_ci struct pwm_chip chip; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return container_of(chip, struct ep93xx_pwm, chip); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(chip->dev); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return ep93xx_pwm_acquire_gpio(pdev); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(chip->dev); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ep93xx_pwm_release_gpio(pdev); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 6262306a36Sopenharmony_ci const struct pwm_state *state) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); 6662306a36Sopenharmony_ci bool enabled = state->enabled; 6762306a36Sopenharmony_ci void __iomem *base = ep93xx_pwm->base; 6862306a36Sopenharmony_ci unsigned long long c; 6962306a36Sopenharmony_ci unsigned long period_cycles; 7062306a36Sopenharmony_ci unsigned long duty_cycles; 7162306a36Sopenharmony_ci unsigned long term; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (state->polarity != pwm->state.polarity) { 7462306a36Sopenharmony_ci if (enabled) { 7562306a36Sopenharmony_ci writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); 7662306a36Sopenharmony_ci clk_disable_unprepare(ep93xx_pwm->clk); 7762306a36Sopenharmony_ci enabled = false; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * The clock needs to be enabled to access the PWM registers. 8262306a36Sopenharmony_ci * Polarity can only be changed when the PWM is disabled. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci ret = clk_prepare_enable(ep93xx_pwm->clk); 8562306a36Sopenharmony_ci if (ret) 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (state->polarity == PWM_POLARITY_INVERSED) 8962306a36Sopenharmony_ci writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci clk_disable_unprepare(ep93xx_pwm->clk); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!state->enabled) { 9762306a36Sopenharmony_ci if (enabled) { 9862306a36Sopenharmony_ci writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); 9962306a36Sopenharmony_ci clk_disable_unprepare(ep93xx_pwm->clk); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * The clock needs to be enabled to access the PWM registers. 10762306a36Sopenharmony_ci * Configuration can be changed at any time. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci if (!pwm_is_enabled(pwm)) { 11062306a36Sopenharmony_ci ret = clk_prepare_enable(ep93xx_pwm->clk); 11162306a36Sopenharmony_ci if (ret) 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci c = clk_get_rate(ep93xx_pwm->clk); 11662306a36Sopenharmony_ci c *= state->period; 11762306a36Sopenharmony_ci do_div(c, 1000000000); 11862306a36Sopenharmony_ci period_cycles = c; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci c = period_cycles; 12162306a36Sopenharmony_ci c *= state->duty_cycle; 12262306a36Sopenharmony_ci do_div(c, state->period); 12362306a36Sopenharmony_ci duty_cycles = c; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (period_cycles < 0x10000 && duty_cycles < 0x10000) { 12662306a36Sopenharmony_ci term = readw(base + EP93XX_PWMx_TERM_COUNT); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Order is important if PWM is running */ 12962306a36Sopenharmony_ci if (period_cycles > term) { 13062306a36Sopenharmony_ci writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); 13162306a36Sopenharmony_ci writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); 13462306a36Sopenharmony_ci writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci ret = 0; 13762306a36Sopenharmony_ci } else { 13862306a36Sopenharmony_ci ret = -EINVAL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!pwm_is_enabled(pwm)) 14262306a36Sopenharmony_ci clk_disable_unprepare(ep93xx_pwm->clk); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (ret) 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!enabled) { 14862306a36Sopenharmony_ci ret = clk_prepare_enable(ep93xx_pwm->clk); 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct pwm_ops ep93xx_pwm_ops = { 15962306a36Sopenharmony_ci .request = ep93xx_pwm_request, 16062306a36Sopenharmony_ci .free = ep93xx_pwm_free, 16162306a36Sopenharmony_ci .apply = ep93xx_pwm_apply, 16262306a36Sopenharmony_ci .owner = THIS_MODULE, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int ep93xx_pwm_probe(struct platform_device *pdev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct ep93xx_pwm *ep93xx_pwm; 16862306a36Sopenharmony_ci int ret; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL); 17162306a36Sopenharmony_ci if (!ep93xx_pwm) 17262306a36Sopenharmony_ci return -ENOMEM; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ep93xx_pwm->base = devm_platform_ioremap_resource(pdev, 0); 17562306a36Sopenharmony_ci if (IS_ERR(ep93xx_pwm->base)) 17662306a36Sopenharmony_ci return PTR_ERR(ep93xx_pwm->base); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); 17962306a36Sopenharmony_ci if (IS_ERR(ep93xx_pwm->clk)) 18062306a36Sopenharmony_ci return PTR_ERR(ep93xx_pwm->clk); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ep93xx_pwm->chip.dev = &pdev->dev; 18362306a36Sopenharmony_ci ep93xx_pwm->chip.ops = &ep93xx_pwm_ops; 18462306a36Sopenharmony_ci ep93xx_pwm->chip.npwm = 1; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = devm_pwmchip_add(&pdev->dev, &ep93xx_pwm->chip); 18762306a36Sopenharmony_ci if (ret < 0) 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct platform_driver ep93xx_pwm_driver = { 19462306a36Sopenharmony_ci .driver = { 19562306a36Sopenharmony_ci .name = "ep93xx-pwm", 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci .probe = ep93xx_pwm_probe, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_cimodule_platform_driver(ep93xx_pwm_driver); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciMODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver"); 20262306a36Sopenharmony_ciMODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); 20362306a36Sopenharmony_ciMODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 20462306a36Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-pwm"); 20562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 206