18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * Copyright (C) 2011 Philippe Rétornaz
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Based on twl4030-pwrbutton driver by:
58c2ecf20Sopenharmony_ci *     Peter De Schrijver <peter.de-schrijver@nokia.com>
68c2ecf20Sopenharmony_ci *     Felipe Balbi <felipe.balbi@nokia.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
98c2ecf20Sopenharmony_ci * Public License. See the file "COPYING" in the main directory of this
108c2ecf20Sopenharmony_ci * archive for more details.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
158c2ecf20Sopenharmony_ci * GNU General Public License for more details.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
188c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
198c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335  USA
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel.h>
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/input.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
288c2ecf20Sopenharmony_ci#include <linux/mfd/mc13783.h>
298c2ecf20Sopenharmony_ci#include <linux/sched.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct mc13783_pwrb {
338c2ecf20Sopenharmony_ci	struct input_dev *pwr;
348c2ecf20Sopenharmony_ci	struct mc13xxx *mc13783;
358c2ecf20Sopenharmony_ci#define MC13783_PWRB_B1_POL_INVERT	(1 << 0)
368c2ecf20Sopenharmony_ci#define MC13783_PWRB_B2_POL_INVERT	(1 << 1)
378c2ecf20Sopenharmony_ci#define MC13783_PWRB_B3_POL_INVERT	(1 << 2)
388c2ecf20Sopenharmony_ci	int flags;
398c2ecf20Sopenharmony_ci	unsigned short keymap[3];
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define MC13783_REG_INTERRUPT_SENSE_1		5
438c2ecf20Sopenharmony_ci#define MC13783_IRQSENSE1_ONOFD1S		(1 << 3)
448c2ecf20Sopenharmony_ci#define MC13783_IRQSENSE1_ONOFD2S		(1 << 4)
458c2ecf20Sopenharmony_ci#define MC13783_IRQSENSE1_ONOFD3S		(1 << 5)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define MC13783_REG_POWER_CONTROL_2		15
488c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON1BDBNC	4
498c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON2BDBNC	6
508c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON3BDBNC	8
518c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON1BRSTEN	(1 << 1)
528c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON2BRSTEN	(1 << 2)
538c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON3BRSTEN	(1 << 3)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic irqreturn_t button_irq(int irq, void *_priv)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct mc13783_pwrb *priv = _priv;
588c2ecf20Sopenharmony_ci	int val;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	mc13xxx_irq_ack(priv->mc13783, irq);
618c2ecf20Sopenharmony_ci	mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	switch (irq) {
648c2ecf20Sopenharmony_ci	case MC13783_IRQ_ONOFD1:
658c2ecf20Sopenharmony_ci		val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0;
668c2ecf20Sopenharmony_ci		if (priv->flags & MC13783_PWRB_B1_POL_INVERT)
678c2ecf20Sopenharmony_ci			val ^= 1;
688c2ecf20Sopenharmony_ci		input_report_key(priv->pwr, priv->keymap[0], val);
698c2ecf20Sopenharmony_ci		break;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	case MC13783_IRQ_ONOFD2:
728c2ecf20Sopenharmony_ci		val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0;
738c2ecf20Sopenharmony_ci		if (priv->flags & MC13783_PWRB_B2_POL_INVERT)
748c2ecf20Sopenharmony_ci			val ^= 1;
758c2ecf20Sopenharmony_ci		input_report_key(priv->pwr, priv->keymap[1], val);
768c2ecf20Sopenharmony_ci		break;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	case MC13783_IRQ_ONOFD3:
798c2ecf20Sopenharmony_ci		val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0;
808c2ecf20Sopenharmony_ci		if (priv->flags & MC13783_PWRB_B3_POL_INVERT)
818c2ecf20Sopenharmony_ci			val ^= 1;
828c2ecf20Sopenharmony_ci		input_report_key(priv->pwr, priv->keymap[2], val);
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	input_sync(priv->pwr);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int mc13783_pwrbutton_probe(struct platform_device *pdev)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	const struct mc13xxx_buttons_platform_data *pdata;
948c2ecf20Sopenharmony_ci	struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
958c2ecf20Sopenharmony_ci	struct input_dev *pwr;
968c2ecf20Sopenharmony_ci	struct mc13783_pwrb *priv;
978c2ecf20Sopenharmony_ci	int err = 0;
988c2ecf20Sopenharmony_ci	int reg = 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
1018c2ecf20Sopenharmony_ci	if (!pdata) {
1028c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "missing platform data\n");
1038c2ecf20Sopenharmony_ci		return -ENODEV;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	pwr = input_allocate_device();
1078c2ecf20Sopenharmony_ci	if (!pwr) {
1088c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "Can't allocate power button\n");
1098c2ecf20Sopenharmony_ci		return -ENOMEM;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
1138c2ecf20Sopenharmony_ci	if (!priv) {
1148c2ecf20Sopenharmony_ci		err = -ENOMEM;
1158c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "Can't allocate power button\n");
1168c2ecf20Sopenharmony_ci		goto free_input_dev;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC;
1208c2ecf20Sopenharmony_ci	reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC;
1218c2ecf20Sopenharmony_ci	reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	priv->pwr = pwr;
1248c2ecf20Sopenharmony_ci	priv->mc13783 = mc13783;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	mc13xxx_lock(mc13783);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) {
1298c2ecf20Sopenharmony_ci		priv->keymap[0] = pdata->b1on_key;
1308c2ecf20Sopenharmony_ci		if (pdata->b1on_key != KEY_RESERVED)
1318c2ecf20Sopenharmony_ci			__set_bit(pdata->b1on_key, pwr->keybit);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT)
1348c2ecf20Sopenharmony_ci			priv->flags |= MC13783_PWRB_B1_POL_INVERT;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN)
1378c2ecf20Sopenharmony_ci			reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1,
1408c2ecf20Sopenharmony_ci					  button_irq, "b1on", priv);
1418c2ecf20Sopenharmony_ci		if (err) {
1428c2ecf20Sopenharmony_ci			dev_dbg(&pdev->dev, "Can't request irq\n");
1438c2ecf20Sopenharmony_ci			goto free_priv;
1448c2ecf20Sopenharmony_ci		}
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) {
1488c2ecf20Sopenharmony_ci		priv->keymap[1] = pdata->b2on_key;
1498c2ecf20Sopenharmony_ci		if (pdata->b2on_key != KEY_RESERVED)
1508c2ecf20Sopenharmony_ci			__set_bit(pdata->b2on_key, pwr->keybit);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT)
1538c2ecf20Sopenharmony_ci			priv->flags |= MC13783_PWRB_B2_POL_INVERT;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN)
1568c2ecf20Sopenharmony_ci			reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2,
1598c2ecf20Sopenharmony_ci					  button_irq, "b2on", priv);
1608c2ecf20Sopenharmony_ci		if (err) {
1618c2ecf20Sopenharmony_ci			dev_dbg(&pdev->dev, "Can't request irq\n");
1628c2ecf20Sopenharmony_ci			goto free_irq_b1;
1638c2ecf20Sopenharmony_ci		}
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) {
1678c2ecf20Sopenharmony_ci		priv->keymap[2] = pdata->b3on_key;
1688c2ecf20Sopenharmony_ci		if (pdata->b3on_key != KEY_RESERVED)
1698c2ecf20Sopenharmony_ci			__set_bit(pdata->b3on_key, pwr->keybit);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT)
1728c2ecf20Sopenharmony_ci			priv->flags |= MC13783_PWRB_B3_POL_INVERT;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN)
1758c2ecf20Sopenharmony_ci			reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3,
1788c2ecf20Sopenharmony_ci					  button_irq, "b3on", priv);
1798c2ecf20Sopenharmony_ci		if (err) {
1808c2ecf20Sopenharmony_ci			dev_dbg(&pdev->dev, "Can't request irq: %d\n", err);
1818c2ecf20Sopenharmony_ci			goto free_irq_b2;
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	mc13xxx_unlock(mc13783);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pwr->name = "mc13783_pwrbutton";
1908c2ecf20Sopenharmony_ci	pwr->phys = "mc13783_pwrbutton/input0";
1918c2ecf20Sopenharmony_ci	pwr->dev.parent = &pdev->dev;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	pwr->keycode = priv->keymap;
1948c2ecf20Sopenharmony_ci	pwr->keycodemax = ARRAY_SIZE(priv->keymap);
1958c2ecf20Sopenharmony_ci	pwr->keycodesize = sizeof(priv->keymap[0]);
1968c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, pwr->evbit);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	err = input_register_device(pwr);
1998c2ecf20Sopenharmony_ci	if (err) {
2008c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
2018c2ecf20Sopenharmony_ci		goto free_irq;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cifree_irq:
2098c2ecf20Sopenharmony_ci	mc13xxx_lock(mc13783);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
2128c2ecf20Sopenharmony_ci		mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cifree_irq_b2:
2158c2ecf20Sopenharmony_ci	if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
2168c2ecf20Sopenharmony_ci		mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cifree_irq_b1:
2198c2ecf20Sopenharmony_ci	if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
2208c2ecf20Sopenharmony_ci		mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cifree_priv:
2238c2ecf20Sopenharmony_ci	mc13xxx_unlock(mc13783);
2248c2ecf20Sopenharmony_ci	kfree(priv);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cifree_input_dev:
2278c2ecf20Sopenharmony_ci	input_free_device(pwr);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return err;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int mc13783_pwrbutton_remove(struct platform_device *pdev)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct mc13783_pwrb *priv = platform_get_drvdata(pdev);
2358c2ecf20Sopenharmony_ci	const struct mc13xxx_buttons_platform_data *pdata;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	mc13xxx_lock(priv->mc13783);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
2428c2ecf20Sopenharmony_ci		mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv);
2438c2ecf20Sopenharmony_ci	if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
2448c2ecf20Sopenharmony_ci		mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv);
2458c2ecf20Sopenharmony_ci	if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
2468c2ecf20Sopenharmony_ci		mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	mc13xxx_unlock(priv->mc13783);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	input_unregister_device(priv->pwr);
2518c2ecf20Sopenharmony_ci	kfree(priv);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic struct platform_driver mc13783_pwrbutton_driver = {
2578c2ecf20Sopenharmony_ci	.probe		= mc13783_pwrbutton_probe,
2588c2ecf20Sopenharmony_ci	.remove		= mc13783_pwrbutton_remove,
2598c2ecf20Sopenharmony_ci	.driver		= {
2608c2ecf20Sopenharmony_ci		.name	= "mc13783-pwrbutton",
2618c2ecf20Sopenharmony_ci	},
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cimodule_platform_driver(mc13783_pwrbutton_driver);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mc13783-pwrbutton");
2678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC13783 Power Button");
2688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philippe Retornaz");
270