diff options
Diffstat (limited to 'arch/arm/plat-mxc/pwm.c')
-rw-r--r-- | arch/arm/plat-mxc/pwm.c | 144 |
1 files changed, 62 insertions, 82 deletions
diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index 9bffbc507cc2..ae34198a79dd 100644 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c | |||
@@ -15,65 +15,26 @@ | |||
15 | #include <linux/clk.h> | 15 | #include <linux/clk.h> |
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/pwm.h> | 17 | #include <linux/pwm.h> |
18 | #include <mach/hardware.h> | ||
19 | |||
20 | |||
21 | /* i.MX1 and i.MX21 share the same PWM function block: */ | ||
22 | |||
23 | #define MX1_PWMC 0x00 /* PWM Control Register */ | ||
24 | #define MX1_PWMS 0x04 /* PWM Sample Register */ | ||
25 | #define MX1_PWMP 0x08 /* PWM Period Register */ | ||
26 | |||
27 | |||
28 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ | ||
29 | |||
30 | #define MX3_PWMCR 0x00 /* PWM Control Register */ | ||
31 | #define MX3_PWMSAR 0x0C /* PWM Sample Register */ | ||
32 | #define MX3_PWMPR 0x10 /* PWM Period Register */ | ||
33 | #define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) | ||
34 | #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) | ||
35 | #define MX3_PWMCR_EN (1 << 0) | ||
36 | |||
18 | 37 | ||
19 | #if defined CONFIG_ARCH_MX1 || defined CONFIG_ARCH_MX21 | ||
20 | #define PWM_VER_1 | ||
21 | |||
22 | #define PWMCR 0x00 /* PWM Control Register */ | ||
23 | #define PWMSR 0x04 /* PWM Sample Register */ | ||
24 | #define PWMPR 0x08 /* PWM Period Register */ | ||
25 | #define PWMCNR 0x0C /* PWM Counter Register */ | ||
26 | |||
27 | #define PWMCR_HCTR (1 << 18) /* Halfword FIFO Data Swapping */ | ||
28 | #define PWMCR_BCTR (1 << 17) /* Byte FIFO Data Swapping */ | ||
29 | #define PWMCR_SWR (1 << 16) /* Software Reset */ | ||
30 | #define PWMCR_CLKSRC_PERCLK (0 << 15) /* PERCLK Clock Source */ | ||
31 | #define PWMCR_CLKSRC_CLK32 (1 << 15) /* 32KHz Clock Source */ | ||
32 | #define PWMCR_PRESCALER(x) (((x - 1) & 0x7F) << 8) /* PRESCALER */ | ||
33 | #define PWMCR_IRQ (1 << 7) /* Interrupt Request */ | ||
34 | #define PWMCR_IRQEN (1 << 6) /* Interrupt Request Enable */ | ||
35 | #define PWMCR_FIFOAV (1 << 5) /* FIFO Available */ | ||
36 | #define PWMCR_EN (1 << 4) /* Enables/Disables the PWM */ | ||
37 | #define PWMCR_REPEAT(x) (((x) & 0x03) << 2) /* Sample Repeats */ | ||
38 | #define PWMCR_DIV(x) (((x) & 0x03) << 0) /* Clock divider 2/4/8/16 */ | ||
39 | |||
40 | #define MAX_DIV (128 * 16) | ||
41 | #endif | ||
42 | |||
43 | #if defined CONFIG_MACH_MX27 || defined CONFIG_ARCH_MX31 | ||
44 | #define PWM_VER_2 | ||
45 | |||
46 | #define PWMCR 0x00 /* PWM Control Register */ | ||
47 | #define PWMSR 0x04 /* PWM Status Register */ | ||
48 | #define PWMIR 0x08 /* PWM Interrupt Register */ | ||
49 | #define PWMSAR 0x0C /* PWM Sample Register */ | ||
50 | #define PWMPR 0x10 /* PWM Period Register */ | ||
51 | #define PWMCNR 0x14 /* PWM Counter Register */ | ||
52 | |||
53 | #define PWMCR_EN (1 << 0) /* Enables/Disables the PWM */ | ||
54 | #define PWMCR_REPEAT(x) (((x) & 0x03) << 1) /* Sample Repeats */ | ||
55 | #define PWMCR_SWR (1 << 3) /* Software Reset */ | ||
56 | #define PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)/* PRESCALER */ | ||
57 | #define PWMCR_CLKSRC(x) (((x) & 0x3) << 16) | ||
58 | #define PWMCR_CLKSRC_OFF (0 << 16) | ||
59 | #define PWMCR_CLKSRC_IPG (1 << 16) | ||
60 | #define PWMCR_CLKSRC_IPG_HIGH (2 << 16) | ||
61 | #define PWMCR_CLKSRC_CLK32 (3 << 16) | ||
62 | #define PWMCR_POUTC | ||
63 | #define PWMCR_HCTR (1 << 20) /* Halfword FIFO Data Swapping */ | ||
64 | #define PWMCR_BCTR (1 << 21) /* Byte FIFO Data Swapping */ | ||
65 | #define PWMCR_DBGEN (1 << 22) /* Debug Mode */ | ||
66 | #define PWMCR_WAITEN (1 << 23) /* Wait Mode */ | ||
67 | #define PWMCR_DOZEN (1 << 24) /* Doze Mode */ | ||
68 | #define PWMCR_STOPEN (1 << 25) /* Stop Mode */ | ||
69 | #define PWMCR_FWM(x) (((x) & 0x3) << 26) /* FIFO Water Mark */ | ||
70 | |||
71 | #define MAX_DIV 4096 | ||
72 | #endif | ||
73 | |||
74 | #define PWMS_SAMPLE(x) ((x) & 0xFFFF) /* Contains a two-sample word */ | ||
75 | #define PWMP_PERIOD(x) ((x) & 0xFFFF) /* Represents the PWM's period */ | ||
76 | #define PWMC_COUNTER(x) ((x) & 0xFFFF) /* Represents the current count value */ | ||
77 | 38 | ||
78 | struct pwm_device { | 39 | struct pwm_device { |
79 | struct list_head node; | 40 | struct list_head node; |
@@ -91,32 +52,52 @@ struct pwm_device { | |||
91 | 52 | ||
92 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | 53 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) |
93 | { | 54 | { |
94 | unsigned long long c; | ||
95 | unsigned long period_cycles, duty_cycles, prescale; | ||
96 | |||
97 | if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) | 55 | if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) |
98 | return -EINVAL; | 56 | return -EINVAL; |
99 | 57 | ||
100 | c = clk_get_rate(pwm->clk); | 58 | if (cpu_is_mx27() || cpu_is_mx3()) { |
101 | c = c * period_ns; | 59 | unsigned long long c; |
102 | do_div(c, 1000000000); | 60 | unsigned long period_cycles, duty_cycles, prescale; |
103 | period_cycles = c; | 61 | c = clk_get_rate(pwm->clk); |
104 | 62 | c = c * period_ns; | |
105 | prescale = period_cycles / 0x10000 + 1; | 63 | do_div(c, 1000000000); |
106 | 64 | period_cycles = c; | |
107 | period_cycles /= prescale; | 65 | |
108 | c = (unsigned long long)period_cycles * duty_ns; | 66 | prescale = period_cycles / 0x10000 + 1; |
109 | do_div(c, period_ns); | 67 | |
110 | duty_cycles = c; | 68 | period_cycles /= prescale; |
111 | 69 | c = (unsigned long long)period_cycles * duty_ns; | |
112 | #ifdef PWM_VER_2 | 70 | do_div(c, period_ns); |
113 | writel(duty_cycles, pwm->mmio_base + PWMSAR); | 71 | duty_cycles = c; |
114 | writel(period_cycles, pwm->mmio_base + PWMPR); | 72 | |
115 | writel(PWMCR_PRESCALER(prescale - 1) | PWMCR_CLKSRC_IPG_HIGH | PWMCR_EN, | 73 | writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR); |
116 | pwm->mmio_base + PWMCR); | 74 | writel(period_cycles, pwm->mmio_base + MX3_PWMPR); |
117 | #elif defined PWM_VER_1 | 75 | writel(MX3_PWMCR_PRESCALER(prescale - 1) | |
118 | #error PWM not yet working on MX1 / MX21 | 76 | MX3_PWMCR_CLKSRC_IPG_HIGH | MX3_PWMCR_EN, |
119 | #endif | 77 | pwm->mmio_base + MX3_PWMCR); |
78 | } else if (cpu_is_mx1() || cpu_is_mx21()) { | ||
79 | /* The PWM subsystem allows for exact frequencies. However, | ||
80 | * I cannot connect a scope on my device to the PWM line and | ||
81 | * thus cannot provide the program the PWM controller | ||
82 | * exactly. Instead, I'm relying on the fact that the | ||
83 | * Bootloader (u-boot or WinCE+haret) has programmed the PWM | ||
84 | * function group already. So I'll just modify the PWM sample | ||
85 | * register to follow the ratio of duty_ns vs. period_ns | ||
86 | * accordingly. | ||
87 | * | ||
88 | * This is good enought for programming the brightness of | ||
89 | * the LCD backlight. | ||
90 | * | ||
91 | * The real implementation would divide PERCLK[0] first by | ||
92 | * both the prescaler (/1 .. /128) and then by CLKSEL | ||
93 | * (/2 .. /16). | ||
94 | */ | ||
95 | u32 max = readl(pwm->mmio_base + MX1_PWMP); | ||
96 | u32 p = max * duty_ns / period_ns; | ||
97 | writel(max - p, pwm->mmio_base + MX1_PWMS); | ||
98 | } else { | ||
99 | BUG(); | ||
100 | } | ||
120 | 101 | ||
121 | return 0; | 102 | return 0; |
122 | } | 103 | } |
@@ -297,4 +278,3 @@ module_exit(mxc_pwm_exit); | |||
297 | 278 | ||
298 | MODULE_LICENSE("GPL v2"); | 279 | MODULE_LICENSE("GPL v2"); |
299 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | 280 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); |
300 | |||