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