aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-twl6030.c
blob: 8e6387864ca2c81672b6e565e757d296b01c0566 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
 * twl6030_pwm.c
 * Driver for PHOENIX (TWL6030) Pulse Width Modulator
 *
 * Copyright (C) 2010 Texas Instruments
 * Author: Hemanth V <hemanthv@ti.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/i2c/twl.h>
#include <linux/slab.h>

#define LED_PWM_CTRL1	0xF4
#define LED_PWM_CTRL2	0xF5

/* Max value for CTRL1 register */
#define PWM_CTRL1_MAX	255

/* Pull down disable */
#define PWM_CTRL2_DIS_PD	(1 << 6)

/* Current control 2.5 milli Amps */
#define PWM_CTRL2_CURR_02	(2 << 4)

/* LED supply source */
#define PWM_CTRL2_SRC_VAC	(1 << 2)

/* LED modes */
#define PWM_CTRL2_MODE_HW	(0 << 0)
#define PWM_CTRL2_MODE_SW	(1 << 0)
#define PWM_CTRL2_MODE_DIS	(2 << 0)

#define PWM_CTRL2_MODE_MASK	0x3

struct twl6030_pwm_chip {
	struct pwm_chip chip;
};

static int twl6030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
	int ret;
	u8 val;

	/* Configure PWM */
	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
	      PWM_CTRL2_MODE_HW;

	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
	if (ret < 0) {
		dev_err(chip->dev, "%s: Failed to configure PWM, Error %d\n",
			pwm->label, ret);
		return ret;
	}

	return 0;
}

static int twl6030_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
			      int duty_ns, int period_ns)
{
	u8 duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns;
	int ret;

	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1);
	if (ret < 0) {
		pr_err("%s: Failed to configure PWM, Error %d\n",
			pwm->label, ret);
		return ret;
	}

	return 0;
}

static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	int ret;
	u8 val;

	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
	if (ret < 0) {
		dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n",
			pwm->label, ret);
		return ret;
	}

	/* Change mode to software control */
	val &= ~PWM_CTRL2_MODE_MASK;
	val |= PWM_CTRL2_MODE_SW;

	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
	if (ret < 0) {
		dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n",
			pwm->label, ret);
		return ret;
	}

	twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
	return 0;
}

static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	int ret;
	u8 val;

	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
	if (ret < 0) {
		dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n",
			pwm->label, ret);
		return;
	}

	val &= ~PWM_CTRL2_MODE_MASK;
	val |= PWM_CTRL2_MODE_HW;

	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
	if (ret < 0) {
		dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n",
			pwm->label, ret);
	}
}

static const struct pwm_ops twl6030_pwm_ops = {
	.request = twl6030_pwm_request,
	.config = twl6030_pwm_config,
	.enable = twl6030_pwm_enable,
	.disable = twl6030_pwm_disable,
};

static int twl6030_pwm_probe(struct platform_device *pdev)
{
	struct twl6030_pwm_chip *twl6030;
	int ret;

	twl6030 = devm_kzalloc(&pdev->dev, sizeof(*twl6030), GFP_KERNEL);
	if (!twl6030)
		return -ENOMEM;

	twl6030->chip.dev = &pdev->dev;
	twl6030->chip.ops = &twl6030_pwm_ops;
	twl6030->chip.base = -1;
	twl6030->chip.npwm = 1;

	ret = pwmchip_add(&twl6030->chip);
	if (ret < 0)
		return ret;

	platform_set_drvdata(pdev, twl6030);

	return 0;
}

static int twl6030_pwm_remove(struct platform_device *pdev)
{
	struct twl6030_pwm_chip *twl6030 = platform_get_drvdata(pdev);

	return pwmchip_remove(&twl6030->chip);
}

static struct platform_driver twl6030_pwm_driver = {
	.driver = {
		.name = "twl6030-pwm",
	},
	.probe = twl6030_pwm_probe,
	.remove = __devexit_p(twl6030_pwm_remove),
};
module_platform_driver(twl6030_pwm_driver);

MODULE_ALIAS("platform:twl6030-pwm");
MODULE_LICENSE("GPL");