^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * tps65910.c -- TI TPS6591x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright 2010 Texas Instruments Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/debugfs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/gpio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/mfd/tps65910.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define COMP1 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define COMP2 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) /* Comparator 1 voltage selection table in millivolts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static const u16 COMP_VSEL_TABLE[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) 0, 2500, 2500, 2500, 2500, 2550, 2600, 2650,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) 3500,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct comparator {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) const char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) int reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) int uV_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) const u16 *vsel_table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static struct comparator tps_comparators[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) .name = "COMP1",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) .reg = TPS65911_VMBCH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) .uV_max = 3500,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) .vsel_table = COMP_VSEL_TABLE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) .name = "COMP2",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) .reg = TPS65911_VMBCH2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) .uV_max = 3500,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) .vsel_table = COMP_VSEL_TABLE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) struct comparator tps_comp = tps_comparators[id];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) int curr_voltage = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) u8 index = 0, val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) while (curr_voltage < tps_comp.uV_max) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) curr_voltage = tps_comp.vsel_table[index];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) if (curr_voltage >= voltage)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) else if (curr_voltage < voltage)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) index ++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) if (curr_voltage > tps_comp.uV_max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) val = index << 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) ret = tps65910_reg_write(tps65910, tps_comp.reg, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static int comp_threshold_get(struct tps65910 *tps65910, int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) struct comparator tps_comp = tps_comparators[id];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) ret = tps65910_reg_read(tps65910, tps_comp.reg, &val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) val >>= 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return tps_comp.vsel_table[val];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) static ssize_t comp_threshold_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) struct tps65910 *tps65910 = dev_get_drvdata(dev->parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct attribute comp_attr = attr->attr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) int id, uVolt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (!strcmp(comp_attr.name, "comp1_threshold"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) id = COMP1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) else if (!strcmp(comp_attr.name, "comp2_threshold"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) id = COMP2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) uVolt = comp_threshold_get(tps65910, id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return sprintf(buf, "%d\n", uVolt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) static int tps65911_comparator_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) ret = comp_threshold_set(tps65910, COMP1, pdata->vmbch_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) dev_err(&pdev->dev, "cannot set COMP1 threshold\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) dev_err(&pdev->dev, "cannot set COMP2 threshold\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) /* Create sysfs entry */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) static int tps65911_comparator_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) struct tps65910 *tps65910;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) tps65910 = dev_get_drvdata(pdev->dev.parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) static struct platform_driver tps65911_comparator_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) .name = "tps65911-comparator",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) .probe = tps65911_comparator_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) .remove = tps65911_comparator_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) static int __init tps65911_comparator_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) return platform_driver_register(&tps65911_comparator_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) subsys_initcall(tps65911_comparator_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) static void __exit tps65911_comparator_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) platform_driver_unregister(&tps65911_comparator_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) module_exit(tps65911_comparator_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) MODULE_DESCRIPTION("TPS65911 comparator driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) MODULE_ALIAS("platform:tps65911-comparator");