Commit | Line | Data |
---|---|---|
31fc03df H |
1 | /* |
2 | * twl6030_pwm.c | |
3 | * Driver for PHOENIX (TWL6030) Pulse Width Modulator | |
4 | * | |
5 | * Copyright (C) 2010 Texas Instruments | |
6 | * Author: Hemanth V <hemanthv@ti.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License version 2 as published by | |
10 | * the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include <linux/module.h> | |
22 | #include <linux/platform_device.h> | |
48a364b7 | 23 | #include <linux/pwm.h> |
31fc03df H |
24 | #include <linux/i2c/twl.h> |
25 | #include <linux/slab.h> | |
26 | ||
27 | #define LED_PWM_CTRL1 0xF4 | |
28 | #define LED_PWM_CTRL2 0xF5 | |
29 | ||
30 | /* Max value for CTRL1 register */ | |
31 | #define PWM_CTRL1_MAX 255 | |
32 | ||
33 | /* Pull down disable */ | |
34 | #define PWM_CTRL2_DIS_PD (1 << 6) | |
35 | ||
36 | /* Current control 2.5 milli Amps */ | |
37 | #define PWM_CTRL2_CURR_02 (2 << 4) | |
38 | ||
39 | /* LED supply source */ | |
40 | #define PWM_CTRL2_SRC_VAC (1 << 2) | |
41 | ||
42 | /* LED modes */ | |
43 | #define PWM_CTRL2_MODE_HW (0 << 0) | |
44 | #define PWM_CTRL2_MODE_SW (1 << 0) | |
45 | #define PWM_CTRL2_MODE_DIS (2 << 0) | |
46 | ||
47 | #define PWM_CTRL2_MODE_MASK 0x3 | |
48 | ||
48a364b7 TR |
49 | struct twl6030_pwm_chip { |
50 | struct pwm_chip chip; | |
31fc03df H |
51 | }; |
52 | ||
48a364b7 | 53 | static int twl6030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) |
31fc03df | 54 | { |
31fc03df | 55 | int ret; |
48a364b7 | 56 | u8 val; |
31fc03df | 57 | |
48a364b7 TR |
58 | /* Configure PWM */ |
59 | val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | | |
60 | PWM_CTRL2_MODE_HW; | |
31fc03df | 61 | |
48a364b7 TR |
62 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); |
63 | if (ret < 0) { | |
64 | dev_err(chip->dev, "%s: Failed to configure PWM, Error %d\n", | |
65 | pwm->label, ret); | |
66 | return ret; | |
67 | } | |
31fc03df | 68 | |
48a364b7 TR |
69 | return 0; |
70 | } | |
31fc03df | 71 | |
48a364b7 TR |
72 | static int twl6030_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, |
73 | int duty_ns, int period_ns) | |
74 | { | |
75 | u8 duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns; | |
76 | int ret; | |
77 | ||
78 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1); | |
31fc03df H |
79 | if (ret < 0) { |
80 | pr_err("%s: Failed to configure PWM, Error %d\n", | |
81 | pwm->label, ret); | |
82 | return ret; | |
83 | } | |
48a364b7 | 84 | |
31fc03df H |
85 | return 0; |
86 | } | |
31fc03df | 87 | |
48a364b7 | 88 | static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
31fc03df | 89 | { |
31fc03df | 90 | int ret; |
48a364b7 | 91 | u8 val; |
31fc03df H |
92 | |
93 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); | |
94 | if (ret < 0) { | |
48a364b7 TR |
95 | dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", |
96 | pwm->label, ret); | |
31fc03df H |
97 | return ret; |
98 | } | |
99 | ||
100 | /* Change mode to software control */ | |
101 | val &= ~PWM_CTRL2_MODE_MASK; | |
102 | val |= PWM_CTRL2_MODE_SW; | |
103 | ||
104 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); | |
105 | if (ret < 0) { | |
48a364b7 TR |
106 | dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", |
107 | pwm->label, ret); | |
31fc03df H |
108 | return ret; |
109 | } | |
110 | ||
111 | twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); | |
112 | return 0; | |
113 | } | |
31fc03df | 114 | |
48a364b7 | 115 | static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
31fc03df | 116 | { |
31fc03df | 117 | int ret; |
48a364b7 | 118 | u8 val; |
31fc03df H |
119 | |
120 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); | |
121 | if (ret < 0) { | |
48a364b7 | 122 | dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", |
31fc03df H |
123 | pwm->label, ret); |
124 | return; | |
125 | } | |
126 | ||
127 | val &= ~PWM_CTRL2_MODE_MASK; | |
128 | val |= PWM_CTRL2_MODE_HW; | |
129 | ||
130 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); | |
131 | if (ret < 0) { | |
48a364b7 | 132 | dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", |
31fc03df | 133 | pwm->label, ret); |
31fc03df | 134 | } |
31fc03df | 135 | } |
31fc03df | 136 | |
48a364b7 TR |
137 | static const struct pwm_ops twl6030_pwm_ops = { |
138 | .request = twl6030_pwm_request, | |
139 | .config = twl6030_pwm_config, | |
140 | .enable = twl6030_pwm_enable, | |
141 | .disable = twl6030_pwm_disable, | |
142 | }; | |
143 | ||
144 | static int twl6030_pwm_probe(struct platform_device *pdev) | |
31fc03df | 145 | { |
48a364b7 | 146 | struct twl6030_pwm_chip *twl6030; |
31fc03df | 147 | int ret; |
31fc03df | 148 | |
48a364b7 TR |
149 | twl6030 = devm_kzalloc(&pdev->dev, sizeof(*twl6030), GFP_KERNEL); |
150 | if (!twl6030) | |
151 | return -ENOMEM; | |
31fc03df | 152 | |
48a364b7 TR |
153 | twl6030->chip.dev = &pdev->dev; |
154 | twl6030->chip.ops = &twl6030_pwm_ops; | |
155 | twl6030->chip.base = -1; | |
156 | twl6030->chip.npwm = 1; | |
31fc03df | 157 | |
48a364b7 TR |
158 | ret = pwmchip_add(&twl6030->chip); |
159 | if (ret < 0) | |
160 | return ret; | |
31fc03df | 161 | |
48a364b7 | 162 | platform_set_drvdata(pdev, twl6030); |
31fc03df | 163 | |
48a364b7 | 164 | return 0; |
31fc03df | 165 | } |
31fc03df | 166 | |
48a364b7 | 167 | static int twl6030_pwm_remove(struct platform_device *pdev) |
31fc03df | 168 | { |
48a364b7 TR |
169 | struct twl6030_pwm_chip *twl6030 = platform_get_drvdata(pdev); |
170 | ||
171 | return pwmchip_remove(&twl6030->chip); | |
31fc03df | 172 | } |
2b959e7f | 173 | |
48a364b7 TR |
174 | static struct platform_driver twl6030_pwm_driver = { |
175 | .driver = { | |
176 | .name = "twl6030-pwm", | |
177 | }, | |
178 | .probe = twl6030_pwm_probe, | |
fd109112 | 179 | .remove = twl6030_pwm_remove, |
48a364b7 TR |
180 | }; |
181 | module_platform_driver(twl6030_pwm_driver); | |
182 | ||
183 | MODULE_ALIAS("platform:twl6030-pwm"); | |
2b959e7f | 184 | MODULE_LICENSE("GPL"); |