Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/pwm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 9 kB image not shown  

Quelle  pwm-rockchip.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * PWM driver for Rockchip SoCs
 *
 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
 * Copyright (C) 2014 ROCKCHIP, Inc.
 */


#include <linux/clk.h>
#include <linux/io.h>
#include <linux/limits.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/time.h>

#define PWM_CTRL_TIMER_EN (1 << 0)
#define PWM_CTRL_OUTPUT_EN (1 << 3)

#define PWM_ENABLE  (1 << 0)
#define PWM_CONTINUOUS  (1 << 1)
#define PWM_DUTY_POSITIVE (1 << 3)
#define PWM_DUTY_NEGATIVE (0 << 3)
#define PWM_INACTIVE_NEGATIVE (0 << 4)
#define PWM_INACTIVE_POSITIVE (1 << 4)
#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
#define PWM_OUTPUT_LEFT  (0 << 5)
#define PWM_LOCK_EN  (1 << 6)
#define PWM_LP_DISABLE  (0 << 8)

struct rockchip_pwm_chip {
 struct clk *clk;
 struct clk *pclk;
 const struct rockchip_pwm_data *data;
 void __iomem *base;
};

struct rockchip_pwm_regs {
 unsigned long duty;
 unsigned long period;
 unsigned long cntr;
 unsigned long ctrl;
};

struct rockchip_pwm_data {
 struct rockchip_pwm_regs regs;
 unsigned int prescaler;
 bool supports_polarity;
 bool supports_lock;
 u32 enable_conf;
};

static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip)
{
 return pwmchip_get_drvdata(chip);
}

static int rockchip_pwm_get_state(struct pwm_chip *chip,
      struct pwm_device *pwm,
      struct pwm_state *state)
{
 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC;
 u32 enable_conf = pc->data->enable_conf;
 unsigned long clk_rate;
 u64 tmp;
 u32 val;
 int ret;

 ret = clk_enable(pc->pclk);
 if (ret)
  return ret;

 ret = clk_enable(pc->clk);
 if (ret)
  return ret;

 clk_rate = clk_get_rate(pc->clk);

 tmp = readl_relaxed(pc->base + pc->data->regs.period);
 tmp *= prescaled_ns;
 state->period = DIV_U64_ROUND_UP(tmp, clk_rate);

 tmp = readl_relaxed(pc->base + pc->data->regs.duty);
 tmp *= prescaled_ns;
 state->duty_cycle =  DIV_U64_ROUND_UP(tmp, clk_rate);

 val = readl_relaxed(pc->base + pc->data->regs.ctrl);
 state->enabled = (val & enable_conf) == enable_conf;

 if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
  state->polarity = PWM_POLARITY_INVERSED;
 else
  state->polarity = PWM_POLARITY_NORMAL;

 clk_disable(pc->clk);
 clk_disable(pc->pclk);

 return 0;
}

static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
          const struct pwm_state *state)
{
 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC;
 u64 clk_rate, tmp;
 u32 period_ticks, duty_ticks;
 u32 ctrl;

 clk_rate = clk_get_rate(pc->clk);

 /*
 * Since period and duty cycle registers have a width of 32
 * bits, every possible input period can be obtained using the
 * default prescaler value for all practical clock rate values.
 */

 tmp = mul_u64_u64_div_u64(clk_rate, state->period, prescaled_ns);
 if (tmp > U32_MAX)
  tmp = U32_MAX;
 period_ticks = tmp;

 tmp = mul_u64_u64_div_u64(clk_rate, state->duty_cycle, prescaled_ns);
 if (tmp > U32_MAX)
  tmp = U32_MAX;
 duty_ticks = tmp;

 /*
 * Lock the period and duty of previous configuration, then
 * change the duty and period, that would not be effective.
 */

 ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
 if (pc->data->supports_lock) {
  ctrl |= PWM_LOCK_EN;
  writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
 }

 writel(period_ticks, pc->base + pc->data->regs.period);
 writel(duty_ticks, pc->base + pc->data->regs.duty);

 if (pc->data->supports_polarity) {
  ctrl &= ~PWM_POLARITY_MASK;
  if (state->polarity == PWM_POLARITY_INVERSED)
   ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
  else
   ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
 }

 /*
 * Unlock and set polarity at the same time,
 * the configuration of duty, period and polarity
 * would be effective together at next period.
 */

 if (pc->data->supports_lock)
  ctrl &= ~PWM_LOCK_EN;

 writel(ctrl, pc->base + pc->data->regs.ctrl);
}

static int rockchip_pwm_enable(struct pwm_chip *chip,
          struct pwm_device *pwm,
          bool enable)
{
 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 u32 enable_conf = pc->data->enable_conf;
 int ret;
 u32 val;

 if (enable) {
  ret = clk_enable(pc->clk);
  if (ret)
   return ret;
 }

 val = readl_relaxed(pc->base + pc->data->regs.ctrl);

 if (enable)
  val |= enable_conf;
 else
  val &= ~enable_conf;

 writel_relaxed(val, pc->base + pc->data->regs.ctrl);

 if (!enable)
  clk_disable(pc->clk);

 return 0;
}

static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
         const struct pwm_state *state)
{
 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 struct pwm_state curstate;
 bool enabled;
 int ret = 0;

 ret = clk_enable(pc->pclk);
 if (ret)
  return ret;

 ret = clk_enable(pc->clk);
 if (ret)
  return ret;

 pwm_get_state(pwm, &curstate);
 enabled = curstate.enabled;

 if (state->polarity != curstate.polarity && enabled &&
     !pc->data->supports_lock) {
  ret = rockchip_pwm_enable(chip, pwm, false);
  if (ret)
   goto out;
  enabled = false;
 }

 rockchip_pwm_config(chip, pwm, state);
 if (state->enabled != enabled) {
  ret = rockchip_pwm_enable(chip, pwm, state->enabled);
  if (ret)
   goto out;
 }

out:
 clk_disable(pc->clk);
 clk_disable(pc->pclk);

 return ret;
}

static const struct pwm_ops rockchip_pwm_ops = {
 .get_state = rockchip_pwm_get_state,
 .apply = rockchip_pwm_apply,
};

static const struct rockchip_pwm_data pwm_data_v1 = {
 .regs = {
  .duty = 0x04,
  .period = 0x08,
  .cntr = 0x00,
  .ctrl = 0x0c,
 },
 .prescaler = 2,
 .supports_polarity = false,
 .supports_lock = false,
 .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
};

static const struct rockchip_pwm_data pwm_data_v2 = {
 .regs = {
  .duty = 0x08,
  .period = 0x04,
  .cntr = 0x00,
  .ctrl = 0x0c,
 },
 .prescaler = 1,
 .supports_polarity = true,
 .supports_lock = false,
 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
         PWM_CONTINUOUS,
};

static const struct rockchip_pwm_data pwm_data_vop = {
 .regs = {
  .duty = 0x08,
  .period = 0x04,
  .cntr = 0x0c,
  .ctrl = 0x00,
 },
 .prescaler = 1,
 .supports_polarity = true,
 .supports_lock = false,
 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
         PWM_CONTINUOUS,
};

static const struct rockchip_pwm_data pwm_data_v3 = {
 .regs = {
  .duty = 0x08,
  .period = 0x04,
  .cntr = 0x00,
  .ctrl = 0x0c,
 },
 .prescaler = 1,
 .supports_polarity = true,
 .supports_lock = true,
 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
         PWM_CONTINUOUS,
};

static const struct of_device_id rockchip_pwm_dt_ids[] = {
 { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1},
 { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2},
 { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop},
 { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3},
 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);

static int rockchip_pwm_probe(struct platform_device *pdev)
{
 struct pwm_chip *chip;
 struct rockchip_pwm_chip *pc;
 u32 enable_conf, ctrl;
 bool enabled;
 int ret, count;

 chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pc));
 if (IS_ERR(chip))
  return PTR_ERR(chip);
 pc = to_rockchip_pwm_chip(chip);

 pc->base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(pc->base))
  return PTR_ERR(pc->base);

 pc->clk = devm_clk_get(&pdev->dev, "pwm");
 if (IS_ERR(pc->clk)) {
  pc->clk = devm_clk_get(&pdev->dev, NULL);
  if (IS_ERR(pc->clk))
   return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
          "Can't get PWM clk\n");
 }

 count = of_count_phandle_with_args(pdev->dev.of_node,
        "clocks""#clock-cells");
 if (count == 2)
  pc->pclk = devm_clk_get(&pdev->dev, "pclk");
 else
  pc->pclk = pc->clk;

 if (IS_ERR(pc->pclk))
  return dev_err_probe(&pdev->dev, PTR_ERR(pc->pclk), "Can't get APB clk\n");

 ret = clk_prepare_enable(pc->clk);
 if (ret)
  return dev_err_probe(&pdev->dev, ret, "Can't prepare enable PWM clk\n");

 ret = clk_prepare_enable(pc->pclk);
 if (ret) {
  dev_err_probe(&pdev->dev, ret, "Can't prepare enable APB clk\n");
  goto err_clk;
 }

 platform_set_drvdata(pdev, chip);

 pc->data = device_get_match_data(&pdev->dev);
 chip->ops = &rockchip_pwm_ops;

 enable_conf = pc->data->enable_conf;
 ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
 enabled = (ctrl & enable_conf) == enable_conf;

 ret = pwmchip_add(chip);
 if (ret < 0) {
  dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
  goto err_pclk;
 }

 /* Keep the PWM clk enabled if the PWM appears to be up and running. */
 if (!enabled)
  clk_disable(pc->clk);

 clk_disable(pc->pclk);

 return 0;

err_pclk:
 clk_disable_unprepare(pc->pclk);
err_clk:
 clk_disable_unprepare(pc->clk);

 return ret;
}

static void rockchip_pwm_remove(struct platform_device *pdev)
{
 struct pwm_chip *chip = platform_get_drvdata(pdev);
 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);

 pwmchip_remove(chip);

 clk_unprepare(pc->pclk);
 clk_unprepare(pc->clk);
}

static struct platform_driver rockchip_pwm_driver = {
 .driver = {
  .name = "rockchip-pwm",
  .of_match_table = rockchip_pwm_dt_ids,
 },
 .probe = rockchip_pwm_probe,
 .remove = rockchip_pwm_remove,
};
module_platform_driver(rockchip_pwm_driver);

MODULE_AUTHOR("Beniamino Galvani ");
MODULE_DESCRIPTION("Rockchip SoC PWM driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.