12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28ed2757eSAshish Jangam /*
38ed2757eSAshish Jangam  * ON pin driver for Dialog DA9055 PMICs
48ed2757eSAshish Jangam  *
58ed2757eSAshish Jangam  * Copyright(c) 2012 Dialog Semiconductor Ltd.
68ed2757eSAshish Jangam  *
78ed2757eSAshish Jangam  * Author: David Dajun Chen <dchen@diasemi.com>
88ed2757eSAshish Jangam  */
98ed2757eSAshish Jangam 
108ed2757eSAshish Jangam #include <linux/input.h>
118ed2757eSAshish Jangam #include <linux/module.h>
128ed2757eSAshish Jangam #include <linux/platform_device.h>
138ed2757eSAshish Jangam 
148ed2757eSAshish Jangam #include <linux/mfd/da9055/core.h>
158ed2757eSAshish Jangam #include <linux/mfd/da9055/reg.h>
168ed2757eSAshish Jangam 
178ed2757eSAshish Jangam struct da9055_onkey {
188ed2757eSAshish Jangam 	struct da9055 *da9055;
198ed2757eSAshish Jangam 	struct input_dev *input;
208ed2757eSAshish Jangam 	struct delayed_work work;
218ed2757eSAshish Jangam };
228ed2757eSAshish Jangam 
da9055_onkey_query(struct da9055_onkey * onkey)238ed2757eSAshish Jangam static void da9055_onkey_query(struct da9055_onkey *onkey)
248ed2757eSAshish Jangam {
258ed2757eSAshish Jangam 	int key_stat;
268ed2757eSAshish Jangam 
278ed2757eSAshish Jangam 	key_stat = da9055_reg_read(onkey->da9055, DA9055_REG_STATUS_A);
288ed2757eSAshish Jangam 	if (key_stat < 0) {
298ed2757eSAshish Jangam 		dev_err(onkey->da9055->dev,
308ed2757eSAshish Jangam 			"Failed to read onkey event %d\n", key_stat);
318ed2757eSAshish Jangam 	} else {
328ed2757eSAshish Jangam 		key_stat &= DA9055_NOKEY_STS;
338ed2757eSAshish Jangam 		/*
3483a35e36SGeert Uytterhoeven 		 * Onkey status bit is cleared when onkey button is released.
358ed2757eSAshish Jangam 		 */
368ed2757eSAshish Jangam 		if (!key_stat) {
378ed2757eSAshish Jangam 			input_report_key(onkey->input, KEY_POWER, 0);
388ed2757eSAshish Jangam 			input_sync(onkey->input);
398ed2757eSAshish Jangam 		}
408ed2757eSAshish Jangam 	}
418ed2757eSAshish Jangam 
428ed2757eSAshish Jangam 	/*
438ed2757eSAshish Jangam 	 * Interrupt is generated only when the ONKEY pin is asserted.
448ed2757eSAshish Jangam 	 * Hence the deassertion of the pin is simulated through work queue.
458ed2757eSAshish Jangam 	 */
468ed2757eSAshish Jangam 	if (key_stat)
478ed2757eSAshish Jangam 		schedule_delayed_work(&onkey->work, msecs_to_jiffies(10));
488ed2757eSAshish Jangam 
498ed2757eSAshish Jangam }
508ed2757eSAshish Jangam 
da9055_onkey_work(struct work_struct * work)518ed2757eSAshish Jangam static void da9055_onkey_work(struct work_struct *work)
528ed2757eSAshish Jangam {
538ed2757eSAshish Jangam 	struct da9055_onkey *onkey = container_of(work, struct da9055_onkey,
548ed2757eSAshish Jangam 						  work.work);
558ed2757eSAshish Jangam 
568ed2757eSAshish Jangam 	da9055_onkey_query(onkey);
578ed2757eSAshish Jangam }
588ed2757eSAshish Jangam 
da9055_onkey_irq(int irq,void * data)598ed2757eSAshish Jangam static irqreturn_t da9055_onkey_irq(int irq, void *data)
608ed2757eSAshish Jangam {
618ed2757eSAshish Jangam 	struct da9055_onkey *onkey = data;
628ed2757eSAshish Jangam 
638ed2757eSAshish Jangam 	input_report_key(onkey->input, KEY_POWER, 1);
648ed2757eSAshish Jangam 	input_sync(onkey->input);
658ed2757eSAshish Jangam 
668ed2757eSAshish Jangam 	da9055_onkey_query(onkey);
678ed2757eSAshish Jangam 
688ed2757eSAshish Jangam 	return IRQ_HANDLED;
698ed2757eSAshish Jangam }
708ed2757eSAshish Jangam 
da9055_onkey_probe(struct platform_device * pdev)715298cc4cSBill Pemberton static int da9055_onkey_probe(struct platform_device *pdev)
728ed2757eSAshish Jangam {
738ed2757eSAshish Jangam 	struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent);
748ed2757eSAshish Jangam 	struct da9055_onkey *onkey;
758ed2757eSAshish Jangam 	struct input_dev *input_dev;
768ed2757eSAshish Jangam 	int irq, err;
778ed2757eSAshish Jangam 
788ed2757eSAshish Jangam 	irq = platform_get_irq_byname(pdev, "ONKEY");
790bec8b7eSStephen Boyd 	if (irq < 0)
808ed2757eSAshish Jangam 		return -EINVAL;
818ed2757eSAshish Jangam 
828ed2757eSAshish Jangam 	onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
838ed2757eSAshish Jangam 	if (!onkey) {
848ed2757eSAshish Jangam 		dev_err(&pdev->dev, "Failed to allocate memory\n");
858ed2757eSAshish Jangam 		return -ENOMEM;
868ed2757eSAshish Jangam 	}
878ed2757eSAshish Jangam 
888ed2757eSAshish Jangam 	input_dev = input_allocate_device();
898ed2757eSAshish Jangam 	if (!input_dev) {
908ed2757eSAshish Jangam 		dev_err(&pdev->dev, "Failed to allocate memory\n");
918ed2757eSAshish Jangam 		return -ENOMEM;
928ed2757eSAshish Jangam 	}
938ed2757eSAshish Jangam 
948ed2757eSAshish Jangam 	onkey->input = input_dev;
958ed2757eSAshish Jangam 	onkey->da9055 = da9055;
968ed2757eSAshish Jangam 	input_dev->name = "da9055-onkey";
978ed2757eSAshish Jangam 	input_dev->phys = "da9055-onkey/input0";
988ed2757eSAshish Jangam 	input_dev->dev.parent = &pdev->dev;
998ed2757eSAshish Jangam 
1008ed2757eSAshish Jangam 	input_dev->evbit[0] = BIT_MASK(EV_KEY);
1018ed2757eSAshish Jangam 	__set_bit(KEY_POWER, input_dev->keybit);
1028ed2757eSAshish Jangam 
1038ed2757eSAshish Jangam 	INIT_DELAYED_WORK(&onkey->work, da9055_onkey_work);
1048ed2757eSAshish Jangam 
1058ed2757eSAshish Jangam 	err = request_threaded_irq(irq, NULL, da9055_onkey_irq,
1068ed2757eSAshish Jangam 				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
1078ed2757eSAshish Jangam 				   "ONKEY", onkey);
1088ed2757eSAshish Jangam 	if (err < 0) {
1098ed2757eSAshish Jangam 		dev_err(&pdev->dev,
1108ed2757eSAshish Jangam 			"Failed to register ONKEY IRQ %d, error = %d\n",
1118ed2757eSAshish Jangam 			irq, err);
1128ed2757eSAshish Jangam 		goto err_free_input;
1138ed2757eSAshish Jangam 	}
1148ed2757eSAshish Jangam 
1158ed2757eSAshish Jangam 	err = input_register_device(input_dev);
1168ed2757eSAshish Jangam 	if (err) {
1178ed2757eSAshish Jangam 		dev_err(&pdev->dev, "Unable to register input device, %d\n",
1188ed2757eSAshish Jangam 			err);
1198ed2757eSAshish Jangam 		goto err_free_irq;
1208ed2757eSAshish Jangam 	}
1218ed2757eSAshish Jangam 
1228ed2757eSAshish Jangam 	platform_set_drvdata(pdev, onkey);
1238ed2757eSAshish Jangam 
1248ed2757eSAshish Jangam 	return 0;
1258ed2757eSAshish Jangam 
1268ed2757eSAshish Jangam err_free_irq:
1278ed2757eSAshish Jangam 	free_irq(irq, onkey);
1288ed2757eSAshish Jangam 	cancel_delayed_work_sync(&onkey->work);
1298ed2757eSAshish Jangam err_free_input:
1308ed2757eSAshish Jangam 	input_free_device(input_dev);
1318ed2757eSAshish Jangam 
1328ed2757eSAshish Jangam 	return err;
1338ed2757eSAshish Jangam }
1348ed2757eSAshish Jangam 
da9055_onkey_remove(struct platform_device * pdev)135e2619cf7SBill Pemberton static int da9055_onkey_remove(struct platform_device *pdev)
1368ed2757eSAshish Jangam {
1378ed2757eSAshish Jangam 	struct da9055_onkey *onkey = platform_get_drvdata(pdev);
1388ed2757eSAshish Jangam 	int irq = platform_get_irq_byname(pdev, "ONKEY");
1398ed2757eSAshish Jangam 
1408ed2757eSAshish Jangam 	irq = regmap_irq_get_virq(onkey->da9055->irq_data, irq);
1418ed2757eSAshish Jangam 	free_irq(irq, onkey);
1428ed2757eSAshish Jangam 	cancel_delayed_work_sync(&onkey->work);
1438ed2757eSAshish Jangam 	input_unregister_device(onkey->input);
1448ed2757eSAshish Jangam 
1458ed2757eSAshish Jangam 	return 0;
1468ed2757eSAshish Jangam }
1478ed2757eSAshish Jangam 
1488ed2757eSAshish Jangam static struct platform_driver da9055_onkey_driver = {
1498ed2757eSAshish Jangam 	.probe	= da9055_onkey_probe,
1501cb0aa88SBill Pemberton 	.remove	= da9055_onkey_remove,
1518ed2757eSAshish Jangam 	.driver = {
1528ed2757eSAshish Jangam 		.name	= "da9055-onkey",
1538ed2757eSAshish Jangam 	},
1548ed2757eSAshish Jangam };
1558ed2757eSAshish Jangam 
1568ed2757eSAshish Jangam module_platform_driver(da9055_onkey_driver);
1578ed2757eSAshish Jangam 
1588ed2757eSAshish Jangam MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
1598ed2757eSAshish Jangam MODULE_DESCRIPTION("Onkey driver for DA9055");
1608ed2757eSAshish Jangam MODULE_LICENSE("GPL");
1618ed2757eSAshish Jangam MODULE_ALIAS("platform:da9055-onkey");
162