mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	pwm: Add a driver for Chrome OS EC PWM
This PWM is used in rk3399-gru-bob and rk3399-gru-kevin to control
the display brightness. We can only change the duty cycle, so on
set_config() we just try to match the duty cycle that dividing duty_ns
by period_ns gives us. To disable, we set the duty cycle to zero while
keeping the old value for when we want to re-enable it.
The cros_ec_set_pwm_duty() function is taken from Depthcharge's
cros_ec_set_bl_pwm_duty() but modified to use the generic pwm type.
The driver itself is very loosely based on rk_pwm.c for the general pwm
driver structure.
The devicetree binding file is from Linux, before it was converted to
YAML at 5df5a577a6b4 ("dt-bindings: pwm: Convert google,cros-ec-pwm.txt
to YAML format") in their repo.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
			
			
This commit is contained in:
		
				
					committed by
					
						 Anatolij Gustschin
						Anatolij Gustschin
					
				
			
			
				
	
			
			
			
						parent
						
							fefa713b18
						
					
				
				
					commit
					1b9ee2882e
				
			
							
								
								
									
										23
									
								
								doc/device-tree-bindings/pwm/cros-ec-pwm.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								doc/device-tree-bindings/pwm/cros-ec-pwm.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| PWM controlled by ChromeOS EC | ||||
|  | ||||
| Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller | ||||
| (EC) and controlled via a host-command interface. | ||||
|  | ||||
| An EC PWM node should be only found as a sub-node of the EC node (see | ||||
| doc/device-tree-bindings/misc/cros-ec.txt). | ||||
|  | ||||
| Required properties: | ||||
| - compatible: Must contain "google,cros-ec-pwm" | ||||
| - #pwm-cells: Should be 1. The cell specifies the PWM index. | ||||
|  | ||||
| Example: | ||||
| 	cros-ec@0 { | ||||
| 		compatible = "google,cros-ec-spi"; | ||||
|  | ||||
| 		... | ||||
|  | ||||
| 		cros_ec_pwm: ec-pwm { | ||||
| 			compatible = "google,cros-ec-pwm"; | ||||
| 			#pwm-cells = <1>; | ||||
| 		}; | ||||
| 	}; | ||||
| @@ -1170,6 +1170,23 @@ int cros_ec_battery_cutoff(struct udevice *dev, uint8_t flags) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int cros_ec_set_pwm_duty(struct udevice *dev, uint8_t index, uint16_t duty) | ||||
| { | ||||
| 	struct ec_params_pwm_set_duty p; | ||||
| 	int ret; | ||||
|  | ||||
| 	p.duty = duty; | ||||
| 	p.pwm_type = EC_PWM_TYPE_GENERIC; | ||||
| 	p.index = index; | ||||
|  | ||||
| 	ret = ec_command(dev, EC_CMD_PWM_SET_DUTY, 0, &p, sizeof(p), | ||||
| 			 NULL, 0); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state) | ||||
| { | ||||
| 	struct ec_params_ldo_set params; | ||||
|   | ||||
| @@ -9,6 +9,15 @@ config DM_PWM | ||||
| 	  frequency/period can be controlled along with the proportion of that | ||||
| 	  time that the signal is high. | ||||
|  | ||||
| config PWM_CROS_EC | ||||
| 	bool "Enable support for the Chrome OS EC PWM" | ||||
| 	depends on DM_PWM | ||||
| 	help | ||||
| 	  This PWM is found on several Chrome OS devices and controlled by | ||||
| 	  the Chrome OS embedded controller. It may be used to control the | ||||
| 	  screen brightness and/or the keyboard backlight depending on the | ||||
| 	  device. | ||||
|  | ||||
| config PWM_EXYNOS | ||||
| 	bool "Enable support for the Exynos PWM" | ||||
| 	depends on DM_PWM | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|  | ||||
| obj-$(CONFIG_DM_PWM)		+= pwm-uclass.o | ||||
|  | ||||
| obj-$(CONFIG_PWM_CROS_EC)	+= cros_ec_pwm.o | ||||
| obj-$(CONFIG_PWM_EXYNOS)	+= exynos_pwm.o | ||||
| obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o pwm-imx-util.o | ||||
| obj-$(CONFIG_PWM_MESON)		+= pwm-meson.o | ||||
|   | ||||
							
								
								
									
										84
									
								
								drivers/pwm/cros_ec_pwm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								drivers/pwm/cros_ec_pwm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0+ | ||||
|  | ||||
| #include <common.h> | ||||
| #include <cros_ec.h> | ||||
| #include <dm.h> | ||||
| #include <errno.h> | ||||
| #include <log.h> | ||||
| #include <pwm.h> | ||||
|  | ||||
| struct cros_ec_pwm_priv { | ||||
| 	bool enabled; | ||||
| 	uint duty; | ||||
| }; | ||||
|  | ||||
| static int cros_ec_pwm_set_config(struct udevice *dev, uint channel, | ||||
| 				  uint period_ns, uint duty_ns) | ||||
| { | ||||
| 	struct cros_ec_pwm_priv *priv = dev_get_priv(dev); | ||||
| 	uint duty; | ||||
| 	int ret; | ||||
|  | ||||
| 	debug("%s: period_ns=%u, duty_ns=%u asked\n", __func__, | ||||
| 	      period_ns, duty_ns); | ||||
|  | ||||
| 	/* No way to set the period, only a relative duty cycle */ | ||||
| 	duty = EC_PWM_MAX_DUTY * duty_ns / period_ns; | ||||
| 	if (duty > EC_PWM_MAX_DUTY) | ||||
| 		duty = EC_PWM_MAX_DUTY; | ||||
|  | ||||
| 	if (!priv->enabled) { | ||||
| 		priv->duty = duty; | ||||
| 		debug("%s: duty=%#x to-be-set\n", __func__, duty); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	ret = cros_ec_set_pwm_duty(dev->parent, channel, duty); | ||||
| 	if (ret) { | ||||
| 		debug("%s: duty=%#x failed\n", __func__, duty); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	priv->duty = duty; | ||||
| 	debug("%s: duty=%#x set\n", __func__, duty); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int cros_ec_pwm_set_enable(struct udevice *dev, uint channel, | ||||
| 				  bool enable) | ||||
| { | ||||
| 	struct cros_ec_pwm_priv *priv = dev_get_priv(dev); | ||||
| 	int ret; | ||||
|  | ||||
| 	ret = cros_ec_set_pwm_duty(dev->parent, channel, | ||||
| 				   enable ? priv->duty : 0); | ||||
| 	if (ret) { | ||||
| 		debug("%s: enable=%d failed\n", __func__, enable); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	priv->enabled = enable; | ||||
| 	debug("%s: enable=%d (duty=%#x) set\n", __func__, | ||||
| 	      enable, priv->duty); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static const struct pwm_ops cros_ec_pwm_ops = { | ||||
| 	.set_config	= cros_ec_pwm_set_config, | ||||
| 	.set_enable	= cros_ec_pwm_set_enable, | ||||
| }; | ||||
|  | ||||
| static const struct udevice_id cros_ec_pwm_ids[] = { | ||||
| 	{ .compatible = "google,cros-ec-pwm" }, | ||||
| 	{ } | ||||
| }; | ||||
|  | ||||
| U_BOOT_DRIVER(cros_ec_pwm) = { | ||||
| 	.name	= "cros_ec_pwm", | ||||
| 	.id	= UCLASS_PWM, | ||||
| 	.of_match = cros_ec_pwm_ids, | ||||
| 	.ops	= &cros_ec_pwm_ops, | ||||
| 	.priv_auto_alloc_size	= sizeof(struct cros_ec_pwm_priv), | ||||
| }; | ||||
| @@ -512,6 +512,19 @@ int cros_ec_efs_verify(struct udevice *dev, enum ec_flash_region region); | ||||
|  */ | ||||
| int cros_ec_battery_cutoff(struct udevice *dev, uint8_t flags); | ||||
|  | ||||
| /** | ||||
|  * cros_ec_set_pwm_duty() - Set duty cycle of a generic pwm | ||||
|  * | ||||
|  * Note that duty value needs to be passed to the EC as a 16 bit number | ||||
|  * for increased precision. | ||||
|  * | ||||
|  * @param dev		CROS-EC device | ||||
|  * @param index		Index of the pwm | ||||
|  * @param duty		Desired duty cycle, in 0..EC_PWM_MAX_DUTY range. | ||||
|  * @return 0 if OK, -ve on error | ||||
|  */ | ||||
| int cros_ec_set_pwm_duty(struct udevice *dev, uint8_t index, uint16_t duty); | ||||
|  | ||||
| /** | ||||
|  * cros_ec_read_limit_power() - Check if power is limited by batter/charger | ||||
|  * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user