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-sc27xx-bltc.c (9138B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (C) 2018 Spreadtrum Communications Inc.
      3
      4#include <linux/leds.h>
      5#include <linux/module.h>
      6#include <linux/of.h>
      7#include <linux/platform_device.h>
      8#include <linux/regmap.h>
      9
     10/* PMIC global control register definition */
     11#define SC27XX_MODULE_EN0	0xc08
     12#define SC27XX_CLK_EN0		0xc18
     13#define SC27XX_RGB_CTRL		0xebc
     14
     15#define SC27XX_BLTC_EN		BIT(9)
     16#define SC27XX_RTC_EN		BIT(7)
     17#define SC27XX_RGB_PD		BIT(0)
     18
     19/* Breathing light controller register definition */
     20#define SC27XX_LEDS_CTRL	0x00
     21#define SC27XX_LEDS_PRESCALE	0x04
     22#define SC27XX_LEDS_DUTY	0x08
     23#define SC27XX_LEDS_CURVE0	0x0c
     24#define SC27XX_LEDS_CURVE1	0x10
     25
     26#define SC27XX_CTRL_SHIFT	4
     27#define SC27XX_LED_RUN		BIT(0)
     28#define SC27XX_LED_TYPE		BIT(1)
     29
     30#define SC27XX_DUTY_SHIFT	8
     31#define SC27XX_DUTY_MASK	GENMASK(15, 0)
     32#define SC27XX_MOD_MASK		GENMASK(7, 0)
     33
     34#define SC27XX_CURVE_SHIFT	8
     35#define SC27XX_CURVE_L_MASK	GENMASK(7, 0)
     36#define SC27XX_CURVE_H_MASK	GENMASK(15, 8)
     37
     38#define SC27XX_LEDS_OFFSET	0x10
     39#define SC27XX_LEDS_MAX		3
     40#define SC27XX_LEDS_PATTERN_CNT	4
     41/* Stage duration step, in milliseconds */
     42#define SC27XX_LEDS_STEP	125
     43/* Minimum and maximum duration, in milliseconds */
     44#define SC27XX_DELTA_T_MIN	SC27XX_LEDS_STEP
     45#define SC27XX_DELTA_T_MAX	(SC27XX_LEDS_STEP * 255)
     46
     47struct sc27xx_led {
     48	struct fwnode_handle *fwnode;
     49	struct led_classdev ldev;
     50	struct sc27xx_led_priv *priv;
     51	u8 line;
     52	bool active;
     53};
     54
     55struct sc27xx_led_priv {
     56	struct sc27xx_led leds[SC27XX_LEDS_MAX];
     57	struct regmap *regmap;
     58	struct mutex lock;
     59	u32 base;
     60};
     61
     62#define to_sc27xx_led(ldev) \
     63	container_of(ldev, struct sc27xx_led, ldev)
     64
     65static int sc27xx_led_init(struct regmap *regmap)
     66{
     67	int err;
     68
     69	err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
     70				 SC27XX_BLTC_EN);
     71	if (err)
     72		return err;
     73
     74	err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
     75				 SC27XX_RTC_EN);
     76	if (err)
     77		return err;
     78
     79	return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
     80}
     81
     82static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
     83{
     84	return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
     85}
     86
     87static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
     88{
     89	u32 base = sc27xx_led_get_offset(leds);
     90	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
     91	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
     92	struct regmap *regmap = leds->priv->regmap;
     93	int err;
     94
     95	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
     96				 SC27XX_DUTY_MASK,
     97				 (value << SC27XX_DUTY_SHIFT) |
     98				 SC27XX_MOD_MASK);
     99	if (err)
    100		return err;
    101
    102	return regmap_update_bits(regmap, ctrl_base,
    103			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
    104			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
    105}
    106
    107static int sc27xx_led_disable(struct sc27xx_led *leds)
    108{
    109	struct regmap *regmap = leds->priv->regmap;
    110	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
    111	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
    112
    113	return regmap_update_bits(regmap, ctrl_base,
    114			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
    115}
    116
    117static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
    118{
    119	struct sc27xx_led *leds = to_sc27xx_led(ldev);
    120	int err;
    121
    122	mutex_lock(&leds->priv->lock);
    123
    124	if (value == LED_OFF)
    125		err = sc27xx_led_disable(leds);
    126	else
    127		err = sc27xx_led_enable(leds, value);
    128
    129	mutex_unlock(&leds->priv->lock);
    130
    131	return err;
    132}
    133
    134static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
    135{
    136	u32 v, offset, t = *delta_t;
    137
    138	v = t + SC27XX_LEDS_STEP / 2;
    139	v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
    140	offset = v - SC27XX_DELTA_T_MIN;
    141	offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
    142
    143	*delta_t = SC27XX_DELTA_T_MIN + offset;
    144}
    145
    146static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
    147{
    148	struct sc27xx_led *leds = to_sc27xx_led(ldev);
    149	struct regmap *regmap = leds->priv->regmap;
    150	u32 base = sc27xx_led_get_offset(leds);
    151	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
    152	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
    153	int err;
    154
    155	mutex_lock(&leds->priv->lock);
    156
    157	/* Reset the rise, high, fall and low time to zero. */
    158	regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
    159	regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
    160
    161	err = regmap_update_bits(regmap, ctrl_base,
    162			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
    163
    164	ldev->brightness = LED_OFF;
    165
    166	mutex_unlock(&leds->priv->lock);
    167
    168	return err;
    169}
    170
    171static int sc27xx_led_pattern_set(struct led_classdev *ldev,
    172				  struct led_pattern *pattern,
    173				  u32 len, int repeat)
    174{
    175	struct sc27xx_led *leds = to_sc27xx_led(ldev);
    176	u32 base = sc27xx_led_get_offset(leds);
    177	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
    178	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
    179	struct regmap *regmap = leds->priv->regmap;
    180	int err;
    181
    182	/*
    183	 * Must contain 4 tuples to configure the rise time, high time, fall
    184	 * time and low time to enable the breathing mode.
    185	 */
    186	if (len != SC27XX_LEDS_PATTERN_CNT)
    187		return -EINVAL;
    188
    189	mutex_lock(&leds->priv->lock);
    190
    191	sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
    192	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
    193				 SC27XX_CURVE_L_MASK,
    194				 pattern[0].delta_t / SC27XX_LEDS_STEP);
    195	if (err)
    196		goto out;
    197
    198	sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
    199	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
    200				 SC27XX_CURVE_L_MASK,
    201				 pattern[1].delta_t / SC27XX_LEDS_STEP);
    202	if (err)
    203		goto out;
    204
    205	sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
    206	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
    207				 SC27XX_CURVE_H_MASK,
    208				 (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
    209				 SC27XX_CURVE_SHIFT);
    210	if (err)
    211		goto out;
    212
    213	sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
    214	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
    215				 SC27XX_CURVE_H_MASK,
    216				 (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
    217				 SC27XX_CURVE_SHIFT);
    218	if (err)
    219		goto out;
    220
    221	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
    222				 SC27XX_DUTY_MASK,
    223				 (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
    224				 SC27XX_MOD_MASK);
    225	if (err)
    226		goto out;
    227
    228	/* Enable the LED breathing mode */
    229	err = regmap_update_bits(regmap, ctrl_base,
    230				 SC27XX_LED_RUN << ctrl_shift,
    231				 SC27XX_LED_RUN << ctrl_shift);
    232	if (!err)
    233		ldev->brightness = pattern[1].brightness;
    234
    235out:
    236	mutex_unlock(&leds->priv->lock);
    237
    238	return err;
    239}
    240
    241static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
    242{
    243	int i, err;
    244
    245	err = sc27xx_led_init(priv->regmap);
    246	if (err)
    247		return err;
    248
    249	for (i = 0; i < SC27XX_LEDS_MAX; i++) {
    250		struct sc27xx_led *led = &priv->leds[i];
    251		struct led_init_data init_data = {};
    252
    253		if (!led->active)
    254			continue;
    255
    256		led->line = i;
    257		led->priv = priv;
    258		led->ldev.brightness_set_blocking = sc27xx_led_set;
    259		led->ldev.pattern_set = sc27xx_led_pattern_set;
    260		led->ldev.pattern_clear = sc27xx_led_pattern_clear;
    261		led->ldev.default_trigger = "pattern";
    262
    263		init_data.fwnode = led->fwnode;
    264		init_data.devicename = "sc27xx";
    265		init_data.default_label = ":";
    266
    267		err = devm_led_classdev_register_ext(dev, &led->ldev,
    268						     &init_data);
    269		if (err)
    270			return err;
    271	}
    272
    273	return 0;
    274}
    275
    276static int sc27xx_led_probe(struct platform_device *pdev)
    277{
    278	struct device *dev = &pdev->dev;
    279	struct device_node *np = dev_of_node(dev), *child;
    280	struct sc27xx_led_priv *priv;
    281	u32 base, count, reg;
    282	int err;
    283
    284	count = of_get_available_child_count(np);
    285	if (!count || count > SC27XX_LEDS_MAX)
    286		return -EINVAL;
    287
    288	err = of_property_read_u32(np, "reg", &base);
    289	if (err) {
    290		dev_err(dev, "fail to get reg of property\n");
    291		return err;
    292	}
    293
    294	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    295	if (!priv)
    296		return -ENOMEM;
    297
    298	platform_set_drvdata(pdev, priv);
    299	mutex_init(&priv->lock);
    300	priv->base = base;
    301	priv->regmap = dev_get_regmap(dev->parent, NULL);
    302	if (!priv->regmap) {
    303		err = -ENODEV;
    304		dev_err(dev, "failed to get regmap: %d\n", err);
    305		return err;
    306	}
    307
    308	for_each_available_child_of_node(np, child) {
    309		err = of_property_read_u32(child, "reg", &reg);
    310		if (err) {
    311			of_node_put(child);
    312			mutex_destroy(&priv->lock);
    313			return err;
    314		}
    315
    316		if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
    317			of_node_put(child);
    318			mutex_destroy(&priv->lock);
    319			return -EINVAL;
    320		}
    321
    322		priv->leds[reg].fwnode = of_fwnode_handle(child);
    323		priv->leds[reg].active = true;
    324	}
    325
    326	err = sc27xx_led_register(dev, priv);
    327	if (err)
    328		mutex_destroy(&priv->lock);
    329
    330	return err;
    331}
    332
    333static int sc27xx_led_remove(struct platform_device *pdev)
    334{
    335	struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
    336
    337	mutex_destroy(&priv->lock);
    338	return 0;
    339}
    340
    341static const struct of_device_id sc27xx_led_of_match[] = {
    342	{ .compatible = "sprd,sc2731-bltc", },
    343	{ }
    344};
    345MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
    346
    347static struct platform_driver sc27xx_led_driver = {
    348	.driver = {
    349		.name = "sprd-bltc",
    350		.of_match_table = sc27xx_led_of_match,
    351	},
    352	.probe = sc27xx_led_probe,
    353	.remove = sc27xx_led_remove,
    354};
    355
    356module_platform_driver(sc27xx_led_driver);
    357
    358MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
    359MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
    360MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
    361MODULE_LICENSE("GPL v2");