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

as3711_bl.c (11590B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * AS3711 PMIC backlight driver, using DCDC Step Up Converters
      4 *
      5 * Copyright (C) 2012 Renesas Electronics Corporation
      6 * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
      7 */
      8
      9#include <linux/backlight.h>
     10#include <linux/delay.h>
     11#include <linux/device.h>
     12#include <linux/err.h>
     13#include <linux/fb.h>
     14#include <linux/kernel.h>
     15#include <linux/mfd/as3711.h>
     16#include <linux/module.h>
     17#include <linux/platform_device.h>
     18#include <linux/regmap.h>
     19#include <linux/slab.h>
     20
     21enum as3711_bl_type {
     22	AS3711_BL_SU1,
     23	AS3711_BL_SU2,
     24};
     25
     26struct as3711_bl_data {
     27	bool powered;
     28	enum as3711_bl_type type;
     29	int brightness;
     30	struct backlight_device *bl;
     31};
     32
     33struct as3711_bl_supply {
     34	struct as3711_bl_data su1;
     35	struct as3711_bl_data su2;
     36	const struct as3711_bl_pdata *pdata;
     37	struct as3711 *as3711;
     38};
     39
     40static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su)
     41{
     42	switch (su->type) {
     43	case AS3711_BL_SU1:
     44		return container_of(su, struct as3711_bl_supply, su1);
     45	case AS3711_BL_SU2:
     46		return container_of(su, struct as3711_bl_supply, su2);
     47	}
     48	return NULL;
     49}
     50
     51static int as3711_set_brightness_auto_i(struct as3711_bl_data *data,
     52					unsigned int brightness)
     53{
     54	struct as3711_bl_supply *supply = to_supply(data);
     55	struct as3711 *as3711 = supply->as3711;
     56	const struct as3711_bl_pdata *pdata = supply->pdata;
     57	int ret = 0;
     58
     59	/* Only all equal current values are supported */
     60	if (pdata->su2_auto_curr1)
     61		ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
     62				   brightness);
     63	if (!ret && pdata->su2_auto_curr2)
     64		ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
     65				   brightness);
     66	if (!ret && pdata->su2_auto_curr3)
     67		ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
     68				   brightness);
     69
     70	return ret;
     71}
     72
     73static int as3711_set_brightness_v(struct as3711 *as3711,
     74				   unsigned int brightness,
     75				   unsigned int reg)
     76{
     77	if (brightness > 31)
     78		return -EINVAL;
     79
     80	return regmap_update_bits(as3711->regmap, reg, 0xf0,
     81				  brightness << 4);
     82}
     83
     84static int as3711_bl_su2_reset(struct as3711_bl_supply *supply)
     85{
     86	struct as3711 *as3711 = supply->as3711;
     87	int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5,
     88				     3, supply->pdata->su2_fbprot);
     89	if (!ret)
     90		ret = regmap_update_bits(as3711->regmap,
     91					 AS3711_STEPUP_CONTROL_2, 1, 0);
     92	if (!ret)
     93		ret = regmap_update_bits(as3711->regmap,
     94					 AS3711_STEPUP_CONTROL_2, 1, 1);
     95	return ret;
     96}
     97
     98/*
     99 * Someone with less fragile or less expensive hardware could try to simplify
    100 * the brightness adjustment procedure.
    101 */
    102static int as3711_bl_update_status(struct backlight_device *bl)
    103{
    104	struct as3711_bl_data *data = bl_get_data(bl);
    105	struct as3711_bl_supply *supply = to_supply(data);
    106	struct as3711 *as3711 = supply->as3711;
    107	int brightness;
    108	int ret = 0;
    109
    110	brightness = backlight_get_brightness(bl);
    111
    112	if (data->type == AS3711_BL_SU1) {
    113		ret = as3711_set_brightness_v(as3711, brightness,
    114					      AS3711_STEPUP_CONTROL_1);
    115	} else {
    116		const struct as3711_bl_pdata *pdata = supply->pdata;
    117
    118		switch (pdata->su2_feedback) {
    119		case AS3711_SU2_VOLTAGE:
    120			ret = as3711_set_brightness_v(as3711, brightness,
    121						      AS3711_STEPUP_CONTROL_2);
    122			break;
    123		case AS3711_SU2_CURR_AUTO:
    124			ret = as3711_set_brightness_auto_i(data, brightness / 4);
    125			if (ret < 0)
    126				return ret;
    127			if (brightness) {
    128				ret = as3711_bl_su2_reset(supply);
    129				if (ret < 0)
    130					return ret;
    131				udelay(500);
    132				ret = as3711_set_brightness_auto_i(data, brightness);
    133			} else {
    134				ret = regmap_update_bits(as3711->regmap,
    135						AS3711_STEPUP_CONTROL_2, 1, 0);
    136			}
    137			break;
    138		/* Manual one current feedback pin below */
    139		case AS3711_SU2_CURR1:
    140			ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
    141					   brightness);
    142			break;
    143		case AS3711_SU2_CURR2:
    144			ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
    145					   brightness);
    146			break;
    147		case AS3711_SU2_CURR3:
    148			ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
    149					   brightness);
    150			break;
    151		default:
    152			ret = -EINVAL;
    153		}
    154	}
    155	if (!ret)
    156		data->brightness = brightness;
    157
    158	return ret;
    159}
    160
    161static int as3711_bl_get_brightness(struct backlight_device *bl)
    162{
    163	struct as3711_bl_data *data = bl_get_data(bl);
    164
    165	return data->brightness;
    166}
    167
    168static const struct backlight_ops as3711_bl_ops = {
    169	.update_status	= as3711_bl_update_status,
    170	.get_brightness	= as3711_bl_get_brightness,
    171};
    172
    173static int as3711_bl_init_su2(struct as3711_bl_supply *supply)
    174{
    175	struct as3711 *as3711 = supply->as3711;
    176	const struct as3711_bl_pdata *pdata = supply->pdata;
    177	u8 ctl = 0;
    178	int ret;
    179
    180	dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback);
    181
    182	/* Turn SU2 off */
    183	ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0);
    184	if (ret < 0)
    185		return ret;
    186
    187	switch (pdata->su2_feedback) {
    188	case AS3711_SU2_VOLTAGE:
    189		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0);
    190		break;
    191	case AS3711_SU2_CURR1:
    192		ctl = 1;
    193		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1);
    194		break;
    195	case AS3711_SU2_CURR2:
    196		ctl = 4;
    197		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2);
    198		break;
    199	case AS3711_SU2_CURR3:
    200		ctl = 0x10;
    201		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3);
    202		break;
    203	case AS3711_SU2_CURR_AUTO:
    204		if (pdata->su2_auto_curr1)
    205			ctl = 2;
    206		if (pdata->su2_auto_curr2)
    207			ctl |= 8;
    208		if (pdata->su2_auto_curr3)
    209			ctl |= 0x20;
    210		ret = 0;
    211		break;
    212	default:
    213		return -EINVAL;
    214	}
    215
    216	if (!ret)
    217		ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl);
    218
    219	return ret;
    220}
    221
    222static int as3711_bl_register(struct platform_device *pdev,
    223			      unsigned int max_brightness, struct as3711_bl_data *su)
    224{
    225	struct backlight_properties props = {.type = BACKLIGHT_RAW,};
    226	struct backlight_device *bl;
    227
    228	/* max tuning I = 31uA for voltage- and 38250uA for current-feedback */
    229	props.max_brightness = max_brightness;
    230
    231	bl = devm_backlight_device_register(&pdev->dev,
    232				       su->type == AS3711_BL_SU1 ?
    233				       "as3711-su1" : "as3711-su2",
    234				       &pdev->dev, su,
    235				       &as3711_bl_ops, &props);
    236	if (IS_ERR(bl)) {
    237		dev_err(&pdev->dev, "failed to register backlight\n");
    238		return PTR_ERR(bl);
    239	}
    240
    241	bl->props.brightness = props.max_brightness;
    242
    243	backlight_update_status(bl);
    244
    245	su->bl = bl;
    246
    247	return 0;
    248}
    249
    250static int as3711_backlight_parse_dt(struct device *dev)
    251{
    252	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
    253	struct device_node *bl, *fb;
    254	int ret;
    255
    256	bl = of_get_child_by_name(dev->parent->of_node, "backlight");
    257	if (!bl) {
    258		dev_dbg(dev, "backlight node not found\n");
    259		return -ENODEV;
    260	}
    261
    262	fb = of_parse_phandle(bl, "su1-dev", 0);
    263	if (fb) {
    264		of_node_put(fb);
    265
    266		pdata->su1_fb = true;
    267
    268		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
    269		if (pdata->su1_max_uA <= 0)
    270			ret = -EINVAL;
    271		if (ret < 0)
    272			goto err_put_bl;
    273	}
    274
    275	fb = of_parse_phandle(bl, "su2-dev", 0);
    276	if (fb) {
    277		int count = 0;
    278
    279		of_node_put(fb);
    280
    281		pdata->su2_fb = true;
    282
    283		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
    284		if (pdata->su2_max_uA <= 0)
    285			ret = -EINVAL;
    286		if (ret < 0)
    287			goto err_put_bl;
    288
    289		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
    290			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
    291			count++;
    292		}
    293		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
    294			pdata->su2_feedback = AS3711_SU2_CURR1;
    295			count++;
    296		}
    297		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
    298			pdata->su2_feedback = AS3711_SU2_CURR2;
    299			count++;
    300		}
    301		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
    302			pdata->su2_feedback = AS3711_SU2_CURR3;
    303			count++;
    304		}
    305		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
    306			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
    307			count++;
    308		}
    309		if (count != 1) {
    310			ret = -EINVAL;
    311			goto err_put_bl;
    312		}
    313
    314		count = 0;
    315		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
    316			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
    317			count++;
    318		}
    319		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
    320			pdata->su2_fbprot = AS3711_SU2_GPIO2;
    321			count++;
    322		}
    323		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
    324			pdata->su2_fbprot = AS3711_SU2_GPIO3;
    325			count++;
    326		}
    327		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
    328			pdata->su2_fbprot = AS3711_SU2_GPIO4;
    329			count++;
    330		}
    331		if (count != 1) {
    332			ret = -EINVAL;
    333			goto err_put_bl;
    334		}
    335
    336		count = 0;
    337		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
    338			pdata->su2_auto_curr1 = true;
    339			count++;
    340		}
    341		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
    342			pdata->su2_auto_curr2 = true;
    343			count++;
    344		}
    345		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
    346			pdata->su2_auto_curr3 = true;
    347			count++;
    348		}
    349
    350		/*
    351		 * At least one su2-auto-curr* must be specified iff
    352		 * AS3711_SU2_CURR_AUTO is used
    353		 */
    354		if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO)) {
    355			ret = -EINVAL;
    356			goto err_put_bl;
    357		}
    358	}
    359
    360	of_node_put(bl);
    361
    362	return 0;
    363
    364err_put_bl:
    365	of_node_put(bl);
    366
    367	return ret;
    368}
    369
    370static int as3711_backlight_probe(struct platform_device *pdev)
    371{
    372	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
    373	struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
    374	struct as3711_bl_supply *supply;
    375	struct as3711_bl_data *su;
    376	unsigned int max_brightness;
    377	int ret;
    378
    379	if (!pdata) {
    380		dev_err(&pdev->dev, "No platform data, exiting...\n");
    381		return -ENODEV;
    382	}
    383
    384	if (pdev->dev.parent->of_node) {
    385		ret = as3711_backlight_parse_dt(&pdev->dev);
    386		if (ret < 0) {
    387			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
    388			return ret;
    389		}
    390	}
    391
    392	if (!pdata->su1_fb && !pdata->su2_fb) {
    393		dev_err(&pdev->dev, "No framebuffer specified\n");
    394		return -EINVAL;
    395	}
    396
    397	/*
    398	 * Due to possible hardware damage I chose to block all modes,
    399	 * unsupported on my hardware. Anyone, wishing to use any of those modes
    400	 * will have to first review the code, then activate and test it.
    401	 */
    402	if (pdata->su1_fb ||
    403	    pdata->su2_fbprot != AS3711_SU2_GPIO4 ||
    404	    pdata->su2_feedback != AS3711_SU2_CURR_AUTO) {
    405		dev_warn(&pdev->dev,
    406			 "Attention! An untested mode has been chosen!\n"
    407			 "Please, review the code, enable, test, and report success:-)\n");
    408		return -EINVAL;
    409	}
    410
    411	supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL);
    412	if (!supply)
    413		return -ENOMEM;
    414
    415	supply->as3711 = as3711;
    416	supply->pdata = pdata;
    417
    418	if (pdata->su1_fb) {
    419		su = &supply->su1;
    420		su->type = AS3711_BL_SU1;
    421
    422		max_brightness = min(pdata->su1_max_uA, 31);
    423		ret = as3711_bl_register(pdev, max_brightness, su);
    424		if (ret < 0)
    425			return ret;
    426	}
    427
    428	if (pdata->su2_fb) {
    429		su = &supply->su2;
    430		su->type = AS3711_BL_SU2;
    431
    432		switch (pdata->su2_fbprot) {
    433		case AS3711_SU2_GPIO2:
    434		case AS3711_SU2_GPIO3:
    435		case AS3711_SU2_GPIO4:
    436		case AS3711_SU2_LX_SD4:
    437			break;
    438		default:
    439			return -EINVAL;
    440		}
    441
    442		switch (pdata->su2_feedback) {
    443		case AS3711_SU2_VOLTAGE:
    444			max_brightness = min(pdata->su2_max_uA, 31);
    445			break;
    446		case AS3711_SU2_CURR1:
    447		case AS3711_SU2_CURR2:
    448		case AS3711_SU2_CURR3:
    449		case AS3711_SU2_CURR_AUTO:
    450			max_brightness = min(pdata->su2_max_uA / 150, 255);
    451			break;
    452		default:
    453			return -EINVAL;
    454		}
    455
    456		ret = as3711_bl_init_su2(supply);
    457		if (ret < 0)
    458			return ret;
    459
    460		ret = as3711_bl_register(pdev, max_brightness, su);
    461		if (ret < 0)
    462			return ret;
    463	}
    464
    465	platform_set_drvdata(pdev, supply);
    466
    467	return 0;
    468}
    469
    470static struct platform_driver as3711_backlight_driver = {
    471	.driver		= {
    472		.name	= "as3711-backlight",
    473	},
    474	.probe		= as3711_backlight_probe,
    475};
    476
    477module_platform_driver(as3711_backlight_driver);
    478
    479MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs");
    480MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de");
    481MODULE_LICENSE("GPL v2");
    482MODULE_ALIAS("platform:as3711-backlight");