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:
Mikko Perttunen 2026-07-01 12:20:35 +09:00 committed by Uwe Kleine-König
parent b9a6f48de9
commit 82dcd68f72

View File

@ -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 |
* | | | dont 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
* 1b1. 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);