10c73b992SMark Brown /** 20c73b992SMark Brown * wm831x-on.c - WM831X ON pin driver 30c73b992SMark Brown * 40c73b992SMark Brown * Copyright (C) 2009 Wolfson Microelectronics plc 50c73b992SMark Brown * 60c73b992SMark Brown * This file is subject to the terms and conditions of the GNU General 70c73b992SMark Brown * Public License. See the file "COPYING" in the main directory of this 80c73b992SMark Brown * archive for more details. 90c73b992SMark Brown * 100c73b992SMark Brown * This program is distributed in the hope that it will be useful, 110c73b992SMark Brown * but WITHOUT ANY WARRANTY; without even the implied warranty of 120c73b992SMark Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 130c73b992SMark Brown * GNU General Public License for more details. 140c73b992SMark Brown * 150c73b992SMark Brown * You should have received a copy of the GNU General Public License 160c73b992SMark Brown * along with this program; if not, write to the Free Software 170c73b992SMark Brown * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 180c73b992SMark Brown */ 190c73b992SMark Brown 200c73b992SMark Brown #include <linux/module.h> 210c73b992SMark Brown #include <linux/init.h> 220c73b992SMark Brown #include <linux/kernel.h> 230c73b992SMark Brown #include <linux/errno.h> 240c73b992SMark Brown #include <linux/input.h> 250c73b992SMark Brown #include <linux/interrupt.h> 260c73b992SMark Brown #include <linux/platform_device.h> 270c73b992SMark Brown #include <linux/workqueue.h> 280c73b992SMark Brown #include <linux/mfd/wm831x/core.h> 290c73b992SMark Brown 300c73b992SMark Brown struct wm831x_on { 310c73b992SMark Brown struct input_dev *dev; 320c73b992SMark Brown struct delayed_work work; 330c73b992SMark Brown struct wm831x *wm831x; 340c73b992SMark Brown }; 350c73b992SMark Brown 360c73b992SMark Brown /* 370c73b992SMark Brown * The chip gives us an interrupt when the ON pin is asserted but we 380c73b992SMark Brown * then need to poll to see when the pin is deasserted. 390c73b992SMark Brown */ 400c73b992SMark Brown static void wm831x_poll_on(struct work_struct *work) 410c73b992SMark Brown { 420c73b992SMark Brown struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, 430c73b992SMark Brown work.work); 440c73b992SMark Brown struct wm831x *wm831x = wm831x_on->wm831x; 450c73b992SMark Brown int poll, ret; 460c73b992SMark Brown 470c73b992SMark Brown ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); 480c73b992SMark Brown if (ret >= 0) { 490c73b992SMark Brown poll = !(ret & WM831X_ON_PIN_STS); 500c73b992SMark Brown 510c73b992SMark Brown input_report_key(wm831x_on->dev, KEY_POWER, poll); 520c73b992SMark Brown input_sync(wm831x_on->dev); 530c73b992SMark Brown } else { 540c73b992SMark Brown dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); 550c73b992SMark Brown poll = 1; 560c73b992SMark Brown } 570c73b992SMark Brown 580c73b992SMark Brown if (poll) 590c73b992SMark Brown schedule_delayed_work(&wm831x_on->work, 100); 600c73b992SMark Brown } 610c73b992SMark Brown 620c73b992SMark Brown static irqreturn_t wm831x_on_irq(int irq, void *data) 630c73b992SMark Brown { 640c73b992SMark Brown struct wm831x_on *wm831x_on = data; 650c73b992SMark Brown 660c73b992SMark Brown schedule_delayed_work(&wm831x_on->work, 0); 670c73b992SMark Brown 680c73b992SMark Brown return IRQ_HANDLED; 690c73b992SMark Brown } 700c73b992SMark Brown 710c73b992SMark Brown static int __devinit wm831x_on_probe(struct platform_device *pdev) 720c73b992SMark Brown { 730c73b992SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 740c73b992SMark Brown struct wm831x_on *wm831x_on; 750c73b992SMark Brown int irq = platform_get_irq(pdev, 0); 760c73b992SMark Brown int ret; 770c73b992SMark Brown 780c73b992SMark Brown wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); 790c73b992SMark Brown if (!wm831x_on) { 800c73b992SMark Brown dev_err(&pdev->dev, "Can't allocate data\n"); 810c73b992SMark Brown return -ENOMEM; 820c73b992SMark Brown } 830c73b992SMark Brown 840c73b992SMark Brown wm831x_on->wm831x = wm831x; 850c73b992SMark Brown INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); 860c73b992SMark Brown 870c73b992SMark Brown wm831x_on->dev = input_allocate_device(); 880c73b992SMark Brown if (!wm831x_on->dev) { 890c73b992SMark Brown dev_err(&pdev->dev, "Can't allocate input dev\n"); 900c73b992SMark Brown ret = -ENOMEM; 910c73b992SMark Brown goto err; 920c73b992SMark Brown } 930c73b992SMark Brown 940c73b992SMark Brown wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); 950c73b992SMark Brown wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); 960c73b992SMark Brown wm831x_on->dev->name = "wm831x_on"; 970c73b992SMark Brown wm831x_on->dev->phys = "wm831x_on/input0"; 980c73b992SMark Brown wm831x_on->dev->dev.parent = &pdev->dev; 990c73b992SMark Brown 100afadb8e0SMark Brown ret = request_threaded_irq(irq, NULL, wm831x_on_irq, 101afadb8e0SMark Brown IRQF_TRIGGER_RISING, "wm831x_on", 102afadb8e0SMark Brown wm831x_on); 1030c73b992SMark Brown if (ret < 0) { 1040c73b992SMark Brown dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); 1050c73b992SMark Brown goto err_input_dev; 1060c73b992SMark Brown } 1070c73b992SMark Brown ret = input_register_device(wm831x_on->dev); 1080c73b992SMark Brown if (ret) { 1090c73b992SMark Brown dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); 1100c73b992SMark Brown goto err_irq; 1110c73b992SMark Brown } 1120c73b992SMark Brown 1130c73b992SMark Brown platform_set_drvdata(pdev, wm831x_on); 1140c73b992SMark Brown 1150c73b992SMark Brown return 0; 1160c73b992SMark Brown 1170c73b992SMark Brown err_irq: 118afadb8e0SMark Brown free_irq(irq, wm831x_on); 1190c73b992SMark Brown err_input_dev: 1200c73b992SMark Brown input_free_device(wm831x_on->dev); 1210c73b992SMark Brown err: 1220c73b992SMark Brown kfree(wm831x_on); 1230c73b992SMark Brown return ret; 1240c73b992SMark Brown } 1250c73b992SMark Brown 1260c73b992SMark Brown static int __devexit wm831x_on_remove(struct platform_device *pdev) 1270c73b992SMark Brown { 1280c73b992SMark Brown struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); 1290c73b992SMark Brown int irq = platform_get_irq(pdev, 0); 1300c73b992SMark Brown 131afadb8e0SMark Brown free_irq(irq, wm831x_on); 1320c73b992SMark Brown cancel_delayed_work_sync(&wm831x_on->work); 1330c73b992SMark Brown input_unregister_device(wm831x_on->dev); 1340c73b992SMark Brown kfree(wm831x_on); 1350c73b992SMark Brown 1360c73b992SMark Brown return 0; 1370c73b992SMark Brown } 1380c73b992SMark Brown 1390c73b992SMark Brown static struct platform_driver wm831x_on_driver = { 1400c73b992SMark Brown .probe = wm831x_on_probe, 1410c73b992SMark Brown .remove = __devexit_p(wm831x_on_remove), 1420c73b992SMark Brown .driver = { 1430c73b992SMark Brown .name = "wm831x-on", 1440c73b992SMark Brown .owner = THIS_MODULE, 1450c73b992SMark Brown }, 1460c73b992SMark Brown }; 1470c73b992SMark Brown 1480c73b992SMark Brown static int __init wm831x_on_init(void) 1490c73b992SMark Brown { 1500c73b992SMark Brown return platform_driver_register(&wm831x_on_driver); 1510c73b992SMark Brown } 1520c73b992SMark Brown module_init(wm831x_on_init); 1530c73b992SMark Brown 1540c73b992SMark Brown static void __exit wm831x_on_exit(void) 1550c73b992SMark Brown { 1560c73b992SMark Brown platform_driver_unregister(&wm831x_on_driver); 1570c73b992SMark Brown } 1580c73b992SMark Brown module_exit(wm831x_on_exit); 1590c73b992SMark Brown 1600c73b992SMark Brown MODULE_ALIAS("platform:wm831x-on"); 1610c73b992SMark Brown MODULE_DESCRIPTION("WM831x ON pin"); 1620c73b992SMark Brown MODULE_LICENSE("GPL"); 1630c73b992SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 1640c73b992SMark Brown 165