mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 16:10:02 +02:00
pwm: tegra: Add support for Tegra264
Tegra264 changes the register layout to accommodate wider fields for duty and scale, and adds configurable depth which will be supported in a later patch. The enable bit also moves from CSR_0 to a separate CSR_1 register. To support the new enable register location, introduce an enable_reg field in struct tegra_pwm_soc that identifies which register contains the PWM_ENABLE bit. tegra_pwm_enable() and tegra_pwm_disable() read/write this field accordingly, and tegra_pwm_config() skips OR-ing PWM_ENABLE into its CSR_0 write on SoCs where the enable bit is not in CSR_0. Update the top comment to describe the register layout in more detail. Co-developed-by: Yi-Wei Wang <yiweiw@nvidia.com> Signed-off-by: Yi-Wei Wang <yiweiw@nvidia.com> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com> Tested-by: Jon Hunter <jonathanh@nvidia.com> Reviewed-by: Jon Hunter <jonathanh@nvidia.com> Link: https://patch.msgid.link/20260701-t264-pwm-v6-6-2718f61f411f@nvidia.com Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
This commit is contained in:
parent
b9a6f48de9
commit
82dcd68f72
|
|
@ -7,23 +7,62 @@
|
|||
* Copyright (c) 2010-2020, NVIDIA Corporation.
|
||||
* Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
|
||||
*
|
||||
* Overview of Tegra Pulse Width Modulator Register:
|
||||
* 1. 13-bit: Frequency division (SCALE)
|
||||
* 2. 8-bit : Pulse division (DUTY)
|
||||
* 3. 1-bit : Enable bit
|
||||
* Overview of Tegra Pulse Width Modulator Register
|
||||
* CSR_0 of Tegra20, Tegra186, and Tegra194:
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | Bit | Field | Description |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 31 | ENB | Enable Pulse width modulator. |
|
||||
* | | | 0 = DISABLE, 1 = ENABLE. |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 30:16 | PWM_0 | Pulse width that needs to be programmed. |
|
||||
* | | | 0 = Always low. |
|
||||
* | | | 1 = 1 / 256 pulse high. |
|
||||
* | | | 2 = 2 / 256 pulse high. |
|
||||
* | | | N = N / 256 pulse high. |
|
||||
* | | | Only 8 bits are usable [23:16]. |
|
||||
* | | | Bit[24] can be programmed to 1 to achieve 100% duty |
|
||||
* | | | cycle. In this case the other bits [23:16] are set to |
|
||||
* | | | don’t care. |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 12:0 | PFM_0 | Frequency divider that needs to be programmed, also known |
|
||||
* | | | as SCALE. Division by (1 + PFM_0). |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
*
|
||||
* The PWM clock frequency is divided by 256 before subdividing it based
|
||||
* on the programmable frequency division value to generate the required
|
||||
* frequency for PWM output. The maximum output frequency that can be
|
||||
* achieved is (max rate of source clock) / 256.
|
||||
* e.g. if source clock rate is 408 MHz, maximum output frequency can be:
|
||||
* 408 MHz/256 = 1.6 MHz.
|
||||
* CSR_0 of Tegra264:
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | Bit | Field | Description |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 31:16 | PWM_0 | Pulse width that needs to be programmed. |
|
||||
* | | | 0 = Always low. |
|
||||
* | | | 1 = 1 / (1 + CSR_1.DEPTH) pulse high. |
|
||||
* | | | 2 = 2 / (1 + CSR_1.DEPTH) pulse high. |
|
||||
* | | | N = N / (1 + CSR_1.DEPTH) pulse high. |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 15:0 | PFM_0 | Frequency divider that needs to be programmed, also known |
|
||||
* | | | as SCALE. Division by (1 + PFM_0). |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
*
|
||||
* CSR_1 of Tegra264:
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | Bit | Field | Description |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 31 | ENB | Enable Pulse width modulator. |
|
||||
* | | | 0 = DISABLE, 1 = ENABLE. |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
* | 30:15 | DEPTH | Depth for pulse width modulator. This controls the pulse |
|
||||
* | | | time generated. Division by (1 + CSR_1.DEPTH). |
|
||||
* +-------+-------+-----------------------------------------------------------+
|
||||
*
|
||||
* The PWM clock frequency is divided by DEPTH = (1 + CSR_1.DEPTH) before
|
||||
* subdividing it based on the programmable frequency division value to
|
||||
* generate the required frequency for PWM output. DEPTH is fixed to 256
|
||||
* before Tegra264. The maximum output frequency that can be achieved is
|
||||
* (max rate of source clock) / DEPTH.
|
||||
* e.g. if source clock rate is 408 MHz, and DEPTH = 256, maximum output
|
||||
* frequency can be: 408 MHz / 256 ~= 1.6 MHz.
|
||||
* This 1.6 MHz frequency can further be divided using SCALE value in PWM.
|
||||
*
|
||||
* PWM pulse width: 8 bits are usable [23:16] for varying pulse width.
|
||||
* To achieve 100% duty cycle, program Bit [24] of this register to
|
||||
* 1’b1. In which case the other bits [23:16] are set to don't care.
|
||||
*
|
||||
* Limitations:
|
||||
* - When PWM is disabled, the output is driven to inactive.
|
||||
* - It does not allow the current PWM period to complete and
|
||||
|
|
@ -56,11 +95,13 @@
|
|||
#define TEGRA_PWM_SCALE_SHIFT 0
|
||||
|
||||
#define TEGRA_PWM_CSR_0 0
|
||||
#define TEGRA_PWM_CSR_1 4
|
||||
|
||||
#define TEGRA_PWM_DEPTH 256
|
||||
|
||||
struct tegra_pwm_soc {
|
||||
unsigned int num_channels;
|
||||
unsigned int enable_reg;
|
||||
|
||||
unsigned int scale_width;
|
||||
};
|
||||
|
|
@ -199,8 +240,9 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
err = pm_runtime_resume_and_get(pwmchip_parent(chip));
|
||||
if (err)
|
||||
return err;
|
||||
} else
|
||||
} else if (pc->soc->enable_reg == TEGRA_PWM_CSR_0) {
|
||||
val |= TEGRA_PWM_ENABLE;
|
||||
}
|
||||
|
||||
tegra_pwm_writel(pwm, TEGRA_PWM_CSR_0, val);
|
||||
|
||||
|
|
@ -215,6 +257,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
|
||||
int rc = 0;
|
||||
u32 val;
|
||||
|
||||
|
|
@ -222,20 +265,21 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
val = tegra_pwm_readl(pwm, TEGRA_PWM_CSR_0);
|
||||
val = tegra_pwm_readl(pwm, pc->soc->enable_reg);
|
||||
val |= TEGRA_PWM_ENABLE;
|
||||
tegra_pwm_writel(pwm, TEGRA_PWM_CSR_0, val);
|
||||
tegra_pwm_writel(pwm, pc->soc->enable_reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
|
||||
u32 val;
|
||||
|
||||
val = tegra_pwm_readl(pwm, TEGRA_PWM_CSR_0);
|
||||
val = tegra_pwm_readl(pwm, pc->soc->enable_reg);
|
||||
val &= ~TEGRA_PWM_ENABLE;
|
||||
tegra_pwm_writel(pwm, TEGRA_PWM_CSR_0, val);
|
||||
tegra_pwm_writel(pwm, pc->soc->enable_reg, val);
|
||||
|
||||
pm_runtime_put_sync(pwmchip_parent(chip));
|
||||
}
|
||||
|
|
@ -405,18 +449,27 @@ static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
|
|||
|
||||
static const struct tegra_pwm_soc tegra20_pwm_soc = {
|
||||
.num_channels = 4,
|
||||
.enable_reg = TEGRA_PWM_CSR_0,
|
||||
.scale_width = 13,
|
||||
};
|
||||
|
||||
static const struct tegra_pwm_soc tegra186_pwm_soc = {
|
||||
.num_channels = 1,
|
||||
.enable_reg = TEGRA_PWM_CSR_0,
|
||||
.scale_width = 13,
|
||||
};
|
||||
|
||||
static const struct tegra_pwm_soc tegra264_pwm_soc = {
|
||||
.num_channels = 1,
|
||||
.enable_reg = TEGRA_PWM_CSR_1,
|
||||
.scale_width = 16,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_pwm_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
|
||||
{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
|
||||
{ .compatible = "nvidia,tegra194-pwm", .data = &tegra186_pwm_soc },
|
||||
{ .compatible = "nvidia,tegra264-pwm", .data = &tegra264_pwm_soc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user