/* 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 #include #include #include #include #include #include #include #include #include /* 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 10000 #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 struct vib_ldo_chip *g_chip; static inline int qpnp_vib_ldo_poll_status(struct vib_ldo_chip *chip) { unsigned int val; int ret; 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); /* Keep VIB_LDO disabled */ regmap_update_bits(chip->regmap, chip->base + QPNP_VIB_LDO_REG_EN_CTL, QPNP_VIB_LDO_EN, 0); } return ret; } static int qpnp_vib_ldo_set_voltage(struct vib_ldo_chip *chip, int new_uV) { u32 vlevel; u8 reg[2]; int ret; pr_info("[VIB]%s, ldo_uV: %d new_uV: %d\n",__func__, chip->ldo_uV, new_uV); 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 = qpnp_vib_ldo_poll_status(chip); if (ret < 0) { pr_err("Vibrator LDO status polling timedout\n"); return ret; } } chip->ldo_uV = new_uV; return ret; } inline int qpnp_vib_ldo_enable(struct vib_ldo_chip *chip, bool enable) { int ret; pr_info("[VIB]%s, vib_enabled: %d enable: %d\n",__func__, chip->vib_enabled, enable); 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 = qpnp_vib_ldo_poll_status(chip); if (ret < 0) { pr_err("Vibrator LDO status polling timedout\n"); return ret; } } chip->vib_enabled = enable; return ret; } extern void ss_vib_ldo_enable(bool enable) { g_chip->state = enable; schedule_work(&g_chip->vib_work); } 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; } 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; pr_info("[VIB]%s\n",__func__); 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); qpnp_vib_ldo_enable(chip, false); 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"); g_chip = chip; 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");