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> 23#include <linux/i2c/twl.h> 24#include <linux/slab.h> 25 26#define LED_PWM_CTRL1 0xF4 27#define LED_PWM_CTRL2 0xF5 28 29/* Max value for CTRL1 register */ 30#define PWM_CTRL1_MAX 255 31 32/* Pull down disable */ 33#define PWM_CTRL2_DIS_PD (1 << 6) 34 35/* Current control 2.5 milli Amps */ 36#define PWM_CTRL2_CURR_02 (2 << 4) 37 38/* LED supply source */ 39#define PWM_CTRL2_SRC_VAC (1 << 2) 40 41/* LED modes */ 42#define PWM_CTRL2_MODE_HW (0 << 0) 43#define PWM_CTRL2_MODE_SW (1 << 0) 44#define PWM_CTRL2_MODE_DIS (2 << 0) 45 46#define PWM_CTRL2_MODE_MASK 0x3 47 48struct pwm_device { 49 const char *label; 50 unsigned int pwm_id; 51}; 52 53int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) 54{ 55 u8 duty_cycle; 56 int ret; 57 58 if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) 59 return -EINVAL; 60 61 duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns; 62 63 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1); 64 65 if (ret < 0) { 66 pr_err("%s: Failed to configure PWM, Error %d\n", 67 pwm->label, ret); 68 return ret; 69 } 70 return 0; 71} 72EXPORT_SYMBOL(pwm_config); 73 74int pwm_enable(struct pwm_device *pwm) 75{ 76 u8 val; 77 int ret; 78 79 ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); 80 if (ret < 0) { 81 pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret); 82 return ret; 83 } 84 85 /* Change mode to software control */ 86 val &= ~PWM_CTRL2_MODE_MASK; 87 val |= PWM_CTRL2_MODE_SW; 88 89 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); 90 if (ret < 0) { 91 pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret); 92 return ret; 93 } 94 95 twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); 96 return 0; 97} 98EXPORT_SYMBOL(pwm_enable); 99 100void pwm_disable(struct pwm_device *pwm) 101{ 102 u8 val; 103 int ret; 104 105 ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); 106 if (ret < 0) { 107 pr_err("%s: Failed to disable PWM, Error %d\n", 108 pwm->label, ret); 109 return; 110 } 111 112 val &= ~PWM_CTRL2_MODE_MASK; 113 val |= PWM_CTRL2_MODE_HW; 114 115 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); 116 if (ret < 0) { 117 pr_err("%s: Failed to disable PWM, Error %d\n", 118 pwm->label, ret); 119 return; 120 } 121 return; 122} 123EXPORT_SYMBOL(pwm_disable); 124 125struct pwm_device *pwm_request(int pwm_id, const char *label) 126{ 127 u8 val; 128 int ret; 129 struct pwm_device *pwm; 130 131 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); 132 if (pwm == NULL) { 133 pr_err("%s: failed to allocate memory\n", label); 134 return NULL; 135 } 136 137 pwm->label = label; 138 pwm->pwm_id = pwm_id; 139 140 /* Configure PWM */ 141 val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | 142 PWM_CTRL2_MODE_HW; 143 144 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); 145 146 if (ret < 0) { 147 pr_err("%s: Failed to configure PWM, Error %d\n", 148 pwm->label, ret); 149 150 kfree(pwm); 151 return NULL; 152 } 153 154 return pwm; 155} 156EXPORT_SYMBOL(pwm_request); 157 158void pwm_free(struct pwm_device *pwm) 159{ 160 pwm_disable(pwm); 161 kfree(pwm); 162} 163EXPORT_SYMBOL(pwm_free); 164