前言:
由于网络上关于mt7628和mt7688 pwm相关的资料非常少,所以只能自己慢慢琢磨。虽然研究的心得没有多少,但是其实也是费了不少精力,希望转载文章时,保留出处。佐须之男,专业从事网络设备(路由器)等嵌入式开发和技术咨询工作。
#include <linux/err.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> #include <linux/types.h> #define NUM_PWM 4 /* mt7628/mt7688 共有4路原生PWM分别是PWM0,PWM1,PWM3,PWM3。 其中PWM2和PWM3和UART1复用,在使用PWM2和PWM3时需要关闭复用。 */ /* PWM registers and bits definitions */ #define PWMCON 0x00 /* PWM Conntrol 寄存器 */ #define PWMHDUR 0x04 /* PWM High Duration 寄存器 */ #define PWMLDUR 0x08 /* PWM Low Duration 寄存器 */ #define PWMGDUR 0x0c /* PWM Guard Duration 寄存器 */ #define PWMWAVENUM 0x28 /* PWM Wave Num 寄存器 */ #define PWMDWIDTH 0x2c /* PWM Data Width 寄存器 */ #define PWMTHRES 0x30 /* PWM Thresh 寄存器 */ /** * struct mtk_pwm_chip - struct representing pwm chip * * @mmio_base: base address of pwm chip * @chip: linux pwm chip representation */ struct mtk_pwm_chip { void __iomem *mmio_base; struct pwm_chip chip; }; static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) { return container_of(chip, struct mtk_pwm_chip, chip); } static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, unsigned long offset) { return ioread32(chip->mmio_base + 0x10 + (num * 0x40) + offset); } static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip, unsigned int num, unsigned long offset, unsigned long val) { iowrite32(val, chip->mmio_base + 0x10 + (num * 0x40) + offset); } static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); u32 resolution = 100 / 4; /* 由于PWM是由40MHZ基准频率分频所得,所以PWM的频率是25ns的倍数关系 perdios_ns = 25*2^clkdiv 0 <= clkdiv <=7 */ u32 clkdiv = 0; /* 分频倍数分别是 0 1 2 4 8 16 32 64 128 , 所以 0 <= clkdiv <=7 */ while (period_ns / resolution > 8191) { clkdiv++; resolution *= 2; } if (clkdiv > 7) return -1; mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv); /* 设置控制寄存器,设置基准频率是40MHZ和分频倍数 */ mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); /* 设置总周期,T */ mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); /* 设置上升沿周期t,占空比 = t/T*100 */ return 0; } static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); u32 val; val = ioread32(pc->mmio_base); val |= BIT(pwm->hwpwm); iowrite32(val, pc->mmio_base); return 0; } static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); u32 val; val = ioread32(pc->mmio_base); val &= ~BIT(pwm->hwpwm); iowrite32(val, pc->mmio_base); } static const struct pwm_ops mtk_pwm_ops = { .config = mtk_pwm_config, .enable = mtk_pwm_enable, .disable = mtk_pwm_disable, .owner = THIS_MODULE, }; static int mtk_pwm_probe(struct platform_device *pdev) { struct mtk_pwm_chip *pc; struct resource *r; int ret; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) return -ENOMEM; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); pc->mmio_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(pc->mmio_base)) return PTR_ERR(pc->mmio_base); platform_set_drvdata(pdev, pc); pc->chip.dev = &pdev->dev; pc->chip.ops = &mtk_pwm_ops; pc->chip.base = -1; pc->chip.npwm = NUM_PWM; ret = pwmchip_add(&pc->chip); if (ret < 0) dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); return ret; } static int mtk_pwm_remove(struct platform_device *pdev) { struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); int i; for (i = 0; i < NUM_PWM; i++) pwm_disable(&pc->chip.pwms[i]); return pwmchip_remove(&pc->chip); } static const struct of_device_id mtk_pwm_of_match[] = { { .compatible = "mediatek,mt7628-pwm" }, { } }; MODULE_DEVICE_TABLE(of, mtk_pwm_of_match); static struct platform_driver mtk_pwm_driver = { .driver = { .name = "mtk-pwm", .owner = THIS_MODULE, .of_match_table = mtk_pwm_of_match, }, .probe = mtk_pwm_probe, .remove = mtk_pwm_remove, }; module_platform_driver(mtk_pwm_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); MODULE_ALIAS("platform:mtk-pwm");