caab277b1de0a (Thomas Gleixner 2019-06-03 07:44:50 +0200 1) // SPDX-License-Identifier: GPL-2.0-only
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 2) /*
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 3) * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 4) *
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 5) * Copyright (C) 2012 Texas Instruments
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 6) * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 7) *
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 8) * This driver is a complete rewrite of the former pwm-twl6030.c authorded by:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 9) * Hemanth V <hemanthv@ti.com>
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 10) */
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 11)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 12) #include <linux/module.h>
e852340deef3c (Sachin Kamat 2013-09-27 16:53:25 +0530 13) #include <linux/of.h>
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 14) #include <linux/platform_device.h>
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 15) #include <linux/pwm.h>
a205425658dea (Wolfram Sang 2017-08-14 18:34:24 +0200 16) #include <linux/mfd/twl.h>
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 17) #include <linux/slab.h>
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 18)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 19) /*
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 20) * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030.
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 21) * To generate the signal on TWL4030:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 22) * - LEDA uses PWMA
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 23) * - LEDB uses PWMB
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 24) * TWL6030 has one LED pin with dedicated LEDPWM
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 25) */
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 26)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 27) #define TWL4030_LED_MAX 0x7f
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 28) #define TWL6030_LED_MAX 0xff
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 29)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 30) /* Registers, bits and macro for TWL4030 */
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 31) #define TWL4030_LEDEN_REG 0x00
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 32) #define TWL4030_PWMA_REG 0x01
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 33)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 34) #define TWL4030_LEDXON (1 << 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 35) #define TWL4030_LEDXPWM (1 << 4)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 36) #define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 37) #define TWL4030_LED_TOGGLE(led, x) ((x) << (led))
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 38)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 39) /* Register, bits and macro for TWL6030 */
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 40) #define TWL6030_LED_PWM_CTRL1 0xf4
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 41) #define TWL6030_LED_PWM_CTRL2 0xf5
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 42)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 43) #define TWL6040_LED_MODE_HW 0x00
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 44) #define TWL6040_LED_MODE_ON 0x01
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 45) #define TWL6040_LED_MODE_OFF 0x02
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 46) #define TWL6040_LED_MODE_MASK 0x03
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 47)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 48) struct twl_pwmled_chip {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 49) struct pwm_chip chip;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 50) struct mutex mutex;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 51) };
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 52)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 53) static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 54) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 55) return container_of(chip, struct twl_pwmled_chip, chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 56) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 57)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 58) static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 59) int duty_ns, int period_ns)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 60) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 61) int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 62) u8 pwm_config[2] = { 1, 0 };
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 63) int base, ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 64)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 65) /*
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 66) * To configure the duty period:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 67) * On-cycle is set to 1 (the minimum allowed value)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 68) * The off time of 0 is not configurable, so the mapping is:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 69) * 0 -> off cycle = 2,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 70) * 1 -> off cycle = 2,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 71) * 2 -> off cycle = 3,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 72) * 126 - > off cycle 127,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 73) * 127 - > off cycle 1
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 74) * When on cycle == off cycle the PWM will be always on
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 75) */
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 76) if (duty_cycle == 1)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 77) duty_cycle = 2;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 78) else if (duty_cycle > TWL4030_LED_MAX)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 79) duty_cycle = 1;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 80)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 81) base = pwm->hwpwm * 2 + TWL4030_PWMA_REG;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 82)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 83) pwm_config[1] = duty_cycle;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 84)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 85) ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 86) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 87) dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 88)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 89) return ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 90) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 91)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 92) static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 93) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 94) struct twl_pwmled_chip *twl = to_twl(chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 95) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 96) u8 val;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 97)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 98) mutex_lock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 99) ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 100) if (ret < 0) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 101) dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 102) goto out;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 103) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 104)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 105) val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 106)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 107) ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 108) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 109) dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 110)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 111) out:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 112) mutex_unlock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 113) return ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 114) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 115)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 116) static void twl4030_pwmled_disable(struct pwm_chip *chip,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 117) struct pwm_device *pwm)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 118) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 119) struct twl_pwmled_chip *twl = to_twl(chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 120) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 121) u8 val;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 122)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 123) mutex_lock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 124) ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 125) if (ret < 0) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 126) dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 127) goto out;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 128) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 129)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 130) val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 131)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 132) ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 133) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 134) dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 135)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 136) out:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 137) mutex_unlock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 138) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 139)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 140) static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 141) int duty_ns, int period_ns)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 142) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 143) int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 144) u8 on_time;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 145) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 146)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 147) on_time = duty_cycle & 0xff;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 148)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 149) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 150) TWL6030_LED_PWM_CTRL1);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 151) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 152) dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 153)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 154) return ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 155) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 156)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 157) static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 158) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 159) struct twl_pwmled_chip *twl = to_twl(chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 160) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 161) u8 val;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 162)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 163) mutex_lock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 164) ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 165) if (ret < 0) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 166) dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 167) pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 168) goto out;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 169) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 170)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 171) val &= ~TWL6040_LED_MODE_MASK;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 172) val |= TWL6040_LED_MODE_ON;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 173)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 174) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 175) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 176) dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 177)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 178) out:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 179) mutex_unlock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 180) return ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 181) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 182)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 183) static void twl6030_pwmled_disable(struct pwm_chip *chip,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 184) struct pwm_device *pwm)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 185) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 186) struct twl_pwmled_chip *twl = to_twl(chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 187) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 188) u8 val;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 189)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 190) mutex_lock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 191) ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 192) if (ret < 0) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 193) dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 194) pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 195) goto out;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 196) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 197)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 198) val &= ~TWL6040_LED_MODE_MASK;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 199) val |= TWL6040_LED_MODE_OFF;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 200)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 201) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 202) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 203) dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 204)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 205) out:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 206) mutex_unlock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 207) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 208)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 209) static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 210) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 211) struct twl_pwmled_chip *twl = to_twl(chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 212) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 213) u8 val;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 214)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 215) mutex_lock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 216) ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 217) if (ret < 0) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 218) dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 219) pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 220) goto out;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 221) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 222)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 223) val &= ~TWL6040_LED_MODE_MASK;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 224) val |= TWL6040_LED_MODE_OFF;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 225)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 226) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 227) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 228) dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 229)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 230) out:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 231) mutex_unlock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 232) return ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 233) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 234)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 235) static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 236) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 237) struct twl_pwmled_chip *twl = to_twl(chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 238) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 239) u8 val;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 240)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 241) mutex_lock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 242) ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 243) if (ret < 0) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 244) dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n",
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 245) pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 246) goto out;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 247) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 248)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 249) val &= ~TWL6040_LED_MODE_MASK;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 250) val |= TWL6040_LED_MODE_HW;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 251)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 252) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 253) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 254) dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 255)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 256) out:
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 257) mutex_unlock(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 258) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 259)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 260) static const struct pwm_ops twl4030_pwmled_ops = {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 261) .enable = twl4030_pwmled_enable,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 262) .disable = twl4030_pwmled_disable,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 263) .config = twl4030_pwmled_config,
7fa25314d534b (Axel Lin 2013-03-31 11:16:14 +0800 264) .owner = THIS_MODULE,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 265) };
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 266)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 267) static const struct pwm_ops twl6030_pwmled_ops = {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 268) .enable = twl6030_pwmled_enable,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 269) .disable = twl6030_pwmled_disable,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 270) .config = twl6030_pwmled_config,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 271) .request = twl6030_pwmled_request,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 272) .free = twl6030_pwmled_free,
7fa25314d534b (Axel Lin 2013-03-31 11:16:14 +0800 273) .owner = THIS_MODULE,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 274) };
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 275)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 276) static int twl_pwmled_probe(struct platform_device *pdev)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 277) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 278) struct twl_pwmled_chip *twl;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 279) int ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 280)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 281) twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 282) if (!twl)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 283) return -ENOMEM;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 284)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 285) if (twl_class_is_4030()) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 286) twl->chip.ops = &twl4030_pwmled_ops;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 287) twl->chip.npwm = 2;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 288) } else {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 289) twl->chip.ops = &twl6030_pwmled_ops;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 290) twl->chip.npwm = 1;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 291) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 292)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 293) twl->chip.dev = &pdev->dev;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 294)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 295) mutex_init(&twl->mutex);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 296)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 297) ret = pwmchip_add(&twl->chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 298) if (ret < 0)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 299) return ret;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 300)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 301) platform_set_drvdata(pdev, twl);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 302)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 303) return 0;
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 304) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 305)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 306) static int twl_pwmled_remove(struct platform_device *pdev)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 307) {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 308) struct twl_pwmled_chip *twl = platform_get_drvdata(pdev);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 309)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 310) return pwmchip_remove(&twl->chip);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 311) }
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 312)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 313) #ifdef CONFIG_OF
f1a8870aeb5ba (Thierry Reding 2013-04-18 10:04:14 +0200 314) static const struct of_device_id twl_pwmled_of_match[] = {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 315) { .compatible = "ti,twl4030-pwmled" },
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 316) { .compatible = "ti,twl6030-pwmled" },
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 317) { },
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 318) };
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 319) MODULE_DEVICE_TABLE(of, twl_pwmled_of_match);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 320) #endif
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 321)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 322) static struct platform_driver twl_pwmled_driver = {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 323) .driver = {
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 324) .name = "twl-pwmled",
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 325) .of_match_table = of_match_ptr(twl_pwmled_of_match),
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 326) },
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 327) .probe = twl_pwmled_probe,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 328) .remove = twl_pwmled_remove,
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 329) };
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 330) module_platform_driver(twl_pwmled_driver);
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 331)
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 332) MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 333) MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs");
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 334) MODULE_ALIAS("platform:twl-pwmled");
aa7656471df6c (Peter Ujfalusi 2012-11-27 11:09:58 +0100 335) MODULE_LICENSE("GPL");