VisionFive2 Linux kernel

StarFive Tech Linux Kernel for VisionFive (JH7110) boards (mirror)

More than 9999 Commits   33 Branches   55 Tags
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");