// SPDX-License-Identifier: GPL-2.0 /* * Driver for LEDs connected to the Intel Cherry Trail Whiskey Cove PMIC * * Copyright 2019 Yauhen Kharuzhy <jekhor@gmail.com> * Copyright 2023 Hans de Goede <hansg@kernel.org> * * Register info comes from the Lenovo Yoga Book Android opensource code * available from Lenovo. File lenovo_yb1_x90f_l_osc_201803.7z path in the 7z: * YB1_source_code/kernel/cht/drivers/misc/charger_gp_led.c
*/
ret = regmap_read(led->regmap, led->regs->ctrl, &val); if (ret < 0) {
dev_err(cdev->dev, "Failed to read LED CTRL reg: %d\n", ret);
ret = 0; goto done;
}
val &= led->regs->on_off_mask; if (val != led->regs->on_val) {
ret = 0; goto done;
}
ret = regmap_read(led->regmap, led->regs->pwm, &val); if (ret < 0) {
dev_err(cdev->dev, "Failed to read LED PWM reg: %d\n", ret);
ret = 0; goto done;
}
ret = val;
done:
mutex_unlock(&led->mutex);
return ret;
}
/* Return blinking period for given CTRL reg value */ staticunsignedlong cht_wc_leds_get_period(int ctrl)
{
ctrl &= CHT_WC_LED_F_MASK;
switch (ctrl) { case CHT_WC_LED_F_1_4_HZ: return 1000 * 4; case CHT_WC_LED_F_1_2_HZ: return 1000 * 2; case CHT_WC_LED_F_1_HZ: return 1000; case CHT_WC_LED_F_2_HZ: return 1000 / 2;
}
return 0;
}
/* * Find suitable hardware blink mode for given period. * period < 750 ms - select 2 HZ * 750 ms <= period < 1500 ms - select 1 HZ * 1500 ms <= period < 3000 ms - select 1/2 HZ * 3000 ms <= period < 5000 ms - select 1/4 HZ * 5000 ms <= period - return -1
*/ staticint cht_wc_leds_find_freq(unsignedlong period)
{ if (period < 750) return CHT_WC_LED_F_2_HZ; elseif (period < 1500) return CHT_WC_LED_F_1_HZ; elseif (period < 3000) return CHT_WC_LED_F_1_2_HZ; elseif (period < 5000) return CHT_WC_LED_F_1_4_HZ; else return -1;
}
/* Blink with 1 Hz as default if nothing specified */ if (!*delay_on && !*delay_off)
*delay_on = *delay_off = 500;
ctrl = cht_wc_leds_find_freq(*delay_on + *delay_off); if (ctrl < 0) { /* Disable HW blinking */
ret = regmap_update_bits(led->regmap, led->regs->fsm,
CHT_WC_LED_EFF_MASK, CHT_WC_LED_EFF_ON); if (ret < 0)
dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret);
/* Fallback to software timer */
*delay_on = *delay_off = 0;
ret = -EINVAL; goto done;
}
ret = regmap_update_bits(led->regmap, led->regs->fsm,
CHT_WC_LED_EFF_MASK, effect); if (ret < 0)
dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret);
/* Set the frequency and make sure the LED is on */
ret = regmap_update_bits(led->regmap, led->regs->ctrl,
CHT_WC_LED_F_MASK | led->regs->on_off_mask,
ctrl | led->regs->on_val); if (ret < 0)
dev_err(cdev->dev, "Failed to update LED CTRL reg: %d\n", ret);
/* * The desired default behavior of LED1 / the charge LED is breathing * while charging and on/solid when full. Since triggers cannot select * breathing, blink_set() gets called when charging. Use slow breathing * when the default "charging-blink-full-solid" trigger is used to * achieve the desired default behavior.
*/ if (cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
*delay_on = *delay_off = 1000;
effect = CHT_WC_LED_EFF_BREATHING;
}
staticint cht_wc_leds_probe(struct platform_device *pdev)
{ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); struct cht_wc_leds *leds; int ret; int i;
/* * On the Lenovo Yoga Tab 3 the LED1 driver output is actually * connected to a haptic feedback motor rather then a LED. * So do not register a LED classdev there (LED2 is unused).
*/ if (pmic->cht_wc_model == INTEL_CHT_WC_LENOVO_YT3_X90) return -ENODEV;
leds = devm_kzalloc(&pdev->dev, sizeof(*leds), GFP_KERNEL); if (!leds) return -ENOMEM;
/* * LED1 might be in hw-controlled mode when this driver gets loaded; and * since the PMIC is always powered by the battery any changes made are * permanent. Save LED1 regs to restore them on remove() or shutdown().
*/
leds->leds[0].regs = &cht_wc_led_regs[0];
leds->leds[0].regmap = pmic->regmap;
ret = cht_wc_led_save_regs(&leds->leds[0], &leds->led1_initial_regs); if (ret < 0) return ret;
/* Set LED1 default trigger based on machine model */ switch (pmic->cht_wc_model) { case INTEL_CHT_WC_GPD_WIN_POCKET:
leds->leds[0].cdev.default_trigger = "max170xx_battery-charging-blink-full-solid"; break; case INTEL_CHT_WC_XIAOMI_MIPAD2:
leds->leds[0].cdev.default_trigger = "bq27520-0-charging-blink-full-solid"; break; case INTEL_CHT_WC_LENOVO_YOGABOOK1:
leds->leds[0].cdev.default_trigger = "bq27542-0-charging-blink-full-solid"; break; default:
dev_warn(&pdev->dev, "Unknown model, no default charging trigger\n"); break;
}
for (i = 0; i < CHT_WC_LED_COUNT; i++) { struct cht_wc_led *led = &leds->leds[i];
/* Restore LED1 regs if hw-control was active else leave LED1 off */ if (!(leds->led1_initial_regs.ctrl & CHT_WC_LED1_SWCTL))
cht_wc_led_restore_regs(&leds->leds[0], &leds->led1_initial_regs);
}
for (i = 0; i < CHT_WC_LED_COUNT; i++)
cht_wc_leds_brightness_set(&leds->leds[i].cdev, 0);
/* Restore LED1 regs if hw-control was active else leave LED1 off */ if (!(leds->led1_initial_regs.ctrl & CHT_WC_LED1_SWCTL))
cht_wc_led_restore_regs(&leds->leds[0], &leds->led1_initial_regs);
}
/* On suspend save current settings and turn LEDs off */ staticint cht_wc_leds_suspend(struct device *dev)
{ struct cht_wc_leds *leds = dev_get_drvdata(dev); int i, ret;
for (i = 0; i < CHT_WC_LED_COUNT; i++) {
ret = cht_wc_led_save_regs(&leds->leds[i], &leds->leds[i].saved_regs); if (ret < 0) return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.