12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26851ad3aSJorge Eduardo Candelaria /*
36851ad3aSJorge Eduardo Candelaria  * tps65910.c  --  TI TPS6591x
46851ad3aSJorge Eduardo Candelaria  *
56851ad3aSJorge Eduardo Candelaria  * Copyright 2010 Texas Instruments Inc.
66851ad3aSJorge Eduardo Candelaria  *
76851ad3aSJorge Eduardo Candelaria  * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
86851ad3aSJorge Eduardo Candelaria  */
96851ad3aSJorge Eduardo Candelaria 
106851ad3aSJorge Eduardo Candelaria #include <linux/kernel.h>
116851ad3aSJorge Eduardo Candelaria #include <linux/module.h>
126851ad3aSJorge Eduardo Candelaria #include <linux/init.h>
136851ad3aSJorge Eduardo Candelaria #include <linux/slab.h>
146851ad3aSJorge Eduardo Candelaria #include <linux/err.h>
156851ad3aSJorge Eduardo Candelaria #include <linux/platform_device.h>
166851ad3aSJorge Eduardo Candelaria #include <linux/debugfs.h>
176851ad3aSJorge Eduardo Candelaria #include <linux/gpio.h>
186851ad3aSJorge Eduardo Candelaria #include <linux/mfd/tps65910.h>
196851ad3aSJorge Eduardo Candelaria 
201768391cSLee Jones #define COMP1					0
211768391cSLee Jones #define COMP2					1
226851ad3aSJorge Eduardo Candelaria 
2391fe4d50SThomas Weber /* Comparator 1 voltage selection table in millivolts */
246851ad3aSJorge Eduardo Candelaria static const u16 COMP_VSEL_TABLE[] = {
256851ad3aSJorge Eduardo Candelaria 	0, 2500, 2500, 2500, 2500, 2550, 2600, 2650,
266851ad3aSJorge Eduardo Candelaria 	2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050,
276851ad3aSJorge Eduardo Candelaria 	3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450,
286851ad3aSJorge Eduardo Candelaria 	3500,
296851ad3aSJorge Eduardo Candelaria };
306851ad3aSJorge Eduardo Candelaria 
316851ad3aSJorge Eduardo Candelaria struct comparator {
326851ad3aSJorge Eduardo Candelaria 	const char *name;
336851ad3aSJorge Eduardo Candelaria 	int reg;
346851ad3aSJorge Eduardo Candelaria 	int uV_max;
356851ad3aSJorge Eduardo Candelaria 	const u16 *vsel_table;
366851ad3aSJorge Eduardo Candelaria };
376851ad3aSJorge Eduardo Candelaria 
386851ad3aSJorge Eduardo Candelaria static struct comparator tps_comparators[] = {
396851ad3aSJorge Eduardo Candelaria 	{
406851ad3aSJorge Eduardo Candelaria 		.name = "COMP1",
416851ad3aSJorge Eduardo Candelaria 		.reg = TPS65911_VMBCH,
426851ad3aSJorge Eduardo Candelaria 		.uV_max = 3500,
436851ad3aSJorge Eduardo Candelaria 		.vsel_table = COMP_VSEL_TABLE,
446851ad3aSJorge Eduardo Candelaria 	},
456851ad3aSJorge Eduardo Candelaria 	{
466851ad3aSJorge Eduardo Candelaria 		.name = "COMP2",
476851ad3aSJorge Eduardo Candelaria 		.reg = TPS65911_VMBCH2,
486851ad3aSJorge Eduardo Candelaria 		.uV_max = 3500,
496851ad3aSJorge Eduardo Candelaria 		.vsel_table = COMP_VSEL_TABLE,
506851ad3aSJorge Eduardo Candelaria 	},
516851ad3aSJorge Eduardo Candelaria };
526851ad3aSJorge Eduardo Candelaria 
comp_threshold_set(struct tps65910 * tps65910,int id,int voltage)536851ad3aSJorge Eduardo Candelaria static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage)
546851ad3aSJorge Eduardo Candelaria {
556851ad3aSJorge Eduardo Candelaria 	struct comparator tps_comp = tps_comparators[id];
566851ad3aSJorge Eduardo Candelaria 	int curr_voltage = 0;
576851ad3aSJorge Eduardo Candelaria 	int ret;
586851ad3aSJorge Eduardo Candelaria 	u8 index = 0, val;
596851ad3aSJorge Eduardo Candelaria 
606851ad3aSJorge Eduardo Candelaria 	while (curr_voltage < tps_comp.uV_max) {
616851ad3aSJorge Eduardo Candelaria 		curr_voltage = tps_comp.vsel_table[index];
626851ad3aSJorge Eduardo Candelaria 		if (curr_voltage >= voltage)
636851ad3aSJorge Eduardo Candelaria 			break;
646851ad3aSJorge Eduardo Candelaria 		else if (curr_voltage < voltage)
656851ad3aSJorge Eduardo Candelaria 			index ++;
666851ad3aSJorge Eduardo Candelaria 	}
676851ad3aSJorge Eduardo Candelaria 
686851ad3aSJorge Eduardo Candelaria 	if (curr_voltage > tps_comp.uV_max)
696851ad3aSJorge Eduardo Candelaria 		return -EINVAL;
706851ad3aSJorge Eduardo Candelaria 
716851ad3aSJorge Eduardo Candelaria 	val = index << 1;
72*da7d203fSMichał Mirosław 	ret = regmap_write(tps65910->regmap, tps_comp.reg, val);
736851ad3aSJorge Eduardo Candelaria 
746851ad3aSJorge Eduardo Candelaria 	return ret;
756851ad3aSJorge Eduardo Candelaria }
766851ad3aSJorge Eduardo Candelaria 
comp_threshold_get(struct tps65910 * tps65910,int id)776851ad3aSJorge Eduardo Candelaria static int comp_threshold_get(struct tps65910 *tps65910, int id)
786851ad3aSJorge Eduardo Candelaria {
796851ad3aSJorge Eduardo Candelaria 	struct comparator tps_comp = tps_comparators[id];
80ac188616SDan Carpenter 	unsigned int val;
816851ad3aSJorge Eduardo Candelaria 	int ret;
826851ad3aSJorge Eduardo Candelaria 
83*da7d203fSMichał Mirosław 	ret = regmap_read(tps65910->regmap, tps_comp.reg, &val);
846851ad3aSJorge Eduardo Candelaria 	if (ret < 0)
856851ad3aSJorge Eduardo Candelaria 		return ret;
866851ad3aSJorge Eduardo Candelaria 
876851ad3aSJorge Eduardo Candelaria 	val >>= 1;
886851ad3aSJorge Eduardo Candelaria 	return tps_comp.vsel_table[val];
896851ad3aSJorge Eduardo Candelaria }
906851ad3aSJorge Eduardo Candelaria 
comp_threshold_show(struct device * dev,struct device_attribute * attr,char * buf)916851ad3aSJorge Eduardo Candelaria static ssize_t comp_threshold_show(struct device *dev,
926851ad3aSJorge Eduardo Candelaria 				struct device_attribute *attr, char *buf)
936851ad3aSJorge Eduardo Candelaria {
946851ad3aSJorge Eduardo Candelaria 	struct tps65910 *tps65910 = dev_get_drvdata(dev->parent);
956851ad3aSJorge Eduardo Candelaria 	struct attribute comp_attr = attr->attr;
966851ad3aSJorge Eduardo Candelaria 	int id, uVolt;
976851ad3aSJorge Eduardo Candelaria 
986851ad3aSJorge Eduardo Candelaria 	if (!strcmp(comp_attr.name, "comp1_threshold"))
996851ad3aSJorge Eduardo Candelaria 		id = COMP1;
1006851ad3aSJorge Eduardo Candelaria 	else if (!strcmp(comp_attr.name, "comp2_threshold"))
1016851ad3aSJorge Eduardo Candelaria 		id = COMP2;
1026851ad3aSJorge Eduardo Candelaria 	else
1036851ad3aSJorge Eduardo Candelaria 		return -EINVAL;
1046851ad3aSJorge Eduardo Candelaria 
1056851ad3aSJorge Eduardo Candelaria 	uVolt = comp_threshold_get(tps65910, id);
1066851ad3aSJorge Eduardo Candelaria 
1076851ad3aSJorge Eduardo Candelaria 	return sprintf(buf, "%d\n", uVolt);
1086851ad3aSJorge Eduardo Candelaria }
1096851ad3aSJorge Eduardo Candelaria 
1106851ad3aSJorge Eduardo Candelaria static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL);
1116851ad3aSJorge Eduardo Candelaria static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL);
1126851ad3aSJorge Eduardo Candelaria 
tps65911_comparator_probe(struct platform_device * pdev)113f791be49SBill Pemberton static int tps65911_comparator_probe(struct platform_device *pdev)
1146851ad3aSJorge Eduardo Candelaria {
1156851ad3aSJorge Eduardo Candelaria 	struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
1161e8ece5cSAxel Lin 	struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
1176851ad3aSJorge Eduardo Candelaria 	int ret;
1186851ad3aSJorge Eduardo Candelaria 
1196851ad3aSJorge Eduardo Candelaria 	ret = comp_threshold_set(tps65910, COMP1,  pdata->vmbch_threshold);
1206851ad3aSJorge Eduardo Candelaria 	if (ret < 0) {
1216851ad3aSJorge Eduardo Candelaria 		dev_err(&pdev->dev, "cannot set COMP1 threshold\n");
1226851ad3aSJorge Eduardo Candelaria 		return ret;
1236851ad3aSJorge Eduardo Candelaria 	}
1246851ad3aSJorge Eduardo Candelaria 
1256851ad3aSJorge Eduardo Candelaria 	ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
1266851ad3aSJorge Eduardo Candelaria 	if (ret < 0) {
1275a4432b9SMasanari Iida 		dev_err(&pdev->dev, "cannot set COMP2 threshold\n");
1286851ad3aSJorge Eduardo Candelaria 		return ret;
1296851ad3aSJorge Eduardo Candelaria 	}
1306851ad3aSJorge Eduardo Candelaria 
1316851ad3aSJorge Eduardo Candelaria 	/* Create sysfs entry */
1326851ad3aSJorge Eduardo Candelaria 	ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold);
1336851ad3aSJorge Eduardo Candelaria 	if (ret < 0)
1346851ad3aSJorge Eduardo Candelaria 		dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n");
1356851ad3aSJorge Eduardo Candelaria 
1366851ad3aSJorge Eduardo Candelaria 	ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold);
1376851ad3aSJorge Eduardo Candelaria 	if (ret < 0)
1386851ad3aSJorge Eduardo Candelaria 		dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n");
1396851ad3aSJorge Eduardo Candelaria 
1406851ad3aSJorge Eduardo Candelaria 	return ret;
1416851ad3aSJorge Eduardo Candelaria }
1426851ad3aSJorge Eduardo Candelaria 
tps65911_comparator_remove(struct platform_device * pdev)1434740f73fSBill Pemberton static int tps65911_comparator_remove(struct platform_device *pdev)
1446851ad3aSJorge Eduardo Candelaria {
1456851ad3aSJorge Eduardo Candelaria 	struct tps65910 *tps65910;
1466851ad3aSJorge Eduardo Candelaria 
1476851ad3aSJorge Eduardo Candelaria 	tps65910 = dev_get_drvdata(pdev->dev.parent);
1482f223341SAxel Lin 	device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
1492f223341SAxel Lin 	device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
1506851ad3aSJorge Eduardo Candelaria 
1516851ad3aSJorge Eduardo Candelaria 	return 0;
1526851ad3aSJorge Eduardo Candelaria }
1536851ad3aSJorge Eduardo Candelaria 
1546851ad3aSJorge Eduardo Candelaria static struct platform_driver tps65911_comparator_driver = {
1556851ad3aSJorge Eduardo Candelaria 	.driver = {
1566851ad3aSJorge Eduardo Candelaria 		.name = "tps65911-comparator",
1576851ad3aSJorge Eduardo Candelaria 	},
1586851ad3aSJorge Eduardo Candelaria 	.probe = tps65911_comparator_probe,
15984449216SBill Pemberton 	.remove = tps65911_comparator_remove,
1606851ad3aSJorge Eduardo Candelaria };
1616851ad3aSJorge Eduardo Candelaria 
tps65911_comparator_init(void)1626851ad3aSJorge Eduardo Candelaria static int __init tps65911_comparator_init(void)
1636851ad3aSJorge Eduardo Candelaria {
1646851ad3aSJorge Eduardo Candelaria 	return platform_driver_register(&tps65911_comparator_driver);
1656851ad3aSJorge Eduardo Candelaria }
1666851ad3aSJorge Eduardo Candelaria subsys_initcall(tps65911_comparator_init);
1676851ad3aSJorge Eduardo Candelaria 
tps65911_comparator_exit(void)1686851ad3aSJorge Eduardo Candelaria static void __exit tps65911_comparator_exit(void)
1696851ad3aSJorge Eduardo Candelaria {
1706851ad3aSJorge Eduardo Candelaria 	platform_driver_unregister(&tps65911_comparator_driver);
1716851ad3aSJorge Eduardo Candelaria }
1726851ad3aSJorge Eduardo Candelaria module_exit(tps65911_comparator_exit);
1736851ad3aSJorge Eduardo Candelaria 
1746851ad3aSJorge Eduardo Candelaria MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
1756851ad3aSJorge Eduardo Candelaria MODULE_DESCRIPTION("TPS65911 comparator driver");
1766851ad3aSJorge Eduardo Candelaria MODULE_LICENSE("GPL v2");
1776851ad3aSJorge Eduardo Candelaria MODULE_ALIAS("platform:tps65911-comparator");
178