c
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define PWM_N_LOADCOUNT(pwm_n) (0x00 + pwm_n * 0x14)
#define PWM_N_CONTROL_REG(pwm_n) (0x08 + pwm_n * 0x14)
#define PWM_N_LOADCOUNT2(pwm_n) (0xB0 + pwm_n * 0x4)
#define PWM_ENABLE BIT(0)
#define PWM_USE_DEFINE_COUNT BIT(1)
#define PWM_MODR_ENABLE BIT(3)
#define RERIOD_CYCLES 40000000 // 40 MHz (FPGA bit)
#define REG_MAX_VAL 0xffffffff
struct pwm_temp_chip {
void __iomem *mmio_base;
// TODO:
};
static inline struct pwm_temp_chip *to_pwm_temp_chip(struct pwm_chip *chip)
{
return pwmchip_get_drvdata(chip);
}
static int temp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_temp_chip *temp =to_pwm_temp_chip(chip);
int val, pwm_n = pwm->hwpwm;
val = readl(temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
val |= (PWM_ENABLE | PWM_USE_DEFINE_COUNT);
writel(val, temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
return 0;
}
static void temp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_temp_chip *temp = to_pwm_temp_chip(chip);
int val, pwm_n = pwm->hwpwm;
val = readl(temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
val &= ~(PWM_ENABLE | PWM_USE_DEFINE_COUNT | PWM_MODR_ENABLE);
writel(val, temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
}
static int pwm_temp_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state)
{
struct pwm_temp_chip *temp = to_pwm_temp_chip(chip);
unsigned int val;
unsigned long period, duty;
int pwm_n = pwm->hwpwm;
val = readl(temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
state->enabled = val & 0x0001;
duty = readl(temp->mmio_base + PWM_N_LOADCOUNT2(pwm_n));
period = readl(temp->mmio_base + PWM_N_LOADCOUNT(pwm_n)) + duty;
state->duty_cycle = duty;
state->period = period;
printk("enable = %d, duty = %lld, period = %lld, %s\n",
state->enabled, state->duty_cycle, state->period, __FUNCTION__);
return 0;
}
static int pwm_temp_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state)
{
struct pwm_temp_chip *temp = to_pwm_temp_chip(chip);
int val, pwm_n = pwm->hwpwm;
unsigned long long tmp ,clk = RERIOD_CYCLES;
unsigned long period, duty;
if(state->enabled) {
temp_pwm_enable(chip, pwm);
} else {
temp_pwm_disable(chip, pwm);
return 0;
}
printk("enable = %d, duty = %lld, period = %lld, %s\n",
state->enabled, state->duty_cycle, state->period, __FUNCTION__);
tmp = clk * state->period;
do_div(tmp, NSEC_PER_SEC); // 64 位除法函数,结果保存到 tmp,返回值是余数
period = tmp; // (clk * period_ns) / NSEC_PER_SEC
if (period < 1) {
printk("%s %d\n", __FUNCTION__, __LINE__);
period = 1;
}
if (period > REG_MAX_VAL) { // 寄存器的最大值,采用 32 位模式
printk("%s %d\n", __FUNCTION__, __LINE__);
return -EINVAL;
}
tmp = clk * state->duty_cycle;
do_div(tmp, NSEC_PER_SEC);
duty = tmp; //(clk * duty_ns) / NSEC_PER_SEC
if (duty < 1) {
printk("%s %d\n", __FUNCTION__, __LINE__);
duty = 1;
}
if (duty > period ) {
printk("%s %d\n", __FUNCTION__, __LINE__);
return -EINVAL;
}
/*dw_apb_times_databook.pdf 3.3.7.1*/
val = readl(temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
val |= PWM_MODR_ENABLE;
writel(val, temp->mmio_base + PWM_N_CONTROL_REG(pwm_n));
writel(duty, temp->mmio_base + PWM_N_LOADCOUNT2(pwm_n));
writel(period - duty, temp->mmio_base + PWM_N_LOADCOUNT(pwm_n));
printk("duty = %ld, period = %ld, %s\n", duty, period, __FUNCTION__);
return 0;
}
static const struct pwm_ops pwm_temp_ops = {
.apply = pwm_temp_apply,
.get_state = pwm_temp_get_state,
};
static const struct of_device_id pwm_temp_dt_ids[] = {
{ .compatible = "pwm-temp", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pwm_temp_dt_ids);
static int pwm_temp_probe(struct platform_device *pdev)
{
struct pwm_chip *chip;
struct pwm_temp_chip *temp;
chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*temp));
if (IS_ERR(chip)) {
printk("%s %s %d\n",__FILE__, __FUNCTION__, __LINE__);
return PTR_ERR(chip);
}
chip->ops = &pwm_temp_ops;
temp = to_pwm_temp_chip(chip);
temp->mmio_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(temp->mmio_base)) {
printk("%s %s %d\n",__FILE__, __FUNCTION__, __LINE__);
return PTR_ERR(temp->mmio_base);
}
return devm_pwmchip_add(&pdev->dev, chip);
}
static struct platform_driver temp_pwm_driver = {
.driver = {
.name = "pwm-temp",
.of_match_table = pwm_temp_dt_ids,
},
.probe = pwm_temp_probe,
};
module_platform_driver(temp_pwm_driver);
MODULE_DESCRIPTION("Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Tanxujia <tanxujia@vanxum.com>");
设备树样例:
bash
pwm0:pwm@f1403000 {
compatible = "pwm-temp";
reg = <0x0 0xf1403000 0x0 0x1000>;
status = "disabled";
};
重点结构体:
struct pwm_ops
struct pwm_chip
重点函数:
devm_pwmchip_alloc
devm_pwmchip_add