pwm驱动样例

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