Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.7-rc7 150 lines 3.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/arch/unicore32/kernel/pwm.c 4 * 5 * Code specific to PKUnity SoC and UniCore ISA 6 * 7 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> 8 * Copyright (C) 2001-2010 Guan Xuetao 9 */ 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/platform_device.h> 14#include <linux/slab.h> 15#include <linux/err.h> 16#include <linux/clk.h> 17#include <linux/io.h> 18#include <linux/pwm.h> 19 20#include <asm/div64.h> 21#include <mach/hardware.h> 22 23struct puv3_pwm_chip { 24 struct pwm_chip chip; 25 void __iomem *base; 26 struct clk *clk; 27}; 28 29static inline struct puv3_pwm_chip *to_puv3(struct pwm_chip *chip) 30{ 31 return container_of(chip, struct puv3_pwm_chip, chip); 32} 33 34/* 35 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE 36 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE 37 */ 38static int puv3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 39 int duty_ns, int period_ns) 40{ 41 unsigned long period_cycles, prescale, pv, dc; 42 struct puv3_pwm_chip *puv3 = to_puv3(chip); 43 unsigned long long c; 44 45 c = clk_get_rate(puv3->clk); 46 c = c * period_ns; 47 do_div(c, 1000000000); 48 period_cycles = c; 49 50 if (period_cycles < 1) 51 period_cycles = 1; 52 53 prescale = (period_cycles - 1) / 1024; 54 pv = period_cycles / (prescale + 1) - 1; 55 56 if (prescale > 63) 57 return -EINVAL; 58 59 if (duty_ns == period_ns) 60 dc = OST_PWMDCCR_FDCYCLE; 61 else 62 dc = (pv + 1) * duty_ns / period_ns; 63 64 /* 65 * NOTE: the clock to PWM has to be enabled first 66 * before writing to the registers 67 */ 68 clk_prepare_enable(puv3->clk); 69 70 writel(prescale, puv3->base + OST_PWM_PWCR); 71 writel(pv - dc, puv3->base + OST_PWM_DCCR); 72 writel(pv, puv3->base + OST_PWM_PCR); 73 74 clk_disable_unprepare(puv3->clk); 75 76 return 0; 77} 78 79static int puv3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 80{ 81 struct puv3_pwm_chip *puv3 = to_puv3(chip); 82 83 return clk_prepare_enable(puv3->clk); 84} 85 86static void puv3_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 87{ 88 struct puv3_pwm_chip *puv3 = to_puv3(chip); 89 90 clk_disable_unprepare(puv3->clk); 91} 92 93static const struct pwm_ops puv3_pwm_ops = { 94 .config = puv3_pwm_config, 95 .enable = puv3_pwm_enable, 96 .disable = puv3_pwm_disable, 97 .owner = THIS_MODULE, 98}; 99 100static int pwm_probe(struct platform_device *pdev) 101{ 102 struct puv3_pwm_chip *puv3; 103 struct resource *r; 104 int ret; 105 106 puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL); 107 if (!puv3) 108 return -ENOMEM; 109 110 puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK"); 111 if (IS_ERR(puv3->clk)) 112 return PTR_ERR(puv3->clk); 113 114 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 115 puv3->base = devm_ioremap_resource(&pdev->dev, r); 116 if (IS_ERR(puv3->base)) 117 return PTR_ERR(puv3->base); 118 119 puv3->chip.dev = &pdev->dev; 120 puv3->chip.ops = &puv3_pwm_ops; 121 puv3->chip.base = -1; 122 puv3->chip.npwm = 1; 123 124 ret = pwmchip_add(&puv3->chip); 125 if (ret < 0) { 126 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 127 return ret; 128 } 129 130 platform_set_drvdata(pdev, puv3); 131 return 0; 132} 133 134static int pwm_remove(struct platform_device *pdev) 135{ 136 struct puv3_pwm_chip *puv3 = platform_get_drvdata(pdev); 137 138 return pwmchip_remove(&puv3->chip); 139} 140 141static struct platform_driver puv3_pwm_driver = { 142 .driver = { 143 .name = "PKUnity-v3-PWM", 144 }, 145 .probe = pwm_probe, 146 .remove = pwm_remove, 147}; 148module_platform_driver(puv3_pwm_driver); 149 150MODULE_LICENSE("GPL v2");