123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/errno.h>
- #include <linux/hrtimer.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/leds.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/of_device.h>
- #include <linux/regmap.h>
- #include <linux/workqueue.h>
- /* Vibrator-LDO register definitions */
- #define QPNP_VIB_LDO_REG_STATUS1 0x08
- #define QPNP_VIB_LDO_VREG_READY BIT(7)
- #define QPNP_VIB_LDO_REG_VSET_LB 0x40
- #define QPNP_VIB_LDO_REG_EN_CTL 0x46
- #define QPNP_VIB_LDO_EN BIT(7)
- /* Vibrator-LDO voltage settings */
- #define QPNP_VIB_LDO_VMIN_UV 1504000
- #define QPNP_VIB_LDO_VMAX_UV 3544000
- #define QPNP_VIB_LDO_VOLT_STEP_UV 8000
- /*
- * Define vibration periods: default(5sec), min(50ms), max(15sec) and
- * overdrive(30ms).
- */
- #define QPNP_VIB_MIN_PLAY_MS 50
- #define QPNP_VIB_PLAY_MS 5000
- #define QPNP_VIB_MAX_PLAY_MS 15000
- #define QPNP_VIB_OVERDRIVE_PLAY_MS 30
- struct vib_ldo_chip {
- struct led_classdev cdev;
- struct regmap *regmap;
- struct mutex lock;
- struct hrtimer stop_timer;
- struct hrtimer overdrive_timer;
- struct work_struct vib_work;
- struct work_struct overdrive_work;
- u16 base;
- int vmax_uV;
- int overdrive_volt_uV;
- int ldo_uV;
- int state;
- u64 vib_play_ms;
- bool vib_enabled;
- bool disable_overdrive;
- };
- static int qpnp_vib_ldo_set_voltage(struct vib_ldo_chip *chip, int new_uV)
- {
- unsigned int val;
- u32 vlevel;
- u8 reg[2];
- int ret;
- if (chip->ldo_uV == new_uV)
- return 0;
- vlevel = roundup(new_uV, QPNP_VIB_LDO_VOLT_STEP_UV) / 1000;
- reg[0] = vlevel & 0xff;
- reg[1] = (vlevel & 0xff00) >> 8;
- ret = regmap_bulk_write(chip->regmap,
- chip->base + QPNP_VIB_LDO_REG_VSET_LB, reg, 2);
- if (ret < 0) {
- pr_err("regmap write failed, ret=%d\n", ret);
- return ret;
- }
- if (chip->vib_enabled) {
- ret = regmap_read_poll_timeout(chip->regmap,
- chip->base + QPNP_VIB_LDO_REG_STATUS1,
- val, val & QPNP_VIB_LDO_VREG_READY,
- 100, 1000);
- if (ret < 0) {
- pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n",
- val, ret);
- return ret;
- }
- }
- chip->ldo_uV = new_uV;
- return ret;
- }
- static inline int qpnp_vib_ldo_enable(struct vib_ldo_chip *chip, bool enable)
- {
- unsigned int val;
- int ret;
- if (chip->vib_enabled == enable)
- return 0;
- ret = regmap_update_bits(chip->regmap,
- chip->base + QPNP_VIB_LDO_REG_EN_CTL,
- QPNP_VIB_LDO_EN,
- enable ? QPNP_VIB_LDO_EN : 0);
- if (ret < 0) {
- pr_err("Program Vibrator LDO %s is failed, ret=%d\n",
- enable ? "enable" : "disable", ret);
- return ret;
- }
- if (enable) {
- ret = regmap_read_poll_timeout(chip->regmap,
- chip->base + QPNP_VIB_LDO_REG_STATUS1,
- val, val & QPNP_VIB_LDO_VREG_READY,
- 100, 1000);
- if (ret < 0) {
- pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n",
- val, ret);
- return ret;
- }
- }
- chip->vib_enabled = enable;
- return ret;
- }
- static int qpnp_vibrator_play_on(struct vib_ldo_chip *chip)
- {
- int volt_uV;
- int ret;
- volt_uV = chip->vmax_uV;
- if (!chip->disable_overdrive)
- volt_uV = chip->overdrive_volt_uV ? chip->overdrive_volt_uV
- : min(chip->vmax_uV * 2, QPNP_VIB_LDO_VMAX_UV);
- ret = qpnp_vib_ldo_set_voltage(chip, volt_uV);
- if (ret < 0) {
- pr_err("set voltage = %duV failed, ret=%d\n", volt_uV, ret);
- return ret;
- }
- pr_debug("voltage set to %d uV\n", volt_uV);
- ret = qpnp_vib_ldo_enable(chip, true);
- if (ret < 0) {
- pr_err("vibration enable failed, ret=%d\n", ret);
- return ret;
- }
- if (!chip->disable_overdrive)
- hrtimer_start(&chip->overdrive_timer,
- ms_to_ktime(QPNP_VIB_OVERDRIVE_PLAY_MS),
- HRTIMER_MODE_REL);
- return ret;
- }
- static void qpnp_vib_work(struct work_struct *work)
- {
- struct vib_ldo_chip *chip = container_of(work, struct vib_ldo_chip,
- vib_work);
- int ret = 0;
- if (chip->state) {
- if (!chip->vib_enabled)
- ret = qpnp_vibrator_play_on(chip);
- if (ret == 0)
- hrtimer_start(&chip->stop_timer,
- ms_to_ktime(chip->vib_play_ms),
- HRTIMER_MODE_REL);
- } else {
- if (!chip->disable_overdrive) {
- hrtimer_cancel(&chip->overdrive_timer);
- cancel_work_sync(&chip->overdrive_work);
- }
- qpnp_vib_ldo_enable(chip, false);
- }
- }
- static enum hrtimer_restart vib_stop_timer(struct hrtimer *timer)
- {
- struct vib_ldo_chip *chip = container_of(timer, struct vib_ldo_chip,
- stop_timer);
- chip->state = 0;
- schedule_work(&chip->vib_work);
- return HRTIMER_NORESTART;
- }
- static void qpnp_vib_overdrive_work(struct work_struct *work)
- {
- struct vib_ldo_chip *chip = container_of(work, struct vib_ldo_chip,
- overdrive_work);
- int ret;
- mutex_lock(&chip->lock);
- /* LDO voltage update not required if Vibration disabled */
- if (!chip->vib_enabled)
- goto unlock;
- ret = qpnp_vib_ldo_set_voltage(chip, chip->vmax_uV);
- if (ret < 0) {
- pr_err("set vibration voltage = %duV failed, ret=%d\n",
- chip->vmax_uV, ret);
- qpnp_vib_ldo_enable(chip, false);
- goto unlock;
- }
- pr_debug("voltage set to %d\n", chip->vmax_uV);
- unlock:
- mutex_unlock(&chip->lock);
- }
- static enum hrtimer_restart vib_overdrive_timer(struct hrtimer *timer)
- {
- struct vib_ldo_chip *chip = container_of(timer, struct vib_ldo_chip,
- overdrive_timer);
- schedule_work(&chip->overdrive_work);
- pr_debug("overdrive timer expired\n");
- return HRTIMER_NORESTART;
- }
- static ssize_t qpnp_vib_show_state(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip,
- cdev);
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->vib_enabled);
- }
- static ssize_t qpnp_vib_store_state(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- /* At present, nothing to do with setting state */
- return count;
- }
- static ssize_t qpnp_vib_show_duration(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip,
- cdev);
- ktime_t time_rem;
- s64 time_ms = 0;
- if (hrtimer_active(&chip->stop_timer)) {
- time_rem = hrtimer_get_remaining(&chip->stop_timer);
- time_ms = ktime_to_ms(time_rem);
- }
- return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms);
- }
- static ssize_t qpnp_vib_store_duration(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip,
- cdev);
- u32 val;
- int ret;
- ret = kstrtouint(buf, 0, &val);
- if (ret < 0)
- return ret;
- /* setting 0 on duration is NOP for now */
- if (val <= 0)
- return count;
- if (val < QPNP_VIB_MIN_PLAY_MS)
- val = QPNP_VIB_MIN_PLAY_MS;
- if (val > QPNP_VIB_MAX_PLAY_MS)
- val = QPNP_VIB_MAX_PLAY_MS;
- mutex_lock(&chip->lock);
- chip->vib_play_ms = val;
- mutex_unlock(&chip->lock);
- return count;
- }
- static ssize_t qpnp_vib_show_activate(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- /* For now nothing to show */
- return snprintf(buf, PAGE_SIZE, "%d\n", 0);
- }
- static ssize_t qpnp_vib_store_activate(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip,
- cdev);
- u32 val;
- int ret;
- ret = kstrtouint(buf, 0, &val);
- if (ret < 0)
- return ret;
- if (val != 0 && val != 1)
- return count;
- mutex_lock(&chip->lock);
- hrtimer_cancel(&chip->stop_timer);
- chip->state = val;
- pr_debug("state = %d, time = %llums\n", chip->state, chip->vib_play_ms);
- mutex_unlock(&chip->lock);
- schedule_work(&chip->vib_work);
- return count;
- }
- static ssize_t qpnp_vib_show_vmax(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip,
- cdev);
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->vmax_uV / 1000);
- }
- static ssize_t qpnp_vib_store_vmax(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip,
- cdev);
- int data, ret;
- ret = kstrtoint(buf, 10, &data);
- if (ret < 0)
- return ret;
- data = data * 1000; /* Convert to microvolts */
- /* check against vibrator ldo min/max voltage limits */
- data = min(data, QPNP_VIB_LDO_VMAX_UV);
- data = max(data, QPNP_VIB_LDO_VMIN_UV);
- mutex_lock(&chip->lock);
- chip->vmax_uV = data;
- mutex_unlock(&chip->lock);
- return ret;
- }
- static struct device_attribute qpnp_vib_attrs[] = {
- __ATTR(state, 0664, qpnp_vib_show_state, qpnp_vib_store_state),
- __ATTR(duration, 0664, qpnp_vib_show_duration, qpnp_vib_store_duration),
- __ATTR(activate, 0664, qpnp_vib_show_activate, qpnp_vib_store_activate),
- __ATTR(vmax_mv, 0664, qpnp_vib_show_vmax, qpnp_vib_store_vmax),
- };
- static int qpnp_vib_parse_dt(struct device *dev, struct vib_ldo_chip *chip)
- {
- int ret;
- ret = of_property_read_u32(dev->of_node, "qcom,vib-ldo-volt-uv",
- &chip->vmax_uV);
- if (ret < 0) {
- pr_err("qcom,vib-ldo-volt-uv property read failed, ret=%d\n",
- ret);
- return ret;
- }
- chip->disable_overdrive = of_property_read_bool(dev->of_node,
- "qcom,disable-overdrive");
- if (of_find_property(dev->of_node, "qcom,vib-overdrive-volt-uv",
- NULL)) {
- ret = of_property_read_u32(dev->of_node,
- "qcom,vib-overdrive-volt-uv",
- &chip->overdrive_volt_uV);
- if (ret < 0) {
- pr_err("qcom,vib-overdrive-volt-uv property read failed, ret=%d\n",
- ret);
- return ret;
- }
- /* check against vibrator ldo min/max voltage limits */
- chip->overdrive_volt_uV = min(chip->overdrive_volt_uV,
- QPNP_VIB_LDO_VMAX_UV);
- chip->overdrive_volt_uV = max(chip->overdrive_volt_uV,
- QPNP_VIB_LDO_VMIN_UV);
- }
- return ret;
- }
- /* Dummy functions for brightness */
- static enum led_brightness qpnp_vib_brightness_get(struct led_classdev *cdev)
- {
- return 0;
- }
- static void qpnp_vib_brightness_set(struct led_classdev *cdev,
- enum led_brightness level)
- {
- }
- static int qpnp_vibrator_ldo_suspend(struct device *dev)
- {
- struct vib_ldo_chip *chip = dev_get_drvdata(dev);
- mutex_lock(&chip->lock);
- if (!chip->disable_overdrive) {
- hrtimer_cancel(&chip->overdrive_timer);
- cancel_work_sync(&chip->overdrive_work);
- }
- hrtimer_cancel(&chip->stop_timer);
- cancel_work_sync(&chip->vib_work);
- mutex_unlock(&chip->lock);
- return 0;
- }
- static SIMPLE_DEV_PM_OPS(qpnp_vibrator_ldo_pm_ops, qpnp_vibrator_ldo_suspend,
- NULL);
- static int qpnp_vibrator_ldo_probe(struct platform_device *pdev)
- {
- struct device_node *of_node = pdev->dev.of_node;
- struct vib_ldo_chip *chip;
- int i, ret;
- u32 base;
- ret = of_property_read_u32(of_node, "reg", &base);
- if (ret < 0) {
- pr_err("reg property reading failed, ret=%d\n", ret);
- return ret;
- }
- chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- pr_err("couldn't get parent's regmap\n");
- return -EINVAL;
- }
- ret = qpnp_vib_parse_dt(&pdev->dev, chip);
- if (ret < 0) {
- pr_err("couldn't parse device tree, ret=%d\n", ret);
- return ret;
- }
- chip->base = (uint16_t)base;
- chip->vib_play_ms = QPNP_VIB_PLAY_MS;
- mutex_init(&chip->lock);
- INIT_WORK(&chip->vib_work, qpnp_vib_work);
- INIT_WORK(&chip->overdrive_work, qpnp_vib_overdrive_work);
- hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- chip->stop_timer.function = vib_stop_timer;
- hrtimer_init(&chip->overdrive_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- chip->overdrive_timer.function = vib_overdrive_timer;
- dev_set_drvdata(&pdev->dev, chip);
- chip->cdev.name = "vibrator";
- chip->cdev.brightness_get = qpnp_vib_brightness_get;
- chip->cdev.brightness_set = qpnp_vib_brightness_set;
- chip->cdev.max_brightness = 100;
- ret = devm_led_classdev_register(&pdev->dev, &chip->cdev);
- if (ret < 0) {
- pr_err("Error in registering led class device, ret=%d\n", ret);
- goto fail;
- }
- for (i = 0; i < ARRAY_SIZE(qpnp_vib_attrs); i++) {
- ret = sysfs_create_file(&chip->cdev.dev->kobj,
- &qpnp_vib_attrs[i].attr);
- if (ret < 0) {
- dev_err(&pdev->dev, "Error in creating sysfs file, ret=%d\n",
- ret);
- goto sysfs_fail;
- }
- }
- pr_info("Vibrator LDO successfully registered: uV = %d, overdrive = %s\n",
- chip->vmax_uV,
- chip->disable_overdrive ? "disabled" : "enabled");
- return 0;
- sysfs_fail:
- for (--i; i >= 0; i--)
- sysfs_remove_file(&chip->cdev.dev->kobj,
- &qpnp_vib_attrs[i].attr);
- fail:
- mutex_destroy(&chip->lock);
- dev_set_drvdata(&pdev->dev, NULL);
- return ret;
- }
- static int qpnp_vibrator_ldo_remove(struct platform_device *pdev)
- {
- struct vib_ldo_chip *chip = dev_get_drvdata(&pdev->dev);
- if (!chip->disable_overdrive) {
- hrtimer_cancel(&chip->overdrive_timer);
- cancel_work_sync(&chip->overdrive_work);
- }
- hrtimer_cancel(&chip->stop_timer);
- cancel_work_sync(&chip->vib_work);
- mutex_destroy(&chip->lock);
- dev_set_drvdata(&pdev->dev, NULL);
- return 0;
- }
- static const struct of_device_id vibrator_ldo_match_table[] = {
- { .compatible = "qcom,qpnp-vibrator-ldo" },
- { /* sentinel */ },
- };
- MODULE_DEVICE_TABLE(of, vibrator_ldo_match_table);
- static struct platform_driver qpnp_vibrator_ldo_driver = {
- .driver = {
- .name = "qcom,qpnp-vibrator-ldo",
- .of_match_table = vibrator_ldo_match_table,
- .pm = &qpnp_vibrator_ldo_pm_ops,
- },
- .probe = qpnp_vibrator_ldo_probe,
- .remove = qpnp_vibrator_ldo_remove,
- };
- module_platform_driver(qpnp_vibrator_ldo_driver);
- MODULE_DESCRIPTION("QCOM QPNP Vibrator-LDO driver");
- MODULE_LICENSE("GPL v2");
|