diff options
Diffstat (limited to 'target/linux/xburst/patches-2.6.35/011-pwm.patch')
-rw-r--r-- | target/linux/xburst/patches-2.6.35/011-pwm.patch | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/target/linux/xburst/patches-2.6.35/011-pwm.patch b/target/linux/xburst/patches-2.6.35/011-pwm.patch new file mode 100644 index 0000000..3c8336b --- /dev/null +++ b/target/linux/xburst/patches-2.6.35/011-pwm.patch @@ -0,0 +1,197 @@ +From 5cd5a44b94b451ecaf593bb49919cfbb51ccb622 Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Sat, 17 Jul 2010 11:12:20 +0000 +Subject: [PATCH] MIPS: JZ4740: Add PWM support + +Add support for the PWM part of the timer unit on a JZ4740 SoC. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +Cc: linux-mips@linux-mips.org +Cc: linux-kernel@vger.kernel.org +Patchwork: https://patchwork.linux-mips.org/patch/1468/ +Signed-off-by: Ralf Baechle <ralf@linux-mips.org> +--- + arch/mips/jz4740/pwm.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 177 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/jz4740/pwm.c + +--- /dev/null ++++ b/arch/mips/jz4740/pwm.c +@@ -0,0 +1,177 @@ ++/* ++ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> ++ * JZ4740 platform PWM support ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/kernel.h> ++ ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/pwm.h> ++#include <linux/gpio.h> ++ ++#include <asm/mach-jz4740/gpio.h> ++#include "timer.h" ++ ++static struct clk *jz4740_pwm_clk; ++ ++DEFINE_MUTEX(jz4740_pwm_mutex); ++ ++struct pwm_device { ++ unsigned int id; ++ unsigned int gpio; ++ bool used; ++}; ++ ++static struct pwm_device jz4740_pwm_list[] = { ++ { 2, JZ_GPIO_PWM2, false }, ++ { 3, JZ_GPIO_PWM3, false }, ++ { 4, JZ_GPIO_PWM4, false }, ++ { 5, JZ_GPIO_PWM5, false }, ++ { 6, JZ_GPIO_PWM6, false }, ++ { 7, JZ_GPIO_PWM7, false }, ++}; ++ ++struct pwm_device *pwm_request(int id, const char *label) ++{ ++ int ret = 0; ++ struct pwm_device *pwm; ++ ++ if (id < 2 || id > 7 || !jz4740_pwm_clk) ++ return ERR_PTR(-ENODEV); ++ ++ mutex_lock(&jz4740_pwm_mutex); ++ ++ pwm = &jz4740_pwm_list[id - 2]; ++ if (pwm->used) ++ ret = -EBUSY; ++ else ++ pwm->used = true; ++ ++ mutex_unlock(&jz4740_pwm_mutex); ++ ++ if (ret) ++ return ERR_PTR(ret); ++ ++ ret = gpio_request(pwm->gpio, label); ++ ++ if (ret) { ++ printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret); ++ pwm->used = false; ++ return ERR_PTR(ret); ++ } ++ ++ jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_PWM); ++ ++ jz4740_timer_start(id); ++ ++ return pwm; ++} ++ ++void pwm_free(struct pwm_device *pwm) ++{ ++ pwm_disable(pwm); ++ jz4740_timer_set_ctrl(pwm->id, 0); ++ ++ jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_NONE); ++ gpio_free(pwm->gpio); ++ ++ jz4740_timer_stop(pwm->id); ++ ++ pwm->used = false; ++} ++ ++int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) ++{ ++ unsigned long long tmp; ++ unsigned long period, duty; ++ unsigned int prescaler = 0; ++ unsigned int id = pwm->id; ++ uint16_t ctrl; ++ bool is_enabled; ++ ++ if (duty_ns < 0 || duty_ns > period_ns) ++ return -EINVAL; ++ ++ tmp = (unsigned long long)clk_get_rate(jz4740_pwm_clk) * period_ns; ++ do_div(tmp, 1000000000); ++ period = tmp; ++ ++ while (period > 0xffff && prescaler < 6) { ++ period >>= 2; ++ ++prescaler; ++ } ++ ++ if (prescaler == 6) ++ return -EINVAL; ++ ++ tmp = (unsigned long long)period * duty_ns; ++ do_div(tmp, period_ns); ++ duty = period - tmp; ++ ++ if (duty >= period) ++ duty = period - 1; ++ ++ is_enabled = jz4740_timer_is_enabled(id); ++ if (is_enabled) ++ pwm_disable(pwm); ++ ++ jz4740_timer_set_count(id, 0); ++ jz4740_timer_set_duty(id, duty); ++ jz4740_timer_set_period(id, period); ++ ++ ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | ++ JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; ++ ++ jz4740_timer_set_ctrl(id, ctrl); ++ ++ if (is_enabled) ++ pwm_enable(pwm); ++ ++ return 0; ++} ++ ++int pwm_enable(struct pwm_device *pwm) ++{ ++ uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); ++ ++ ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; ++ jz4740_timer_set_ctrl(pwm->id, ctrl); ++ jz4740_timer_enable(pwm->id); ++ ++ return 0; ++} ++ ++void pwm_disable(struct pwm_device *pwm) ++{ ++ uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); ++ ++ ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; ++ jz4740_timer_disable(pwm->id); ++ jz4740_timer_set_ctrl(pwm->id, ctrl); ++} ++ ++static int __init jz4740_pwm_init(void) ++{ ++ int ret = 0; ++ ++ jz4740_pwm_clk = clk_get(NULL, "ext"); ++ ++ if (IS_ERR(jz4740_pwm_clk)) { ++ ret = PTR_ERR(jz4740_pwm_clk); ++ jz4740_pwm_clk = NULL; ++ } ++ ++ return ret; ++} ++subsys_initcall(jz4740_pwm_init); |