Date:2012-09-02 11:52:30 (10 years 8 months ago)
Author:Thierry Reding
Commit:9ed25f0ee4508b30de1619c081a9d6d75471bd0d
Message:pwm: Add Ingenic JZ4740 support

This commit moves the driver to drivers/pwm and converts it to the new
PWM framework.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Files: arch/mips/include/asm/mach-jz4740/platform.h (1 diff)
arch/mips/jz4740/Kconfig (1 diff)
arch/mips/jz4740/Makefile (1 diff)
arch/mips/jz4740/board-qi_lb60.c (2 diffs)
arch/mips/jz4740/platform.c (1 diff)
arch/mips/jz4740/pwm.c (1 diff)
drivers/pwm/Kconfig (2 diffs)
drivers/pwm/Makefile (1 diff)
drivers/pwm/core.c (1 diff)
drivers/pwm/pwm-jz4740.c (1 diff)

Change Details

arch/mips/include/asm/mach-jz4740/platform.h
3131extern struct platform_device jz4740_codec_device;
3232extern struct platform_device jz4740_adc_device;
3333extern struct platform_device jz4740_wdt_device;
34extern struct platform_device jz4740_pwm_device;
3435
3536void jz4740_serial_device_register(void);
3637
arch/mips/jz4740/Kconfig
1010    bool "Dingoo A320 game and media player"
1111
1212endchoice
13
14config HAVE_PWM
15    bool
arch/mips/jz4740/Makefile
55# Object file lists.
66
77obj-y += prom.o irq.o time.o reset.o setup.o dma.o \
8    gpio.o clock.o platform.o timer.o pwm.o serial.o
8    gpio.o clock.o platform.o timer.o serial.o
99
1010obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
1111
arch/mips/jz4740/board-qi_lb60.c
393393    .name = "pwm-beeper",
394394    .id = -1,
395395    .dev = {
396        .platform_data = (void *)4,
396        .platform_data = (void *)2,
397397    },
398398};
399399
...... 
436436    &jz4740_codec_device,
437437    &jz4740_rtc_device,
438438    &jz4740_adc_device,
439    &jz4740_pwm_device,
439440    &qi_lb60_gpio_keys,
440441    &qi_lb60_pwm_beeper,
441442    &qi_lb60_charger_device,
arch/mips/jz4740/platform.c
323323    .num_resources = ARRAY_SIZE(jz4740_wdt_resources),
324324    .resource = jz4740_wdt_resources,
325325};
326
327/* PWM */
328struct platform_device jz4740_pwm_device = {
329    .name = "jz4740-pwm",
330    .id = -1,
331};
arch/mips/jz4740/pwm.c
1/*
2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3 * JZ4740 platform PWM support
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * You should have received a copy of the GNU General Public License along
11 * with this program; if not, write to the Free Software Foundation, Inc.,
12 * 675 Mass Ave, Cambridge, MA 02139, USA.
13 *
14 */
15
16#include <linux/kernel.h>
17
18#include <linux/clk.h>
19#include <linux/err.h>
20#include <linux/pwm.h>
21#include <linux/gpio.h>
22
23#include <asm/mach-jz4740/gpio.h>
24#include "timer.h"
25
26static struct clk *jz4740_pwm_clk;
27
28DEFINE_MUTEX(jz4740_pwm_mutex);
29
30struct pwm_device {
31    unsigned int id;
32    unsigned int gpio;
33    bool used;
34};
35
36static struct pwm_device jz4740_pwm_list[] = {
37    { 2, JZ_GPIO_PWM2, false },
38    { 3, JZ_GPIO_PWM3, false },
39    { 4, JZ_GPIO_PWM4, false },
40    { 5, JZ_GPIO_PWM5, false },
41    { 6, JZ_GPIO_PWM6, false },
42    { 7, JZ_GPIO_PWM7, false },
43};
44
45struct pwm_device *pwm_request(int id, const char *label)
46{
47    int ret = 0;
48    struct pwm_device *pwm;
49
50    if (id < 2 || id > 7 || !jz4740_pwm_clk)
51        return ERR_PTR(-ENODEV);
52
53    mutex_lock(&jz4740_pwm_mutex);
54
55    pwm = &jz4740_pwm_list[id - 2];
56    if (pwm->used)
57        ret = -EBUSY;
58    else
59        pwm->used = true;
60
61    mutex_unlock(&jz4740_pwm_mutex);
62
63    if (ret)
64        return ERR_PTR(ret);
65
66    ret = gpio_request(pwm->gpio, label);
67
68    if (ret) {
69        printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret);
70        pwm->used = false;
71        return ERR_PTR(ret);
72    }
73
74    jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_PWM);
75
76    jz4740_timer_start(id);
77
78    return pwm;
79}
80
81void pwm_free(struct pwm_device *pwm)
82{
83    pwm_disable(pwm);
84    jz4740_timer_set_ctrl(pwm->id, 0);
85
86    jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_NONE);
87    gpio_free(pwm->gpio);
88
89    jz4740_timer_stop(pwm->id);
90
91    pwm->used = false;
92}
93
94int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
95{
96    unsigned long long tmp;
97    unsigned long period, duty;
98    unsigned int prescaler = 0;
99    unsigned int id = pwm->id;
100    uint16_t ctrl;
101    bool is_enabled;
102
103    if (duty_ns < 0 || duty_ns > period_ns)
104        return -EINVAL;
105
106    tmp = (unsigned long long)clk_get_rate(jz4740_pwm_clk) * period_ns;
107    do_div(tmp, 1000000000);
108    period = tmp;
109
110    while (period > 0xffff && prescaler < 6) {
111        period >>= 2;
112        ++prescaler;
113    }
114
115    if (prescaler == 6)
116        return -EINVAL;
117
118    tmp = (unsigned long long)period * duty_ns;
119    do_div(tmp, period_ns);
120    duty = period - tmp;
121
122    if (duty >= period)
123        duty = period - 1;
124
125    is_enabled = jz4740_timer_is_enabled(id);
126    if (is_enabled)
127        pwm_disable(pwm);
128
129    jz4740_timer_set_count(id, 0);
130    jz4740_timer_set_duty(id, duty);
131    jz4740_timer_set_period(id, period);
132
133    ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT |
134        JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN;
135
136    jz4740_timer_set_ctrl(id, ctrl);
137
138    if (is_enabled)
139        pwm_enable(pwm);
140
141    return 0;
142}
143
144int pwm_enable(struct pwm_device *pwm)
145{
146    uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
147
148    ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
149    jz4740_timer_set_ctrl(pwm->id, ctrl);
150    jz4740_timer_enable(pwm->id);
151
152    return 0;
153}
154
155void pwm_disable(struct pwm_device *pwm)
156{
157    uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
158
159    ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
160    jz4740_timer_disable(pwm->id);
161    jz4740_timer_set_ctrl(pwm->id, ctrl);
162}
163
164static int __init jz4740_pwm_init(void)
165{
166    int ret = 0;
167
168    jz4740_pwm_clk = clk_get(NULL, "ext");
169
170    if (IS_ERR(jz4740_pwm_clk)) {
171        ret = PTR_ERR(jz4740_pwm_clk);
172        jz4740_pwm_clk = NULL;
173    }
174
175    return ret;
176}
177subsys_initcall(jz4740_pwm_init);
drivers/pwm/Kconfig
11menuconfig PWM
22    bool "Pulse-Width Modulation (PWM) Support"
3    depends on !MACH_JZ4740 && !PUV3_PWM
3    depends on !PUV3_PWM
44    help
55      Generic Pulse-Width Modulation (PWM) support.
66
...... 
4747      To compile this driver as a module, choose M here: the module
4848      will be called pwm-imx.
4949
50config PWM_JZ4740
51    tristate "Ingenic JZ4740 PWM support"
52    depends on MACH_JZ4740
53    help
54      Generic PWM framework driver for Ingenic JZ4740 based
55      machines.
56
57      To compile this driver as a module, choose M here: the module
58      will be called pwm-jz4740.
59
5060config PWM_LPC32XX
5161    tristate "LPC32XX PWM support"
5262    depends on ARCH_LPC32XX
drivers/pwm/Makefile
11obj-$(CONFIG_PWM) += core.o
22obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
33obj-$(CONFIG_PWM_IMX) += pwm-imx.o
4obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
45obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
56obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
67obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
drivers/pwm/core.c
371371 */
372372int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
373373{
374    if (!pwm || period_ns == 0 || duty_ns > period_ns)
374    if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
375375        return -EINVAL;
376376
377377    return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
drivers/pwm/pwm-jz4740.c
1/*
2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3 * JZ4740 platform PWM support
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * You should have received a copy of the GNU General Public License along
11 * with this program; if not, write to the Free Software Foundation, Inc.,
12 * 675 Mass Ave, Cambridge, MA 02139, USA.
13 *
14 */
15
16#include <linux/clk.h>
17#include <linux/err.h>
18#include <linux/gpio.h>
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/pwm.h>
23
24#include <asm/mach-jz4740/gpio.h>
25#include <timer.h>
26
27#define NUM_PWM 6
28
29static const unsigned int jz4740_pwm_gpio_list[NUM_PWM] = {
30    JZ_GPIO_PWM2,
31    JZ_GPIO_PWM3,
32    JZ_GPIO_PWM4,
33    JZ_GPIO_PWM5,
34    JZ_GPIO_PWM6,
35    JZ_GPIO_PWM7,
36};
37
38struct jz4740_pwm_chip {
39    struct pwm_chip chip;
40    struct clk *clk;
41};
42
43static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
44{
45    return container_of(chip, struct jz4740_pwm_chip, chip);
46}
47
48static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
49{
50    unsigned int gpio = jz4740_pwm_gpio_list[pwm->hwpwm];
51    int ret;
52
53    ret = gpio_request(gpio, pwm->label);
54
55    if (ret) {
56        printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret);
57        return ret;
58    }
59
60    jz_gpio_set_function(gpio, JZ_GPIO_FUNC_PWM);
61
62    jz4740_timer_start(pwm->hwpwm);
63
64    return 0;
65}
66
67static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
68{
69    unsigned int gpio = jz4740_pwm_gpio_list[pwm->hwpwm];
70
71    jz4740_timer_set_ctrl(pwm->hwpwm, 0);
72
73    jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE);
74    gpio_free(gpio);
75
76    jz4740_timer_stop(pwm->hwpwm);
77}
78
79static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
80                 int duty_ns, int period_ns)
81{
82    struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
83    unsigned long long tmp;
84    unsigned long period, duty;
85    unsigned int prescaler = 0;
86    uint16_t ctrl;
87    bool is_enabled;
88
89    tmp = (unsigned long long)clk_get_rate(jz4740->clk) * period_ns;
90    do_div(tmp, 1000000000);
91    period = tmp;
92
93    while (period > 0xffff && prescaler < 6) {
94        period >>= 2;
95        ++prescaler;
96    }
97
98    if (prescaler == 6)
99        return -EINVAL;
100
101    tmp = (unsigned long long)period * duty_ns;
102    do_div(tmp, period_ns);
103    duty = period - tmp;
104
105    if (duty >= period)
106        duty = period - 1;
107
108    is_enabled = jz4740_timer_is_enabled(pwm->hwpwm);
109    if (is_enabled)
110        pwm_disable(pwm);
111
112    jz4740_timer_set_count(pwm->hwpwm, 0);
113    jz4740_timer_set_duty(pwm->hwpwm, duty);
114    jz4740_timer_set_period(pwm->hwpwm, period);
115
116    ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT |
117        JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN;
118
119    jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
120
121    if (is_enabled)
122        pwm_enable(pwm);
123
124    return 0;
125}
126
127static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
128{
129    uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm);
130
131    ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
132    jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
133    jz4740_timer_enable(pwm->hwpwm);
134
135    return 0;
136}
137
138static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
139{
140    uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm);
141
142    ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
143    jz4740_timer_disable(pwm->hwpwm);
144    jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
145}
146
147static const struct pwm_ops jz4740_pwm_ops = {
148    .request = jz4740_pwm_request,
149    .free = jz4740_pwm_free,
150    .config = jz4740_pwm_config,
151    .enable = jz4740_pwm_enable,
152    .disable = jz4740_pwm_disable,
153};
154
155static int jz4740_pwm_probe(struct platform_device *pdev)
156{
157    struct jz4740_pwm_chip *jz4740;
158    int ret = 0;
159
160    jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL);
161    if (!jz4740)
162        return -ENOMEM;
163
164    jz4740->clk = clk_get(NULL, "ext");
165    if (IS_ERR(jz4740->clk))
166        return PTR_ERR(jz4740->clk);
167
168    jz4740->chip.dev = &pdev->dev;
169    jz4740->chip.ops = &jz4740_pwm_ops;
170    jz4740->chip.npwm = NUM_PWM;
171    jz4740->chip.base = -1;
172
173    ret = pwmchip_add(&jz4740->chip);
174    if (ret < 0) {
175        clk_put(jz4740->clk);
176        return ret;
177    }
178
179    platform_set_drvdata(pdev, jz4740);
180
181    return 0;
182}
183
184static int jz4740_pwm_remove(struct platform_device *pdev)
185{
186    struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev);
187    int ret;
188
189    ret = pwmchip_remove(&jz4740->chip);
190    if (ret < 0)
191        return ret;
192
193    clk_put(jz4740->clk);
194
195    return 0;
196}
197
198static struct platform_driver jz4740_pwm_driver = {
199    .driver = {
200        .name = "jz4740-pwm",
201    },
202    .probe = jz4740_pwm_probe,
203    .remove = jz4740_pwm_remove,
204};
205module_platform_driver(jz4740_pwm_driver);

Archive Download the corresponding diff file



interactive