Message ID | 1391061145-2078-1-git-send-email-21cnbao@gmail.com |
---|---|
State | Superseded |
Headers | show |
On Thursday 30 January 2014, Barry Song wrote: > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig > index 7acab93..0a252f8 100644 > --- a/drivers/pwm/Kconfig > +++ b/drivers/pwm/Kconfig > @@ -166,6 +166,15 @@ config PWM_SAMSUNG > To compile this driver as a module, choose M here: the module > will be called pwm-samsung. > > +config PWM_SIRF > + tristate "SiRF PWM support" > + depends on ARCH_SIRF > + help please make this "depends on ARCH_SIRF || COMPILE_TEST" if you can, so we can build it on other platforms for test purposes. If you do this, you have to list the full set of dependencies, so probably another "depends on HAVE_CLK". > + > +#define to_sirf_chip(chip) container_of(chip, struct sirf_pwm, chip) > + > +static unsigned int sirf_pwm_ns_to_cycles(struct pwm_chip *chip, unsigned int time_ns) > +{ > + u64 dividend; > + unsigned int cycle; > + /* > + * on SiRFSoC, OSC input is const, we use it as the source to generate > + * PWM wave > + */ > +#define SRC_OSC_RATE 26000000ULL > + dividend = SRC_OSC_RATE * time_ns + NSEC_PER_SEC / 2; > + do_div(dividend, NSEC_PER_SEC); > + > + cycle = dividend & 0xFFFFFFFFUL; > + > + return cycle > 1 ? cycle : 1; > +} > + Is SRC_OSC_RATE the rate of spwm->clk? If so, it would be nice to just call clk_get_rate() here, in case you ever have a chip with a different rate. This is a very nice driver otherwise! Arnd -- To unsubscribe from this list: send the line "unsubscribe linux-pwm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
2014-01-31 Arnd Bergmann <arnd@arndb.de>: > On Thursday 30 January 2014, Barry Song wrote: >> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig >> index 7acab93..0a252f8 100644 >> --- a/drivers/pwm/Kconfig >> +++ b/drivers/pwm/Kconfig >> @@ -166,6 +166,15 @@ config PWM_SAMSUNG >> To compile this driver as a module, choose M here: the module >> will be called pwm-samsung. >> >> +config PWM_SIRF >> + tristate "SiRF PWM support" >> + depends on ARCH_SIRF >> + help > > please make this "depends on ARCH_SIRF || COMPILE_TEST" if you can, so we > can build it on other platforms for test purposes. If you do this, you > have to list the full set of dependencies, so probably another > "depends on HAVE_CLK". sounds good. it seems we can have this for other PWM drivers as well? > >> + >> +#define to_sirf_chip(chip) container_of(chip, struct sirf_pwm, chip) >> + >> +static unsigned int sirf_pwm_ns_to_cycles(struct pwm_chip *chip, unsigned int time_ns) >> +{ >> + u64 dividend; >> + unsigned int cycle; >> + /* >> + * on SiRFSoC, OSC input is const, we use it as the source to generate >> + * PWM wave >> + */ >> +#define SRC_OSC_RATE 26000000ULL >> + dividend = SRC_OSC_RATE * time_ns + NSEC_PER_SEC / 2; >> + do_div(dividend, NSEC_PER_SEC); >> + >> + cycle = dividend & 0xFFFFFFFFUL; >> + >> + return cycle > 1 ? cycle : 1; >> +} >> + > > Is SRC_OSC_RATE the rate of spwm->clk? If so, it would be nice to just call > clk_get_rate() here, in case you ever have a chip with a different rate. > SRC_OSC_RATE is the fixed frequency of crystal oscillator, but spwm->clk comes from the IO bus. the design is a little strange, pwm channels don't use the clock of PWM controller to generate period/duty, but use other sources. > This is a very nice driver otherwise! > > Arnd -barry -- To unsubscribe from this list: send the line "unsubscribe linux-pwm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Friday 31 January 2014, Barry Song wrote: > > > > Is SRC_OSC_RATE the rate of spwm->clk? If so, it would be nice to just call > > clk_get_rate() here, in case you ever have a chip with a different rate. > > > > SRC_OSC_RATE is the fixed frequency of crystal oscillator, but > spwm->clk comes from the IO bus. the design is a little strange, pwm > channels don't use the clock of PWM controller to generate > period/duty, but use other sources. How about modeling that other source as a fixed-rate clock in DT then? Arnd -- To unsubscribe from this list: send the line "unsubscribe linux-pwm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
2014-01-31 23:23 GMT+08:00 Arnd Bergmann <arnd@arndb.de>: > On Friday 31 January 2014, Barry Song wrote: >> > >> > Is SRC_OSC_RATE the rate of spwm->clk? If so, it would be nice to just call >> > clk_get_rate() here, in case you ever have a chip with a different rate. >> > >> >> SRC_OSC_RATE is the fixed frequency of crystal oscillator, but >> spwm->clk comes from the IO bus. the design is a little strange, pwm >> channels don't use the clock of PWM controller to generate >> period/duty, but use other sources. > > How about modeling that other source as a fixed-rate clock in DT > then? sirfsoc clock drivers have a clock node for OSC whose index is "1". do you think the following is the right way to handle? in dts, put both pwm controller clock and OSC 672 pwm: pwm@b0130000 { 673 compatible = "sirf,prima2-pwm"; 674 #pwm-cells = <2>; 675 reg = <0xb0130000 0x10000>; 676 clocks = <&clks 21>, <&clks 1>; 677 clock-names = "pwmc", "osc"; 678 }; and in pwm-sirf.c driver, use clk = clk_get(dev, "osc"); clk_get_rate(clk); to get the rate in probe()? > > Arnd -barry -- To unsubscribe from this list: send the line "unsubscribe linux-pwm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thursday 06 February 2014, Barry Song wrote: > > How about modeling that other source as a fixed-rate clock in DT > > then? > > sirfsoc clock drivers have a clock node for OSC whose index is "1". > do you think the following is the right way to handle? > > in dts, put both pwm controller clock and OSC > 672 pwm: pwm@b0130000 { > 673 compatible = "sirf,prima2-pwm"; > 674 #pwm-cells = <2>; > 675 reg = <0xb0130000 0x10000>; > 676 clocks = <&clks 21>, <&clks 1>; > 677 clock-names = "pwmc", "osc"; > 678 }; > > and in pwm-sirf.c driver, use > clk = clk_get(dev, "osc"); > clk_get_rate(clk); > > to get the rate in probe()? Ah, if that's the right clock, it sounds great, yes. Just make sure that the clock-names values make sense from the point of view of the pwm node, rather than referring to the name given in the clock provider. Arnd -- To unsubscribe from this list: send the line "unsubscribe linux-pwm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
2014-02-06 23:27 GMT+08:00 Arnd Bergmann <arnd@arndb.de>: > On Thursday 06 February 2014, Barry Song wrote: >> > How about modeling that other source as a fixed-rate clock in DT >> > then? >> >> sirfsoc clock drivers have a clock node for OSC whose index is "1". >> do you think the following is the right way to handle? >> >> in dts, put both pwm controller clock and OSC >> 672 pwm: pwm@b0130000 { >> 673 compatible = "sirf,prima2-pwm"; >> 674 #pwm-cells = <2>; >> 675 reg = <0xb0130000 0x10000>; >> 676 clocks = <&clks 21>, <&clks 1>; >> 677 clock-names = "pwmc", "osc"; >> 678 }; >> >> and in pwm-sirf.c driver, use >> clk = clk_get(dev, "osc"); >> clk_get_rate(clk); >> >> to get the rate in probe()? > > Ah, if that's the right clock, it sounds great, yes. > > Just make sure that the clock-names values make sense from the > point of view of the pwm node, rather than referring to the > name given in the clock provider. from the point of clock provider, it is "osc", from the view of pwm, it is the clock source of PWM waves. so i guess "clk_pwm_source" is preferred? > > Arnd -barry -- To unsubscribe from this list: send the line "unsubscribe linux-pwm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 7acab93..0a252f8 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -166,6 +166,15 @@ config PWM_SAMSUNG To compile this driver as a module, choose M here: the module will be called pwm-samsung. +config PWM_SIRF + tristate "SiRF PWM support" + depends on ARCH_SIRF + help + Generic PWM framework driver for SiRF SoC. + + To compile this driver as a module, choose M here: the module + will be called pwm-sirf. + config PWM_SPEAR tristate "STMicroelectronics SPEAr PWM support" depends on PLAT_SPEAR diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 4abf337..49ab0d7 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o +obj-$(CONFIG_PWM_SIRF) += pwm-sirf.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o diff --git a/drivers/pwm/pwm-sirf.c b/drivers/pwm/pwm-sirf.c new file mode 100644 index 0000000..1a5c5b7 --- /dev/null +++ b/drivers/pwm/pwm-sirf.c @@ -0,0 +1,294 @@ +/* + * SIRF serial SoC PWM device core driver + * + * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/pwm.h> +#include <linux/of.h> +#include <linux/io.h> + +#define SIRF_PWM_SELECT_PRECLK 0x0 +#define SIRF_PWM_OE 0x4 +#define SIRF_PWM_ENABLE_PRECLOCK 0x8 +#define SIRF_PWM_ENABLE_POSTCLOCK 0xC +#define SIRF_PWM_GET_WAIT_OFFSET(n) (0x10 + 0x8*n) +#define SIRF_PWM_GET_HOLD_OFFSET(n) (0x14 + 0x8*n) + +#define SIRF_PWM_TR_STEP(n) (0x48 + 0x8*n) +#define SIRF_PWM_STEP_HOLD(n) (0x4c + 0x8*n) + +#define SRC_FIELD_SIZE 3 +#define BYPASS_MODE_BIT 21 +#define TRANS_MODE_SELECT_BIT 7 + +#define SIRF_PWM_CHL_NUM 7 +#define SIRF_PWM_BLS_GRP_NUM 16 + +struct sirf_pwm { + void __iomem *base; + struct clk *clk; + struct pwm_chip chip; +}; + +#define to_sirf_chip(chip) container_of(chip, struct sirf_pwm, chip) + +static unsigned int sirf_pwm_ns_to_cycles(struct pwm_chip *chip, unsigned int time_ns) +{ + u64 dividend; + unsigned int cycle; + /* + * on SiRFSoC, OSC input is const, we use it as the source to generate + * PWM wave + */ +#define SRC_OSC_RATE 26000000ULL + dividend = SRC_OSC_RATE * time_ns + NSEC_PER_SEC / 2; + do_div(dividend, NSEC_PER_SEC); + + cycle = dividend & 0xFFFFFFFFUL; + + return cycle > 1 ? cycle : 1; +} + +static int sirf_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + unsigned int period_cycles, high_cycles, low_cycles; + unsigned int val; + struct sirf_pwm *spwm = to_sirf_chip(chip); + + period_cycles = sirf_pwm_ns_to_cycles(chip, period_ns); + + high_cycles = sirf_pwm_ns_to_cycles(chip, duty_ns); + low_cycles = period_cycles - high_cycles; + + if (period_cycles == 1) { + /* bypass mode */ + val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK); + val |= 0x1 << (BYPASS_MODE_BIT + pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK); + dev_warn(chip->dev, "period is too short!\n"); + } else { + /* divider mode */ + val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK); + val &= ~(0x1 << (BYPASS_MODE_BIT + pwm->hwpwm)); + writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK); + + if (high_cycles == period_cycles) { + high_cycles--; + low_cycles = 1; + } + + writel(high_cycles, spwm->base + SIRF_PWM_GET_WAIT_OFFSET(pwm->hwpwm)); + writel(low_cycles, spwm->base + SIRF_PWM_GET_HOLD_OFFSET(pwm->hwpwm)); + } + + return 0; +} + +static int sirf_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct sirf_pwm *spwm = to_sirf_chip(chip); + unsigned int val; + + /* disable preclock */ + val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK); + val &= ~(1 << pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK); + + /* select preclock source must after disable preclk*/ + val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK); + val &= ~(0x7 << (SRC_FIELD_SIZE * pwm->hwpwm)); + writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK); + /* wait for some time */ + udelay(100); + + /* enable preclock */ + val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK); + val |= (1 << pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK); + + /* enable post clock*/ + val = readl(spwm->base + SIRF_PWM_ENABLE_POSTCLOCK); + val |= (1 << pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_ENABLE_POSTCLOCK); + + /* enable output */ + val = readl(spwm->base + SIRF_PWM_OE); + val |= 1 << pwm->hwpwm; + val &= ~(1 << (pwm->hwpwm + TRANS_MODE_SELECT_BIT)); + + writel(val, spwm->base + SIRF_PWM_OE); + + return 0; +} + +static void sirf_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + unsigned int val; + struct sirf_pwm *spwm = to_sirf_chip(chip); + /* disable output */ + val = readl(spwm->base + SIRF_PWM_OE); + val &= ~(1 << pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_OE); + + /* disable postclock */ + val = readl(spwm->base + SIRF_PWM_ENABLE_POSTCLOCK); + val &= ~(1 << pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_ENABLE_POSTCLOCK); + + /* disable preclock */ + val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK); + val &= ~(1 << pwm->hwpwm); + writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK); +} + +static struct pwm_ops sirf_pwm_ops = { + .enable = sirf_pwm_enable, + .disable = sirf_pwm_disable, + .config = sirf_pwm_config, + .owner = THIS_MODULE, +}; + +static int sirf_pwm_probe(struct platform_device *pdev) +{ + struct sirf_pwm *spwm; + struct resource *mem_res; + int ret; + + spwm = devm_kzalloc(&pdev->dev, sizeof(struct sirf_pwm), + GFP_KERNEL); + if (!spwm) + return -ENOMEM; + + platform_set_drvdata(pdev, spwm); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spwm->base = devm_ioremap_resource(&pdev->dev, mem_res); + if (!spwm->base) + return -ENOMEM; + + spwm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(spwm->clk)) { + dev_err(&pdev->dev, "Get clock failed.\n"); + return PTR_ERR(spwm->clk); + } + + clk_prepare_enable(spwm->clk); + + spwm->chip.dev = &pdev->dev; + spwm->chip.ops = &sirf_pwm_ops; + spwm->chip.base = 0; + spwm->chip.npwm = SIRF_PWM_CHL_NUM; + + ret = pwmchip_add(&spwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm\n"); + clk_disable_unprepare(spwm->clk); + return ret; + } + + return 0; +} + +static int sirf_pwm_remove(struct platform_device *pdev) +{ + struct sirf_pwm *spwm = platform_get_drvdata(pdev); + clk_disable_unprepare(spwm->clk); + + pwmchip_remove(&spwm->chip); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sirf_pwm_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sirf_pwm *spwm = platform_get_drvdata(pdev); + + clk_disable_unprepare(spwm->clk); + + return 0; +} + +static void sirf_pwm_config_restore(struct sirf_pwm *spwm) +{ + struct pwm_device *pwm; + int i; + + for (i = 0; i < spwm->chip.npwm; i++) { + pwm = &spwm->chip.pwms[i]; + /* + * while restoring from hibernation, state of pwm is enabled, + * but PWM hardware is not re-enabled + */ + if (test_bit(PWMF_REQUESTED, &pwm->flags) && + test_bit(PWMF_ENABLED, &pwm->flags)) + sirf_pwm_enable(&spwm->chip, pwm); + } +} + +static int sirf_pwm_resume(struct device *dev) +{ + struct sirf_pwm *spwm = dev_get_drvdata(dev); + + clk_prepare_enable(spwm->clk); + + sirf_pwm_config_restore(spwm); + + return 0; +} + +static int sirf_pwm_restore(struct device *dev) +{ + struct sirf_pwm *spwm = dev_get_drvdata(dev); + + /* back from hibernation, clock is already enabled */ + sirf_pwm_config_restore(spwm); + + return 0; +} + +#else +#define sirf_pwm_resume NULL +#define sirf_pwm_suspend NULL +#define sirf_pwm_restore NULL +#endif + +static const struct dev_pm_ops sirf_pwm_pm_ops = { + .suspend = sirf_pwm_suspend, + .resume = sirf_pwm_resume, + .restore = sirf_pwm_restore, +}; + +static const struct of_device_id sirf_pwm_of_match[] = { + { .compatible = "sirf,prima2-pwm", }, + {} +}; +MODULE_DEVICE_TABLE(of, sirf_pwm_of_match); + +static struct platform_driver sirf_pwm_driver = { + .driver = { + .name = "prima2-pwm", + .owner = THIS_MODULE, + .pm = &sirf_pwm_pm_ops, + .of_match_table = sirf_pwm_of_match, + }, + .probe = sirf_pwm_probe, + .remove = sirf_pwm_remove, +}; + +module_platform_driver(sirf_pwm_driver); + +MODULE_DESCRIPTION("SIRF serial SoC PWM device core driver"); +MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>, " + "Huayi Li <huayi.li@csr.com>"); +MODULE_LICENSE("GPL v2");