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

regulator-haptic.c (6459B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Regulator haptic driver
      4 *
      5 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
      6 * Author: Jaewon Kim <jaewon02.kim@samsung.com>
      7 * Author: Hyunhee Kim <hyunhee.kim@samsung.com>
      8 */
      9
     10#include <linux/input.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/platform_data/regulator-haptic.h>
     14#include <linux/platform_device.h>
     15#include <linux/regulator/consumer.h>
     16#include <linux/slab.h>
     17
     18#define MAX_MAGNITUDE_SHIFT	16
     19
     20struct regulator_haptic {
     21	struct device *dev;
     22	struct input_dev *input_dev;
     23	struct regulator *regulator;
     24
     25	struct work_struct work;
     26	struct mutex mutex;
     27
     28	bool active;
     29	bool suspended;
     30
     31	unsigned int max_volt;
     32	unsigned int min_volt;
     33	unsigned int magnitude;
     34};
     35
     36static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on)
     37{
     38	int error;
     39
     40	if (haptic->active != on) {
     41
     42		error = on ? regulator_enable(haptic->regulator) :
     43			     regulator_disable(haptic->regulator);
     44		if (error) {
     45			dev_err(haptic->dev,
     46				"failed to switch regulator %s: %d\n",
     47				on ? "on" : "off", error);
     48			return error;
     49		}
     50
     51		haptic->active = on;
     52	}
     53
     54	return 0;
     55}
     56
     57static int regulator_haptic_set_voltage(struct regulator_haptic *haptic,
     58					 unsigned int magnitude)
     59{
     60	u64 volt_mag_multi;
     61	unsigned int intensity;
     62	int error;
     63
     64	volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude;
     65	intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT);
     66
     67	error = regulator_set_voltage(haptic->regulator,
     68				      intensity + haptic->min_volt,
     69				      haptic->max_volt);
     70	if (error) {
     71		dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n",
     72			intensity + haptic->min_volt, error);
     73		return error;
     74	}
     75
     76	regulator_haptic_toggle(haptic, !!magnitude);
     77
     78	return 0;
     79}
     80
     81static void regulator_haptic_work(struct work_struct *work)
     82{
     83	struct regulator_haptic *haptic = container_of(work,
     84					struct regulator_haptic, work);
     85
     86	mutex_lock(&haptic->mutex);
     87
     88	if (!haptic->suspended)
     89		regulator_haptic_set_voltage(haptic, haptic->magnitude);
     90
     91	mutex_unlock(&haptic->mutex);
     92}
     93
     94static int regulator_haptic_play_effect(struct input_dev *input, void *data,
     95					struct ff_effect *effect)
     96{
     97	struct regulator_haptic *haptic = input_get_drvdata(input);
     98
     99	haptic->magnitude = effect->u.rumble.strong_magnitude;
    100	if (!haptic->magnitude)
    101		haptic->magnitude = effect->u.rumble.weak_magnitude;
    102
    103	schedule_work(&haptic->work);
    104
    105	return 0;
    106}
    107
    108static void regulator_haptic_close(struct input_dev *input)
    109{
    110	struct regulator_haptic *haptic = input_get_drvdata(input);
    111
    112	cancel_work_sync(&haptic->work);
    113	regulator_haptic_set_voltage(haptic, 0);
    114}
    115
    116static int __maybe_unused
    117regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
    118{
    119	struct device_node *node;
    120	int error;
    121
    122	node = dev->of_node;
    123	if(!node) {
    124		dev_err(dev, "Missing device tree data\n");
    125		return -EINVAL;
    126	}
    127
    128	error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt);
    129	if (error) {
    130		dev_err(dev, "cannot parse max-microvolt\n");
    131		return error;
    132	}
    133
    134	error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt);
    135	if (error) {
    136		dev_err(dev, "cannot parse min-microvolt\n");
    137		return error;
    138	}
    139
    140	return 0;
    141}
    142
    143static int regulator_haptic_probe(struct platform_device *pdev)
    144{
    145	const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev);
    146	struct regulator_haptic *haptic;
    147	struct input_dev *input_dev;
    148	int error;
    149
    150	haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
    151	if (!haptic)
    152		return -ENOMEM;
    153
    154	platform_set_drvdata(pdev, haptic);
    155	haptic->dev = &pdev->dev;
    156	mutex_init(&haptic->mutex);
    157	INIT_WORK(&haptic->work, regulator_haptic_work);
    158
    159	if (pdata) {
    160		haptic->max_volt = pdata->max_volt;
    161		haptic->min_volt = pdata->min_volt;
    162	} else if (IS_ENABLED(CONFIG_OF)) {
    163		error = regulator_haptic_parse_dt(&pdev->dev, haptic);
    164		if (error)
    165			return error;
    166	} else {
    167		dev_err(&pdev->dev, "Missing platform data\n");
    168		return -EINVAL;
    169	}
    170
    171	haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic");
    172	if (IS_ERR(haptic->regulator)) {
    173		dev_err(&pdev->dev, "failed to get regulator\n");
    174		return PTR_ERR(haptic->regulator);
    175	}
    176
    177	input_dev = devm_input_allocate_device(&pdev->dev);
    178	if (!input_dev)
    179		return	-ENOMEM;
    180
    181	haptic->input_dev = input_dev;
    182	haptic->input_dev->name = "regulator-haptic";
    183	haptic->input_dev->dev.parent = &pdev->dev;
    184	haptic->input_dev->close = regulator_haptic_close;
    185	input_set_drvdata(haptic->input_dev, haptic);
    186	input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
    187
    188	error = input_ff_create_memless(input_dev, NULL,
    189					regulator_haptic_play_effect);
    190	if (error) {
    191		dev_err(&pdev->dev, "failed to create force-feedback\n");
    192		return error;
    193	}
    194
    195	error = input_register_device(haptic->input_dev);
    196	if (error) {
    197		dev_err(&pdev->dev, "failed to register input device\n");
    198		return error;
    199	}
    200
    201	return 0;
    202}
    203
    204static int __maybe_unused regulator_haptic_suspend(struct device *dev)
    205{
    206	struct platform_device *pdev = to_platform_device(dev);
    207	struct regulator_haptic *haptic = platform_get_drvdata(pdev);
    208	int error;
    209
    210	error = mutex_lock_interruptible(&haptic->mutex);
    211	if (error)
    212		return error;
    213
    214	regulator_haptic_set_voltage(haptic, 0);
    215
    216	haptic->suspended = true;
    217
    218	mutex_unlock(&haptic->mutex);
    219
    220	return 0;
    221}
    222
    223static int __maybe_unused regulator_haptic_resume(struct device *dev)
    224{
    225	struct platform_device *pdev = to_platform_device(dev);
    226	struct regulator_haptic *haptic = platform_get_drvdata(pdev);
    227	unsigned int magnitude;
    228
    229	mutex_lock(&haptic->mutex);
    230
    231	haptic->suspended = false;
    232
    233	magnitude = READ_ONCE(haptic->magnitude);
    234	if (magnitude)
    235		regulator_haptic_set_voltage(haptic, magnitude);
    236
    237	mutex_unlock(&haptic->mutex);
    238
    239	return 0;
    240}
    241
    242static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
    243		regulator_haptic_suspend, regulator_haptic_resume);
    244
    245static const struct of_device_id regulator_haptic_dt_match[] = {
    246	{ .compatible = "regulator-haptic" },
    247	{ /* sentinel */ },
    248};
    249MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match);
    250
    251static struct platform_driver regulator_haptic_driver = {
    252	.probe		= regulator_haptic_probe,
    253	.driver		= {
    254		.name		= "regulator-haptic",
    255		.of_match_table = regulator_haptic_dt_match,
    256		.pm		= &regulator_haptic_pm_ops,
    257	},
    258};
    259module_platform_driver(regulator_haptic_driver);
    260
    261MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
    262MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>");
    263MODULE_DESCRIPTION("Regulator haptic driver");
    264MODULE_LICENSE("GPL");