cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leds-mt6323.c (12608B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * LED driver for Mediatek MT6323 PMIC
      4 *
      5 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
      6 */
      7#include <linux/kernel.h>
      8#include <linux/leds.h>
      9#include <linux/mfd/mt6323/registers.h>
     10#include <linux/mfd/mt6397/core.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/platform_device.h>
     14#include <linux/regmap.h>
     15
     16/*
     17 * Register field for MT6323_TOP_CKPDN0 to enable
     18 * 32K clock common for LED device.
     19 */
     20#define MT6323_RG_DRV_32K_CK_PDN	BIT(11)
     21#define MT6323_RG_DRV_32K_CK_PDN_MASK	BIT(11)
     22
     23/*
     24 * Register field for MT6323_TOP_CKPDN2 to enable
     25 * individual clock for LED device.
     26 */
     27#define MT6323_RG_ISINK_CK_PDN(i)	BIT(i)
     28#define MT6323_RG_ISINK_CK_PDN_MASK(i)	BIT(i)
     29
     30/*
     31 * Register field for MT6323_TOP_CKCON1 to select
     32 * clock source.
     33 */
     34#define MT6323_RG_ISINK_CK_SEL_MASK(i)	(BIT(10) << (i))
     35
     36/*
     37 * Register for MT6323_ISINK_CON0 to setup the
     38 * duty cycle of the blink.
     39 */
     40#define MT6323_ISINK_CON0(i)		(MT6323_ISINK0_CON0 + 0x8 * (i))
     41#define MT6323_ISINK_DIM_DUTY_MASK	(0x1f << 8)
     42#define MT6323_ISINK_DIM_DUTY(i)	(((i) << 8) & \
     43					MT6323_ISINK_DIM_DUTY_MASK)
     44
     45/* Register to setup the period of the blink. */
     46#define MT6323_ISINK_CON1(i)		(MT6323_ISINK0_CON1 + 0x8 * (i))
     47#define MT6323_ISINK_DIM_FSEL_MASK	(0xffff)
     48#define MT6323_ISINK_DIM_FSEL(i)	((i) & MT6323_ISINK_DIM_FSEL_MASK)
     49
     50/* Register to control the brightness. */
     51#define MT6323_ISINK_CON2(i)		(MT6323_ISINK0_CON2 + 0x8 * (i))
     52#define MT6323_ISINK_CH_STEP_SHIFT	12
     53#define MT6323_ISINK_CH_STEP_MASK	(0x7 << 12)
     54#define MT6323_ISINK_CH_STEP(i)		(((i) << 12) & \
     55					MT6323_ISINK_CH_STEP_MASK)
     56#define MT6323_ISINK_SFSTR0_TC_MASK	(0x3 << 1)
     57#define MT6323_ISINK_SFSTR0_TC(i)	(((i) << 1) & \
     58					MT6323_ISINK_SFSTR0_TC_MASK)
     59#define MT6323_ISINK_SFSTR0_EN_MASK	BIT(0)
     60#define MT6323_ISINK_SFSTR0_EN		BIT(0)
     61
     62/* Register to LED channel enablement. */
     63#define MT6323_ISINK_CH_EN_MASK(i)	BIT(i)
     64#define MT6323_ISINK_CH_EN(i)		BIT(i)
     65
     66#define MT6323_MAX_PERIOD		10000
     67#define MT6323_MAX_LEDS			4
     68#define MT6323_MAX_BRIGHTNESS		6
     69#define MT6323_UNIT_DUTY		3125
     70#define MT6323_CAL_HW_DUTY(o, p)	DIV_ROUND_CLOSEST((o) * 100000ul,\
     71					(p) * MT6323_UNIT_DUTY)
     72
     73struct mt6323_leds;
     74
     75/**
     76 * struct mt6323_led - state container for the LED device
     77 * @id:			the identifier in MT6323 LED device
     78 * @parent:		the pointer to MT6323 LED controller
     79 * @cdev:		LED class device for this LED device
     80 * @current_brightness: current state of the LED device
     81 */
     82struct mt6323_led {
     83	int			id;
     84	struct mt6323_leds	*parent;
     85	struct led_classdev	cdev;
     86	enum led_brightness	current_brightness;
     87};
     88
     89/**
     90 * struct mt6323_leds -	state container for holding LED controller
     91 *			of the driver
     92 * @dev:		the device pointer
     93 * @hw:			the underlying hardware providing shared
     94 *			bus for the register operations
     95 * @lock:		the lock among process context
     96 * @led:		the array that contains the state of individual
     97 *			LED device
     98 */
     99struct mt6323_leds {
    100	struct device		*dev;
    101	struct mt6397_chip	*hw;
    102	/* protect among process context */
    103	struct mutex		lock;
    104	struct mt6323_led	*led[MT6323_MAX_LEDS];
    105};
    106
    107static int mt6323_led_hw_brightness(struct led_classdev *cdev,
    108				    enum led_brightness brightness)
    109{
    110	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    111	struct mt6323_leds *leds = led->parent;
    112	struct regmap *regmap = leds->hw->regmap;
    113	u32 con2_mask = 0, con2_val = 0;
    114	int ret;
    115
    116	/*
    117	 * Setup current output for the corresponding
    118	 * brightness level.
    119	 */
    120	con2_mask |= MT6323_ISINK_CH_STEP_MASK |
    121		     MT6323_ISINK_SFSTR0_TC_MASK |
    122		     MT6323_ISINK_SFSTR0_EN_MASK;
    123	con2_val |=  MT6323_ISINK_CH_STEP(brightness - 1) |
    124		     MT6323_ISINK_SFSTR0_TC(2) |
    125		     MT6323_ISINK_SFSTR0_EN;
    126
    127	ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id),
    128				 con2_mask, con2_val);
    129	return ret;
    130}
    131
    132static int mt6323_led_hw_off(struct led_classdev *cdev)
    133{
    134	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    135	struct mt6323_leds *leds = led->parent;
    136	struct regmap *regmap = leds->hw->regmap;
    137	unsigned int status;
    138	int ret;
    139
    140	status = MT6323_ISINK_CH_EN(led->id);
    141	ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
    142				 MT6323_ISINK_CH_EN_MASK(led->id), ~status);
    143	if (ret < 0)
    144		return ret;
    145
    146	usleep_range(100, 300);
    147	ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
    148				 MT6323_RG_ISINK_CK_PDN_MASK(led->id),
    149				 MT6323_RG_ISINK_CK_PDN(led->id));
    150	if (ret < 0)
    151		return ret;
    152
    153	return 0;
    154}
    155
    156static enum led_brightness
    157mt6323_get_led_hw_brightness(struct led_classdev *cdev)
    158{
    159	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    160	struct mt6323_leds *leds = led->parent;
    161	struct regmap *regmap = leds->hw->regmap;
    162	unsigned int status;
    163	int ret;
    164
    165	ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status);
    166	if (ret < 0)
    167		return ret;
    168
    169	if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id))
    170		return 0;
    171
    172	ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status);
    173	if (ret < 0)
    174		return ret;
    175
    176	if (!(status & MT6323_ISINK_CH_EN(led->id)))
    177		return 0;
    178
    179	ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status);
    180	if (ret < 0)
    181		return ret;
    182
    183	return  ((status & MT6323_ISINK_CH_STEP_MASK)
    184		  >> MT6323_ISINK_CH_STEP_SHIFT) + 1;
    185}
    186
    187static int mt6323_led_hw_on(struct led_classdev *cdev,
    188			    enum led_brightness brightness)
    189{
    190	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    191	struct mt6323_leds *leds = led->parent;
    192	struct regmap *regmap = leds->hw->regmap;
    193	unsigned int status;
    194	int ret;
    195
    196	/*
    197	 * Setup required clock source, enable the corresponding
    198	 * clock and channel and let work with continuous blink as
    199	 * the default.
    200	 */
    201	ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1,
    202				 MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0);
    203	if (ret < 0)
    204		return ret;
    205
    206	status = MT6323_RG_ISINK_CK_PDN(led->id);
    207	ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
    208				 MT6323_RG_ISINK_CK_PDN_MASK(led->id),
    209				 ~status);
    210	if (ret < 0)
    211		return ret;
    212
    213	usleep_range(100, 300);
    214
    215	ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
    216				 MT6323_ISINK_CH_EN_MASK(led->id),
    217				 MT6323_ISINK_CH_EN(led->id));
    218	if (ret < 0)
    219		return ret;
    220
    221	ret = mt6323_led_hw_brightness(cdev, brightness);
    222	if (ret < 0)
    223		return ret;
    224
    225	ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
    226				 MT6323_ISINK_DIM_DUTY_MASK,
    227				 MT6323_ISINK_DIM_DUTY(31));
    228	if (ret < 0)
    229		return ret;
    230
    231	ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
    232				 MT6323_ISINK_DIM_FSEL_MASK,
    233				 MT6323_ISINK_DIM_FSEL(1000));
    234	if (ret < 0)
    235		return ret;
    236
    237	return 0;
    238}
    239
    240static int mt6323_led_set_blink(struct led_classdev *cdev,
    241				unsigned long *delay_on,
    242				unsigned long *delay_off)
    243{
    244	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    245	struct mt6323_leds *leds = led->parent;
    246	struct regmap *regmap = leds->hw->regmap;
    247	unsigned long period;
    248	u8 duty_hw;
    249	int ret;
    250
    251	/*
    252	 * LED subsystem requires a default user
    253	 * friendly blink pattern for the LED so using
    254	 * 1Hz duty cycle 50% here if without specific
    255	 * value delay_on and delay off being assigned.
    256	 */
    257	if (!*delay_on && !*delay_off) {
    258		*delay_on = 500;
    259		*delay_off = 500;
    260	}
    261
    262	/*
    263	 * Units are in ms, if over the hardware able
    264	 * to support, fallback into software blink
    265	 */
    266	period = *delay_on + *delay_off;
    267
    268	if (period > MT6323_MAX_PERIOD)
    269		return -EINVAL;
    270
    271	/*
    272	 * Calculate duty_hw based on the percentage of period during
    273	 * which the led is ON.
    274	 */
    275	duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period);
    276
    277	/* hardware doesn't support zero duty cycle. */
    278	if (!duty_hw)
    279		return -EINVAL;
    280
    281	mutex_lock(&leds->lock);
    282	/*
    283	 * Set max_brightness as the software blink behavior
    284	 * when no blink brightness.
    285	 */
    286	if (!led->current_brightness) {
    287		ret = mt6323_led_hw_on(cdev, cdev->max_brightness);
    288		if (ret < 0)
    289			goto out;
    290		led->current_brightness = cdev->max_brightness;
    291	}
    292
    293	ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
    294				 MT6323_ISINK_DIM_DUTY_MASK,
    295				 MT6323_ISINK_DIM_DUTY(duty_hw - 1));
    296	if (ret < 0)
    297		goto out;
    298
    299	ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
    300				 MT6323_ISINK_DIM_FSEL_MASK,
    301				 MT6323_ISINK_DIM_FSEL(period - 1));
    302out:
    303	mutex_unlock(&leds->lock);
    304
    305	return ret;
    306}
    307
    308static int mt6323_led_set_brightness(struct led_classdev *cdev,
    309				     enum led_brightness brightness)
    310{
    311	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    312	struct mt6323_leds *leds = led->parent;
    313	int ret;
    314
    315	mutex_lock(&leds->lock);
    316
    317	if (!led->current_brightness && brightness) {
    318		ret = mt6323_led_hw_on(cdev, brightness);
    319		if (ret < 0)
    320			goto out;
    321	} else if (brightness) {
    322		ret = mt6323_led_hw_brightness(cdev, brightness);
    323		if (ret < 0)
    324			goto out;
    325	} else {
    326		ret = mt6323_led_hw_off(cdev);
    327		if (ret < 0)
    328			goto out;
    329	}
    330
    331	led->current_brightness = brightness;
    332out:
    333	mutex_unlock(&leds->lock);
    334
    335	return ret;
    336}
    337
    338static int mt6323_led_set_dt_default(struct led_classdev *cdev,
    339				     struct device_node *np)
    340{
    341	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
    342	const char *state;
    343	int ret = 0;
    344
    345	state = of_get_property(np, "default-state", NULL);
    346	if (state) {
    347		if (!strcmp(state, "keep")) {
    348			ret = mt6323_get_led_hw_brightness(cdev);
    349			if (ret < 0)
    350				return ret;
    351			led->current_brightness = ret;
    352			ret = 0;
    353		} else if (!strcmp(state, "on")) {
    354			ret =
    355			mt6323_led_set_brightness(cdev, cdev->max_brightness);
    356		} else  {
    357			ret = mt6323_led_set_brightness(cdev, LED_OFF);
    358		}
    359	}
    360
    361	return ret;
    362}
    363
    364static int mt6323_led_probe(struct platform_device *pdev)
    365{
    366	struct device *dev = &pdev->dev;
    367	struct device_node *np = dev_of_node(dev);
    368	struct device_node *child;
    369	struct mt6397_chip *hw = dev_get_drvdata(dev->parent);
    370	struct mt6323_leds *leds;
    371	struct mt6323_led *led;
    372	int ret;
    373	unsigned int status;
    374	u32 reg;
    375
    376	leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
    377	if (!leds)
    378		return -ENOMEM;
    379
    380	platform_set_drvdata(pdev, leds);
    381	leds->dev = dev;
    382
    383	/*
    384	 * leds->hw points to the underlying bus for the register
    385	 * controlled.
    386	 */
    387	leds->hw = hw;
    388	mutex_init(&leds->lock);
    389
    390	status = MT6323_RG_DRV_32K_CK_PDN;
    391	ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
    392				 MT6323_RG_DRV_32K_CK_PDN_MASK, ~status);
    393	if (ret < 0) {
    394		dev_err(leds->dev,
    395			"Failed to update MT6323_TOP_CKPDN0 Register\n");
    396		return ret;
    397	}
    398
    399	for_each_available_child_of_node(np, child) {
    400		struct led_init_data init_data = {};
    401
    402		ret = of_property_read_u32(child, "reg", &reg);
    403		if (ret) {
    404			dev_err(dev, "Failed to read led 'reg' property\n");
    405			goto put_child_node;
    406		}
    407
    408		if (reg >= MT6323_MAX_LEDS || leds->led[reg]) {
    409			dev_err(dev, "Invalid led reg %u\n", reg);
    410			ret = -EINVAL;
    411			goto put_child_node;
    412		}
    413
    414		led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
    415		if (!led) {
    416			ret = -ENOMEM;
    417			goto put_child_node;
    418		}
    419
    420		leds->led[reg] = led;
    421		leds->led[reg]->id = reg;
    422		leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS;
    423		leds->led[reg]->cdev.brightness_set_blocking =
    424					mt6323_led_set_brightness;
    425		leds->led[reg]->cdev.blink_set = mt6323_led_set_blink;
    426		leds->led[reg]->cdev.brightness_get =
    427					mt6323_get_led_hw_brightness;
    428		leds->led[reg]->parent = leds;
    429
    430		ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child);
    431		if (ret < 0) {
    432			dev_err(leds->dev,
    433				"Failed to LED set default from devicetree\n");
    434			goto put_child_node;
    435		}
    436
    437		init_data.fwnode = of_fwnode_handle(child);
    438
    439		ret = devm_led_classdev_register_ext(dev, &leds->led[reg]->cdev,
    440						     &init_data);
    441		if (ret) {
    442			dev_err(dev, "Failed to register LED: %d\n", ret);
    443			goto put_child_node;
    444		}
    445	}
    446
    447	return 0;
    448
    449put_child_node:
    450	of_node_put(child);
    451	return ret;
    452}
    453
    454static int mt6323_led_remove(struct platform_device *pdev)
    455{
    456	struct mt6323_leds *leds = platform_get_drvdata(pdev);
    457	int i;
    458
    459	/* Turn the LEDs off on driver removal. */
    460	for (i = 0 ; leds->led[i] ; i++)
    461		mt6323_led_hw_off(&leds->led[i]->cdev);
    462
    463	regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
    464			   MT6323_RG_DRV_32K_CK_PDN_MASK,
    465			   MT6323_RG_DRV_32K_CK_PDN);
    466
    467	mutex_destroy(&leds->lock);
    468
    469	return 0;
    470}
    471
    472static const struct of_device_id mt6323_led_dt_match[] = {
    473	{ .compatible = "mediatek,mt6323-led" },
    474	{},
    475};
    476MODULE_DEVICE_TABLE(of, mt6323_led_dt_match);
    477
    478static struct platform_driver mt6323_led_driver = {
    479	.probe		= mt6323_led_probe,
    480	.remove		= mt6323_led_remove,
    481	.driver		= {
    482		.name	= "mt6323-led",
    483		.of_match_table = mt6323_led_dt_match,
    484	},
    485};
    486
    487module_platform_driver(mt6323_led_driver);
    488
    489MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC");
    490MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
    491MODULE_LICENSE("GPL");