16851ad3aSJorge Eduardo Candelaria /*
26851ad3aSJorge Eduardo Candelaria  * tps65910.c  --  TI TPS6591x
36851ad3aSJorge Eduardo Candelaria  *
46851ad3aSJorge Eduardo Candelaria  * Copyright 2010 Texas Instruments Inc.
56851ad3aSJorge Eduardo Candelaria  *
66851ad3aSJorge Eduardo Candelaria  * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
76851ad3aSJorge Eduardo Candelaria  *
86851ad3aSJorge Eduardo Candelaria  *  This program is free software; you can redistribute it and/or modify it
96851ad3aSJorge Eduardo Candelaria  *  under  the terms of the GNU General  Public License as published by the
106851ad3aSJorge Eduardo Candelaria  *  Free Software Foundation;  either version 2 of the License, or (at your
116851ad3aSJorge Eduardo Candelaria  *  option) any later version.
126851ad3aSJorge Eduardo Candelaria  *
136851ad3aSJorge Eduardo Candelaria  */
146851ad3aSJorge Eduardo Candelaria 
156851ad3aSJorge Eduardo Candelaria #include <linux/kernel.h>
166851ad3aSJorge Eduardo Candelaria #include <linux/module.h>
176851ad3aSJorge Eduardo Candelaria #include <linux/init.h>
186851ad3aSJorge Eduardo Candelaria #include <linux/slab.h>
196851ad3aSJorge Eduardo Candelaria #include <linux/err.h>
206851ad3aSJorge Eduardo Candelaria #include <linux/platform_device.h>
216851ad3aSJorge Eduardo Candelaria #include <linux/debugfs.h>
226851ad3aSJorge Eduardo Candelaria #include <linux/gpio.h>
236851ad3aSJorge Eduardo Candelaria #include <linux/mfd/tps65910.h>
246851ad3aSJorge Eduardo Candelaria 
256851ad3aSJorge Eduardo Candelaria #define COMP					0
266851ad3aSJorge Eduardo Candelaria #define COMP1					1
276851ad3aSJorge Eduardo Candelaria #define COMP2					2
286851ad3aSJorge Eduardo Candelaria 
2991fe4d50SThomas Weber /* Comparator 1 voltage selection table in millivolts */
306851ad3aSJorge Eduardo Candelaria static const u16 COMP_VSEL_TABLE[] = {
316851ad3aSJorge Eduardo Candelaria 	0, 2500, 2500, 2500, 2500, 2550, 2600, 2650,
326851ad3aSJorge Eduardo Candelaria 	2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050,
336851ad3aSJorge Eduardo Candelaria 	3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450,
346851ad3aSJorge Eduardo Candelaria 	3500,
356851ad3aSJorge Eduardo Candelaria };
366851ad3aSJorge Eduardo Candelaria 
376851ad3aSJorge Eduardo Candelaria struct comparator {
386851ad3aSJorge Eduardo Candelaria 	const char *name;
396851ad3aSJorge Eduardo Candelaria 	int reg;
406851ad3aSJorge Eduardo Candelaria 	int uV_max;
416851ad3aSJorge Eduardo Candelaria 	const u16 *vsel_table;
426851ad3aSJorge Eduardo Candelaria };
436851ad3aSJorge Eduardo Candelaria 
446851ad3aSJorge Eduardo Candelaria static struct comparator tps_comparators[] = {
456851ad3aSJorge Eduardo Candelaria 	{
466851ad3aSJorge Eduardo Candelaria 		.name = "COMP1",
476851ad3aSJorge Eduardo Candelaria 		.reg = TPS65911_VMBCH,
486851ad3aSJorge Eduardo Candelaria 		.uV_max = 3500,
496851ad3aSJorge Eduardo Candelaria 		.vsel_table = COMP_VSEL_TABLE,
506851ad3aSJorge Eduardo Candelaria 	},
516851ad3aSJorge Eduardo Candelaria 	{
526851ad3aSJorge Eduardo Candelaria 		.name = "COMP2",
536851ad3aSJorge Eduardo Candelaria 		.reg = TPS65911_VMBCH2,
546851ad3aSJorge Eduardo Candelaria 		.uV_max = 3500,
556851ad3aSJorge Eduardo Candelaria 		.vsel_table = COMP_VSEL_TABLE,
566851ad3aSJorge Eduardo Candelaria 	},
576851ad3aSJorge Eduardo Candelaria };
586851ad3aSJorge Eduardo Candelaria 
596851ad3aSJorge Eduardo Candelaria static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage)
606851ad3aSJorge Eduardo Candelaria {
616851ad3aSJorge Eduardo Candelaria 	struct comparator tps_comp = tps_comparators[id];
626851ad3aSJorge Eduardo Candelaria 	int curr_voltage = 0;
636851ad3aSJorge Eduardo Candelaria 	int ret;
646851ad3aSJorge Eduardo Candelaria 	u8 index = 0, val;
656851ad3aSJorge Eduardo Candelaria 
666851ad3aSJorge Eduardo Candelaria 	if (id == COMP)
676851ad3aSJorge Eduardo Candelaria 		return 0;
686851ad3aSJorge Eduardo Candelaria 
696851ad3aSJorge Eduardo Candelaria 	while (curr_voltage < tps_comp.uV_max) {
706851ad3aSJorge Eduardo Candelaria 		curr_voltage = tps_comp.vsel_table[index];
716851ad3aSJorge Eduardo Candelaria 		if (curr_voltage >= voltage)
726851ad3aSJorge Eduardo Candelaria 			break;
736851ad3aSJorge Eduardo Candelaria 		else if (curr_voltage < voltage)
746851ad3aSJorge Eduardo Candelaria 			index ++;
756851ad3aSJorge Eduardo Candelaria 	}
766851ad3aSJorge Eduardo Candelaria 
776851ad3aSJorge Eduardo Candelaria 	if (curr_voltage > tps_comp.uV_max)
786851ad3aSJorge Eduardo Candelaria 		return -EINVAL;
796851ad3aSJorge Eduardo Candelaria 
806851ad3aSJorge Eduardo Candelaria 	val = index << 1;
81ac188616SDan Carpenter 	ret = tps65910_reg_write(tps65910, tps_comp.reg, val);
826851ad3aSJorge Eduardo Candelaria 
836851ad3aSJorge Eduardo Candelaria 	return ret;
846851ad3aSJorge Eduardo Candelaria }
856851ad3aSJorge Eduardo Candelaria 
866851ad3aSJorge Eduardo Candelaria static int comp_threshold_get(struct tps65910 *tps65910, int id)
876851ad3aSJorge Eduardo Candelaria {
886851ad3aSJorge Eduardo Candelaria 	struct comparator tps_comp = tps_comparators[id];
89ac188616SDan Carpenter 	unsigned int val;
906851ad3aSJorge Eduardo Candelaria 	int ret;
916851ad3aSJorge Eduardo Candelaria 
926851ad3aSJorge Eduardo Candelaria 	if (id == COMP)
936851ad3aSJorge Eduardo Candelaria 		return 0;
946851ad3aSJorge Eduardo Candelaria 
95ac188616SDan Carpenter 	ret = tps65910_reg_read(tps65910, tps_comp.reg, &val);
966851ad3aSJorge Eduardo Candelaria 	if (ret < 0)
976851ad3aSJorge Eduardo Candelaria 		return ret;
986851ad3aSJorge Eduardo Candelaria 
996851ad3aSJorge Eduardo Candelaria 	val >>= 1;
1006851ad3aSJorge Eduardo Candelaria 	return tps_comp.vsel_table[val];
1016851ad3aSJorge Eduardo Candelaria }
1026851ad3aSJorge Eduardo Candelaria 
1036851ad3aSJorge Eduardo Candelaria static ssize_t comp_threshold_show(struct device *dev,
1046851ad3aSJorge Eduardo Candelaria 				struct device_attribute *attr, char *buf)
1056851ad3aSJorge Eduardo Candelaria {
1066851ad3aSJorge Eduardo Candelaria 	struct tps65910 *tps65910 = dev_get_drvdata(dev->parent);
1076851ad3aSJorge Eduardo Candelaria 	struct attribute comp_attr = attr->attr;
1086851ad3aSJorge Eduardo Candelaria 	int id, uVolt;
1096851ad3aSJorge Eduardo Candelaria 
1106851ad3aSJorge Eduardo Candelaria 	if (!strcmp(comp_attr.name, "comp1_threshold"))
1116851ad3aSJorge Eduardo Candelaria 		id = COMP1;
1126851ad3aSJorge Eduardo Candelaria 	else if (!strcmp(comp_attr.name, "comp2_threshold"))
1136851ad3aSJorge Eduardo Candelaria 		id = COMP2;
1146851ad3aSJorge Eduardo Candelaria 	else
1156851ad3aSJorge Eduardo Candelaria 		return -EINVAL;
1166851ad3aSJorge Eduardo Candelaria 
1176851ad3aSJorge Eduardo Candelaria 	uVolt = comp_threshold_get(tps65910, id);
1186851ad3aSJorge Eduardo Candelaria 
1196851ad3aSJorge Eduardo Candelaria 	return sprintf(buf, "%d\n", uVolt);
1206851ad3aSJorge Eduardo Candelaria }
1216851ad3aSJorge Eduardo Candelaria 
1226851ad3aSJorge Eduardo Candelaria static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL);
1236851ad3aSJorge Eduardo Candelaria static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL);
1246851ad3aSJorge Eduardo Candelaria 
125f791be49SBill Pemberton static int tps65911_comparator_probe(struct platform_device *pdev)
1266851ad3aSJorge Eduardo Candelaria {
1276851ad3aSJorge Eduardo Candelaria 	struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
1281e8ece5cSAxel Lin 	struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
1296851ad3aSJorge Eduardo Candelaria 	int ret;
1306851ad3aSJorge Eduardo Candelaria 
1316851ad3aSJorge Eduardo Candelaria 	ret = comp_threshold_set(tps65910, COMP1,  pdata->vmbch_threshold);
1326851ad3aSJorge Eduardo Candelaria 	if (ret < 0) {
1336851ad3aSJorge Eduardo Candelaria 		dev_err(&pdev->dev, "cannot set COMP1 threshold\n");
1346851ad3aSJorge Eduardo Candelaria 		return ret;
1356851ad3aSJorge Eduardo Candelaria 	}
1366851ad3aSJorge Eduardo Candelaria 
1376851ad3aSJorge Eduardo Candelaria 	ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
1386851ad3aSJorge Eduardo Candelaria 	if (ret < 0) {
1395a4432b9SMasanari Iida 		dev_err(&pdev->dev, "cannot set COMP2 threshold\n");
1406851ad3aSJorge Eduardo Candelaria 		return ret;
1416851ad3aSJorge Eduardo Candelaria 	}
1426851ad3aSJorge Eduardo Candelaria 
1436851ad3aSJorge Eduardo Candelaria 	/* Create sysfs entry */
1446851ad3aSJorge Eduardo Candelaria 	ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold);
1456851ad3aSJorge Eduardo Candelaria 	if (ret < 0)
1466851ad3aSJorge Eduardo Candelaria 		dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n");
1476851ad3aSJorge Eduardo Candelaria 
1486851ad3aSJorge Eduardo Candelaria 	ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold);
1496851ad3aSJorge Eduardo Candelaria 	if (ret < 0)
1506851ad3aSJorge Eduardo Candelaria 		dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n");
1516851ad3aSJorge Eduardo Candelaria 
1526851ad3aSJorge Eduardo Candelaria 	return ret;
1536851ad3aSJorge Eduardo Candelaria }
1546851ad3aSJorge Eduardo Candelaria 
1554740f73fSBill Pemberton static int tps65911_comparator_remove(struct platform_device *pdev)
1566851ad3aSJorge Eduardo Candelaria {
1576851ad3aSJorge Eduardo Candelaria 	struct tps65910 *tps65910;
1586851ad3aSJorge Eduardo Candelaria 
1596851ad3aSJorge Eduardo Candelaria 	tps65910 = dev_get_drvdata(pdev->dev.parent);
1602f223341SAxel Lin 	device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
1612f223341SAxel Lin 	device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
1626851ad3aSJorge Eduardo Candelaria 
1636851ad3aSJorge Eduardo Candelaria 	return 0;
1646851ad3aSJorge Eduardo Candelaria }
1656851ad3aSJorge Eduardo Candelaria 
1666851ad3aSJorge Eduardo Candelaria static struct platform_driver tps65911_comparator_driver = {
1676851ad3aSJorge Eduardo Candelaria 	.driver = {
1686851ad3aSJorge Eduardo Candelaria 		.name = "tps65911-comparator",
1696851ad3aSJorge Eduardo Candelaria 	},
1706851ad3aSJorge Eduardo Candelaria 	.probe = tps65911_comparator_probe,
17184449216SBill Pemberton 	.remove = tps65911_comparator_remove,
1726851ad3aSJorge Eduardo Candelaria };
1736851ad3aSJorge Eduardo Candelaria 
1746851ad3aSJorge Eduardo Candelaria static int __init tps65911_comparator_init(void)
1756851ad3aSJorge Eduardo Candelaria {
1766851ad3aSJorge Eduardo Candelaria 	return platform_driver_register(&tps65911_comparator_driver);
1776851ad3aSJorge Eduardo Candelaria }
1786851ad3aSJorge Eduardo Candelaria subsys_initcall(tps65911_comparator_init);
1796851ad3aSJorge Eduardo Candelaria 
1806851ad3aSJorge Eduardo Candelaria static void __exit tps65911_comparator_exit(void)
1816851ad3aSJorge Eduardo Candelaria {
1826851ad3aSJorge Eduardo Candelaria 	platform_driver_unregister(&tps65911_comparator_driver);
1836851ad3aSJorge Eduardo Candelaria }
1846851ad3aSJorge Eduardo Candelaria module_exit(tps65911_comparator_exit);
1856851ad3aSJorge Eduardo Candelaria 
1866851ad3aSJorge Eduardo Candelaria MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
1876851ad3aSJorge Eduardo Candelaria MODULE_DESCRIPTION("TPS65911 comparator driver");
1886851ad3aSJorge Eduardo Candelaria MODULE_LICENSE("GPL v2");
1896851ad3aSJorge Eduardo Candelaria MODULE_ALIAS("platform:tps65911-comparator");
190