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

pwm-vibra.c (6449B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  PWM vibrator driver
      4 *
      5 *  Copyright (C) 2017 Collabora Ltd.
      6 *
      7 *  Based on previous work from:
      8 *  Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov@gmail.com>
      9 *
     10 *  Based on PWM beeper driver:
     11 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
     12 */
     13
     14#include <linux/input.h>
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/of_device.h>
     18#include <linux/platform_device.h>
     19#include <linux/property.h>
     20#include <linux/pwm.h>
     21#include <linux/regulator/consumer.h>
     22#include <linux/slab.h>
     23
     24struct pwm_vibrator {
     25	struct input_dev *input;
     26	struct pwm_device *pwm;
     27	struct pwm_device *pwm_dir;
     28	struct regulator *vcc;
     29
     30	struct work_struct play_work;
     31	u16 level;
     32	u32 direction_duty_cycle;
     33	bool vcc_on;
     34};
     35
     36static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
     37{
     38	struct device *pdev = vibrator->input->dev.parent;
     39	struct pwm_state state;
     40	int err;
     41
     42	if (!vibrator->vcc_on) {
     43		err = regulator_enable(vibrator->vcc);
     44		if (err) {
     45			dev_err(pdev, "failed to enable regulator: %d", err);
     46			return err;
     47		}
     48		vibrator->vcc_on = true;
     49	}
     50
     51	pwm_get_state(vibrator->pwm, &state);
     52	pwm_set_relative_duty_cycle(&state, vibrator->level, 0xffff);
     53	state.enabled = true;
     54
     55	err = pwm_apply_state(vibrator->pwm, &state);
     56	if (err) {
     57		dev_err(pdev, "failed to apply pwm state: %d", err);
     58		return err;
     59	}
     60
     61	if (vibrator->pwm_dir) {
     62		pwm_get_state(vibrator->pwm_dir, &state);
     63		state.duty_cycle = vibrator->direction_duty_cycle;
     64		state.enabled = true;
     65
     66		err = pwm_apply_state(vibrator->pwm_dir, &state);
     67		if (err) {
     68			dev_err(pdev, "failed to apply dir-pwm state: %d", err);
     69			pwm_disable(vibrator->pwm);
     70			return err;
     71		}
     72	}
     73
     74	return 0;
     75}
     76
     77static void pwm_vibrator_stop(struct pwm_vibrator *vibrator)
     78{
     79	if (vibrator->pwm_dir)
     80		pwm_disable(vibrator->pwm_dir);
     81	pwm_disable(vibrator->pwm);
     82
     83	if (vibrator->vcc_on) {
     84		regulator_disable(vibrator->vcc);
     85		vibrator->vcc_on = false;
     86	}
     87}
     88
     89static void pwm_vibrator_play_work(struct work_struct *work)
     90{
     91	struct pwm_vibrator *vibrator = container_of(work,
     92					struct pwm_vibrator, play_work);
     93
     94	if (vibrator->level)
     95		pwm_vibrator_start(vibrator);
     96	else
     97		pwm_vibrator_stop(vibrator);
     98}
     99
    100static int pwm_vibrator_play_effect(struct input_dev *dev, void *data,
    101				    struct ff_effect *effect)
    102{
    103	struct pwm_vibrator *vibrator = input_get_drvdata(dev);
    104
    105	vibrator->level = effect->u.rumble.strong_magnitude;
    106	if (!vibrator->level)
    107		vibrator->level = effect->u.rumble.weak_magnitude;
    108
    109	schedule_work(&vibrator->play_work);
    110
    111	return 0;
    112}
    113
    114static void pwm_vibrator_close(struct input_dev *input)
    115{
    116	struct pwm_vibrator *vibrator = input_get_drvdata(input);
    117
    118	cancel_work_sync(&vibrator->play_work);
    119	pwm_vibrator_stop(vibrator);
    120}
    121
    122static int pwm_vibrator_probe(struct platform_device *pdev)
    123{
    124	struct pwm_vibrator *vibrator;
    125	struct pwm_state state;
    126	int err;
    127
    128	vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
    129	if (!vibrator)
    130		return -ENOMEM;
    131
    132	vibrator->input = devm_input_allocate_device(&pdev->dev);
    133	if (!vibrator->input)
    134		return -ENOMEM;
    135
    136	vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
    137	err = PTR_ERR_OR_ZERO(vibrator->vcc);
    138	if (err) {
    139		if (err != -EPROBE_DEFER)
    140			dev_err(&pdev->dev, "Failed to request regulator: %d",
    141				err);
    142		return err;
    143	}
    144
    145	vibrator->pwm = devm_pwm_get(&pdev->dev, "enable");
    146	err = PTR_ERR_OR_ZERO(vibrator->pwm);
    147	if (err) {
    148		if (err != -EPROBE_DEFER)
    149			dev_err(&pdev->dev, "Failed to request main pwm: %d",
    150				err);
    151		return err;
    152	}
    153
    154	INIT_WORK(&vibrator->play_work, pwm_vibrator_play_work);
    155
    156	/* Sync up PWM state and ensure it is off. */
    157	pwm_init_state(vibrator->pwm, &state);
    158	state.enabled = false;
    159	err = pwm_apply_state(vibrator->pwm, &state);
    160	if (err) {
    161		dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
    162			err);
    163		return err;
    164	}
    165
    166	vibrator->pwm_dir = devm_pwm_get(&pdev->dev, "direction");
    167	err = PTR_ERR_OR_ZERO(vibrator->pwm_dir);
    168	switch (err) {
    169	case 0:
    170		/* Sync up PWM state and ensure it is off. */
    171		pwm_init_state(vibrator->pwm_dir, &state);
    172		state.enabled = false;
    173		err = pwm_apply_state(vibrator->pwm_dir, &state);
    174		if (err) {
    175			dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
    176				err);
    177			return err;
    178		}
    179
    180		vibrator->direction_duty_cycle =
    181			pwm_get_period(vibrator->pwm_dir) / 2;
    182		device_property_read_u32(&pdev->dev, "direction-duty-cycle-ns",
    183					 &vibrator->direction_duty_cycle);
    184		break;
    185
    186	case -ENODATA:
    187		/* Direction PWM is optional */
    188		vibrator->pwm_dir = NULL;
    189		break;
    190
    191	default:
    192		dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
    193		fallthrough;
    194
    195	case -EPROBE_DEFER:
    196		return err;
    197	}
    198
    199	vibrator->input->name = "pwm-vibrator";
    200	vibrator->input->id.bustype = BUS_HOST;
    201	vibrator->input->dev.parent = &pdev->dev;
    202	vibrator->input->close = pwm_vibrator_close;
    203
    204	input_set_drvdata(vibrator->input, vibrator);
    205	input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
    206
    207	err = input_ff_create_memless(vibrator->input, NULL,
    208				      pwm_vibrator_play_effect);
    209	if (err) {
    210		dev_err(&pdev->dev, "Couldn't create FF dev: %d", err);
    211		return err;
    212	}
    213
    214	err = input_register_device(vibrator->input);
    215	if (err) {
    216		dev_err(&pdev->dev, "Couldn't register input dev: %d", err);
    217		return err;
    218	}
    219
    220	platform_set_drvdata(pdev, vibrator);
    221
    222	return 0;
    223}
    224
    225static int __maybe_unused pwm_vibrator_suspend(struct device *dev)
    226{
    227	struct pwm_vibrator *vibrator = dev_get_drvdata(dev);
    228
    229	cancel_work_sync(&vibrator->play_work);
    230	if (vibrator->level)
    231		pwm_vibrator_stop(vibrator);
    232
    233	return 0;
    234}
    235
    236static int __maybe_unused pwm_vibrator_resume(struct device *dev)
    237{
    238	struct pwm_vibrator *vibrator = dev_get_drvdata(dev);
    239
    240	if (vibrator->level)
    241		pwm_vibrator_start(vibrator);
    242
    243	return 0;
    244}
    245
    246static SIMPLE_DEV_PM_OPS(pwm_vibrator_pm_ops,
    247			 pwm_vibrator_suspend, pwm_vibrator_resume);
    248
    249#ifdef CONFIG_OF
    250static const struct of_device_id pwm_vibra_dt_match_table[] = {
    251	{ .compatible = "pwm-vibrator" },
    252	{},
    253};
    254MODULE_DEVICE_TABLE(of, pwm_vibra_dt_match_table);
    255#endif
    256
    257static struct platform_driver pwm_vibrator_driver = {
    258	.probe	= pwm_vibrator_probe,
    259	.driver	= {
    260		.name	= "pwm-vibrator",
    261		.pm	= &pwm_vibrator_pm_ops,
    262		.of_match_table = of_match_ptr(pwm_vibra_dt_match_table),
    263	},
    264};
    265module_platform_driver(pwm_vibrator_driver);
    266
    267MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
    268MODULE_DESCRIPTION("PWM vibrator driver");
    269MODULE_LICENSE("GPL");
    270MODULE_ALIAS("platform:pwm-vibrator");