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-rt4505.c (11177B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include <linux/bitops.h>
      4#include <linux/i2c.h>
      5#include <linux/kernel.h>
      6#include <linux/led-class-flash.h>
      7#include <linux/module.h>
      8#include <linux/mutex.h>
      9#include <linux/property.h>
     10#include <linux/regmap.h>
     11#include <media/v4l2-flash-led-class.h>
     12
     13#define RT4505_REG_RESET	0x0
     14#define RT4505_REG_CONFIG	0x8
     15#define RT4505_REG_ILED		0x9
     16#define RT4505_REG_ENABLE	0xA
     17#define RT4505_REG_FLAGS	0xB
     18
     19#define RT4505_RESET_MASK	BIT(7)
     20#define RT4505_FLASHTO_MASK	GENMASK(2, 0)
     21#define RT4505_ITORCH_MASK	GENMASK(7, 5)
     22#define RT4505_ITORCH_SHIFT	5
     23#define RT4505_IFLASH_MASK	GENMASK(4, 0)
     24#define RT4505_ENABLE_MASK	GENMASK(5, 0)
     25#define RT4505_TORCH_SET	(BIT(0) | BIT(4))
     26#define RT4505_FLASH_SET	(BIT(0) | BIT(1) | BIT(2) | BIT(4))
     27#define RT4505_EXT_FLASH_SET	(BIT(0) | BIT(1) | BIT(4) | BIT(5))
     28#define RT4505_FLASH_GET	(BIT(0) | BIT(1) | BIT(4))
     29#define RT4505_OVP_MASK		BIT(3)
     30#define RT4505_SHORT_MASK	BIT(2)
     31#define RT4505_OTP_MASK		BIT(1)
     32#define RT4505_TIMEOUT_MASK	BIT(0)
     33
     34#define RT4505_ITORCH_MINUA	46000
     35#define RT4505_ITORCH_MAXUA	375000
     36#define RT4505_ITORCH_STPUA	47000
     37#define RT4505_IFLASH_MINUA	93750
     38#define RT4505_IFLASH_MAXUA	1500000
     39#define RT4505_IFLASH_STPUA	93750
     40#define RT4505_FLASHTO_MINUS	100000
     41#define RT4505_FLASHTO_MAXUS	800000
     42#define RT4505_FLASHTO_STPUS	100000
     43
     44struct rt4505_priv {
     45	struct device *dev;
     46	struct regmap *regmap;
     47	struct mutex lock;
     48	struct led_classdev_flash flash;
     49	struct v4l2_flash *v4l2_flash;
     50};
     51
     52static int rt4505_torch_brightness_set(struct led_classdev *lcdev,
     53				       enum led_brightness level)
     54{
     55	struct rt4505_priv *priv =
     56		container_of(lcdev, struct rt4505_priv, flash.led_cdev);
     57	u32 val = 0;
     58	int ret;
     59
     60	mutex_lock(&priv->lock);
     61
     62	if (level != LED_OFF) {
     63		ret = regmap_update_bits(priv->regmap,
     64					 RT4505_REG_ILED, RT4505_ITORCH_MASK,
     65					 (level - 1) << RT4505_ITORCH_SHIFT);
     66		if (ret)
     67			goto unlock;
     68
     69		val = RT4505_TORCH_SET;
     70	}
     71
     72	ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
     73				 RT4505_ENABLE_MASK, val);
     74
     75unlock:
     76	mutex_unlock(&priv->lock);
     77	return ret;
     78}
     79
     80static enum led_brightness rt4505_torch_brightness_get(
     81						struct led_classdev *lcdev)
     82{
     83	struct rt4505_priv *priv =
     84		container_of(lcdev, struct rt4505_priv, flash.led_cdev);
     85	u32 val;
     86	int ret;
     87
     88	mutex_lock(&priv->lock);
     89
     90	ret = regmap_read(priv->regmap, RT4505_REG_ENABLE, &val);
     91	if (ret) {
     92		dev_err(lcdev->dev, "Failed to get LED enable\n");
     93		ret = LED_OFF;
     94		goto unlock;
     95	}
     96
     97	if ((val & RT4505_ENABLE_MASK) != RT4505_TORCH_SET) {
     98		ret = LED_OFF;
     99		goto unlock;
    100	}
    101
    102	ret = regmap_read(priv->regmap, RT4505_REG_ILED, &val);
    103	if (ret) {
    104		dev_err(lcdev->dev, "Failed to get LED brightness\n");
    105		ret = LED_OFF;
    106		goto unlock;
    107	}
    108
    109	ret = ((val & RT4505_ITORCH_MASK) >> RT4505_ITORCH_SHIFT) + 1;
    110
    111unlock:
    112	mutex_unlock(&priv->lock);
    113	return ret;
    114}
    115
    116static int rt4505_flash_brightness_set(struct led_classdev_flash *fled_cdev,
    117				       u32 brightness)
    118{
    119	struct rt4505_priv *priv =
    120		container_of(fled_cdev, struct rt4505_priv, flash);
    121	struct led_flash_setting *s = &fled_cdev->brightness;
    122	u32 val = (brightness - s->min) / s->step;
    123	int ret;
    124
    125	mutex_lock(&priv->lock);
    126	ret = regmap_update_bits(priv->regmap, RT4505_REG_ILED,
    127				 RT4505_IFLASH_MASK, val);
    128	mutex_unlock(&priv->lock);
    129
    130	return ret;
    131}
    132
    133static int rt4505_flash_strobe_set(struct led_classdev_flash *fled_cdev,
    134				   bool state)
    135{
    136	struct rt4505_priv *priv =
    137		container_of(fled_cdev, struct rt4505_priv, flash);
    138	u32 val = state ? RT4505_FLASH_SET : 0;
    139	int ret;
    140
    141	mutex_lock(&priv->lock);
    142	ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
    143				 RT4505_ENABLE_MASK, val);
    144	mutex_unlock(&priv->lock);
    145
    146	return ret;
    147}
    148
    149static int rt4505_flash_strobe_get(struct led_classdev_flash *fled_cdev,
    150				   bool *state)
    151{
    152	struct rt4505_priv *priv =
    153		container_of(fled_cdev, struct rt4505_priv, flash);
    154	u32 val;
    155	int ret;
    156
    157	mutex_lock(&priv->lock);
    158
    159	ret = regmap_read(priv->regmap, RT4505_REG_ENABLE, &val);
    160	if (ret)
    161		goto unlock;
    162
    163	*state = (val & RT4505_FLASH_GET) == RT4505_FLASH_GET;
    164
    165unlock:
    166	mutex_unlock(&priv->lock);
    167	return ret;
    168}
    169
    170static int rt4505_flash_timeout_set(struct led_classdev_flash *fled_cdev,
    171				    u32 timeout)
    172{
    173	struct rt4505_priv *priv =
    174		container_of(fled_cdev, struct rt4505_priv, flash);
    175	struct led_flash_setting *s = &fled_cdev->timeout;
    176	u32 val = (timeout - s->min) / s->step;
    177	int ret;
    178
    179	mutex_lock(&priv->lock);
    180	ret = regmap_update_bits(priv->regmap, RT4505_REG_CONFIG,
    181				 RT4505_FLASHTO_MASK, val);
    182	mutex_unlock(&priv->lock);
    183
    184	return ret;
    185}
    186
    187static int rt4505_fault_get(struct led_classdev_flash *fled_cdev, u32 *fault)
    188{
    189	struct rt4505_priv *priv =
    190		container_of(fled_cdev, struct rt4505_priv, flash);
    191	u32 val, led_faults = 0;
    192	int ret;
    193
    194	ret = regmap_read(priv->regmap, RT4505_REG_FLAGS, &val);
    195	if (ret)
    196		return ret;
    197
    198	if (val & RT4505_OVP_MASK)
    199		led_faults |= LED_FAULT_OVER_VOLTAGE;
    200
    201	if (val & RT4505_SHORT_MASK)
    202		led_faults |= LED_FAULT_SHORT_CIRCUIT;
    203
    204	if (val & RT4505_OTP_MASK)
    205		led_faults |= LED_FAULT_OVER_TEMPERATURE;
    206
    207	if (val & RT4505_TIMEOUT_MASK)
    208		led_faults |= LED_FAULT_TIMEOUT;
    209
    210	*fault = led_faults;
    211	return 0;
    212}
    213
    214static const struct led_flash_ops rt4505_flash_ops = {
    215	.flash_brightness_set = rt4505_flash_brightness_set,
    216	.strobe_set = rt4505_flash_strobe_set,
    217	.strobe_get = rt4505_flash_strobe_get,
    218	.timeout_set = rt4505_flash_timeout_set,
    219	.fault_get = rt4505_fault_get,
    220};
    221
    222static bool rt4505_is_accessible_reg(struct device *dev, unsigned int reg)
    223{
    224	if (reg == RT4505_REG_RESET ||
    225		(reg >= RT4505_REG_CONFIG && reg <= RT4505_REG_FLAGS))
    226		return true;
    227	return false;
    228}
    229
    230static const struct regmap_config rt4505_regmap_config = {
    231	.reg_bits = 8,
    232	.val_bits = 8,
    233	.max_register = RT4505_REG_FLAGS,
    234
    235	.readable_reg = rt4505_is_accessible_reg,
    236	.writeable_reg = rt4505_is_accessible_reg,
    237};
    238
    239#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
    240static int rt4505_flash_external_strobe_set(struct v4l2_flash *v4l2_flash,
    241					    bool enable)
    242{
    243	struct led_classdev_flash *flash = v4l2_flash->fled_cdev;
    244	struct rt4505_priv *priv =
    245		container_of(flash, struct rt4505_priv, flash);
    246	u32 val = enable ? RT4505_EXT_FLASH_SET : 0;
    247	int ret;
    248
    249	mutex_lock(&priv->lock);
    250	ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
    251				 RT4505_ENABLE_MASK, val);
    252	mutex_unlock(&priv->lock);
    253
    254	return ret;
    255}
    256
    257static const struct v4l2_flash_ops v4l2_flash_ops = {
    258	.external_strobe_set = rt4505_flash_external_strobe_set,
    259};
    260
    261static void rt4505_init_v4l2_config(struct rt4505_priv *priv,
    262				    struct v4l2_flash_config *config)
    263{
    264	struct led_classdev_flash *flash = &priv->flash;
    265	struct led_classdev *lcdev = &flash->led_cdev;
    266	struct led_flash_setting *s;
    267
    268	strscpy(config->dev_name, lcdev->dev->kobj.name,
    269		sizeof(config->dev_name));
    270
    271	s = &config->intensity;
    272	s->min = RT4505_ITORCH_MINUA;
    273	s->step = RT4505_ITORCH_STPUA;
    274	s->max = s->val = s->min + (lcdev->max_brightness - 1) * s->step;
    275
    276	config->flash_faults = LED_FAULT_OVER_VOLTAGE |
    277			       LED_FAULT_SHORT_CIRCUIT |
    278			       LED_FAULT_LED_OVER_TEMPERATURE |
    279			       LED_FAULT_TIMEOUT;
    280	config->has_external_strobe = 1;
    281}
    282#else
    283static const struct v4l2_flash_ops v4l2_flash_ops;
    284static void rt4505_init_v4l2_config(struct rt4505_priv *priv,
    285				    struct v4l2_flash_config *config)
    286{
    287}
    288#endif
    289
    290static void rt4505_init_flash_properties(struct rt4505_priv *priv,
    291					 struct fwnode_handle *child)
    292{
    293	struct led_classdev_flash *flash = &priv->flash;
    294	struct led_classdev *lcdev = &flash->led_cdev;
    295	struct led_flash_setting *s;
    296	u32 val;
    297	int ret;
    298
    299	ret = fwnode_property_read_u32(child, "led-max-microamp", &val);
    300	if (ret) {
    301		dev_warn(priv->dev, "led-max-microamp DT property missing\n");
    302		val = RT4505_ITORCH_MINUA;
    303	} else
    304		val = clamp_val(val, RT4505_ITORCH_MINUA, RT4505_ITORCH_MAXUA);
    305
    306	lcdev->max_brightness =
    307		(val - RT4505_ITORCH_MINUA) / RT4505_ITORCH_STPUA + 1;
    308	lcdev->brightness_set_blocking = rt4505_torch_brightness_set;
    309	lcdev->brightness_get = rt4505_torch_brightness_get;
    310	lcdev->flags |= LED_DEV_CAP_FLASH;
    311
    312	ret = fwnode_property_read_u32(child, "flash-max-microamp", &val);
    313	if (ret) {
    314		dev_warn(priv->dev, "flash-max-microamp DT property missing\n");
    315		val = RT4505_IFLASH_MINUA;
    316	} else
    317		val = clamp_val(val, RT4505_IFLASH_MINUA, RT4505_IFLASH_MAXUA);
    318
    319	s = &flash->brightness;
    320	s->min = RT4505_IFLASH_MINUA;
    321	s->step = RT4505_IFLASH_STPUA;
    322	s->max = s->val = val;
    323
    324	ret = fwnode_property_read_u32(child, "flash-max-timeout-us", &val);
    325	if (ret) {
    326		dev_warn(priv->dev,
    327			 "flash-max-timeout-us DT property missing\n");
    328		val = RT4505_FLASHTO_MINUS;
    329	} else
    330		val = clamp_val(val, RT4505_FLASHTO_MINUS,
    331				RT4505_FLASHTO_MAXUS);
    332
    333	s = &flash->timeout;
    334	s->min = RT4505_FLASHTO_MINUS;
    335	s->step = RT4505_FLASHTO_STPUS;
    336	s->max = s->val = val;
    337
    338	flash->ops = &rt4505_flash_ops;
    339}
    340
    341static int rt4505_probe(struct i2c_client *client)
    342{
    343	struct rt4505_priv *priv;
    344	struct fwnode_handle *child;
    345	struct led_init_data init_data = {};
    346	struct v4l2_flash_config v4l2_config = {};
    347	int ret;
    348
    349	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    350	if (!priv)
    351		return -ENOMEM;
    352
    353	priv->dev = &client->dev;
    354	mutex_init(&priv->lock);
    355
    356	priv->regmap = devm_regmap_init_i2c(client, &rt4505_regmap_config);
    357	if (IS_ERR(priv->regmap)) {
    358		dev_err(priv->dev, "Failed to allocate register map\n");
    359		return PTR_ERR(priv->regmap);
    360	}
    361
    362	ret = regmap_write(priv->regmap, RT4505_REG_RESET, RT4505_RESET_MASK);
    363	if (ret) {
    364		dev_err(priv->dev, "Failed to reset registers\n");
    365		return ret;
    366	}
    367
    368	child = fwnode_get_next_available_child_node(client->dev.fwnode, NULL);
    369	if (!child) {
    370		dev_err(priv->dev, "Failed to get child node\n");
    371		return -EINVAL;
    372	}
    373	init_data.fwnode = child;
    374
    375	rt4505_init_flash_properties(priv, child);
    376	ret = devm_led_classdev_flash_register_ext(priv->dev, &priv->flash,
    377						   &init_data);
    378	if (ret) {
    379		dev_err(priv->dev, "Failed to register flash\n");
    380		return ret;
    381	}
    382
    383	rt4505_init_v4l2_config(priv, &v4l2_config);
    384	priv->v4l2_flash = v4l2_flash_init(priv->dev, init_data.fwnode,
    385					   &priv->flash, &v4l2_flash_ops,
    386					   &v4l2_config);
    387	if (IS_ERR(priv->v4l2_flash)) {
    388		dev_err(priv->dev, "Failed to register v4l2 flash\n");
    389		return PTR_ERR(priv->v4l2_flash);
    390	}
    391
    392	i2c_set_clientdata(client, priv);
    393	return 0;
    394}
    395
    396static int rt4505_remove(struct i2c_client *client)
    397{
    398	struct rt4505_priv *priv = i2c_get_clientdata(client);
    399
    400	v4l2_flash_release(priv->v4l2_flash);
    401	return 0;
    402}
    403
    404static void rt4505_shutdown(struct i2c_client *client)
    405{
    406	struct rt4505_priv *priv = i2c_get_clientdata(client);
    407
    408	/* Reset registers to make sure all off before shutdown */
    409	regmap_write(priv->regmap, RT4505_REG_RESET, RT4505_RESET_MASK);
    410}
    411
    412static const struct of_device_id __maybe_unused rt4505_leds_match[] = {
    413	{ .compatible = "richtek,rt4505", },
    414	{}
    415};
    416MODULE_DEVICE_TABLE(of, rt4505_leds_match);
    417
    418static struct i2c_driver rt4505_driver = {
    419	.driver = {
    420		.name = "rt4505",
    421		.of_match_table = of_match_ptr(rt4505_leds_match),
    422	},
    423	.probe_new = rt4505_probe,
    424	.remove = rt4505_remove,
    425	.shutdown = rt4505_shutdown,
    426};
    427module_i2c_driver(rt4505_driver);
    428
    429MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
    430MODULE_LICENSE("GPL v2");