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> 225a0e3ad6STejun Heo #include <linux/slab.h> 230c73b992SMark Brown #include <linux/kernel.h> 240c73b992SMark Brown #include <linux/errno.h> 250c73b992SMark Brown #include <linux/input.h> 260c73b992SMark Brown #include <linux/interrupt.h> 270c73b992SMark Brown #include <linux/platform_device.h> 280c73b992SMark Brown #include <linux/workqueue.h> 290c73b992SMark Brown #include <linux/mfd/wm831x/core.h> 300c73b992SMark Brown 310c73b992SMark Brown struct wm831x_on { 320c73b992SMark Brown struct input_dev *dev; 330c73b992SMark Brown struct delayed_work work; 340c73b992SMark Brown struct wm831x *wm831x; 350c73b992SMark Brown }; 360c73b992SMark Brown 370c73b992SMark Brown /* 380c73b992SMark Brown * The chip gives us an interrupt when the ON pin is asserted but we 390c73b992SMark Brown * then need to poll to see when the pin is deasserted. 400c73b992SMark Brown */ 410c73b992SMark Brown static void wm831x_poll_on(struct work_struct *work) 420c73b992SMark Brown { 430c73b992SMark Brown struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, 440c73b992SMark Brown work.work); 450c73b992SMark Brown struct wm831x *wm831x = wm831x_on->wm831x; 460c73b992SMark Brown int poll, ret; 470c73b992SMark Brown 480c73b992SMark Brown ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); 490c73b992SMark Brown if (ret >= 0) { 500c73b992SMark Brown poll = !(ret & WM831X_ON_PIN_STS); 510c73b992SMark Brown 520c73b992SMark Brown input_report_key(wm831x_on->dev, KEY_POWER, poll); 530c73b992SMark Brown input_sync(wm831x_on->dev); 540c73b992SMark Brown } else { 550c73b992SMark Brown dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); 560c73b992SMark Brown poll = 1; 570c73b992SMark Brown } 580c73b992SMark Brown 590c73b992SMark Brown if (poll) 600c73b992SMark Brown schedule_delayed_work(&wm831x_on->work, 100); 610c73b992SMark Brown } 620c73b992SMark Brown 630c73b992SMark Brown static irqreturn_t wm831x_on_irq(int irq, void *data) 640c73b992SMark Brown { 650c73b992SMark Brown struct wm831x_on *wm831x_on = data; 660c73b992SMark Brown 670c73b992SMark Brown schedule_delayed_work(&wm831x_on->work, 0); 680c73b992SMark Brown 690c73b992SMark Brown return IRQ_HANDLED; 700c73b992SMark Brown } 710c73b992SMark Brown 720c73b992SMark Brown static int __devinit wm831x_on_probe(struct platform_device *pdev) 730c73b992SMark Brown { 740c73b992SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 750c73b992SMark Brown struct wm831x_on *wm831x_on; 760c73b992SMark Brown int irq = platform_get_irq(pdev, 0); 770c73b992SMark Brown int ret; 780c73b992SMark Brown 790c73b992SMark Brown wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); 800c73b992SMark Brown if (!wm831x_on) { 810c73b992SMark Brown dev_err(&pdev->dev, "Can't allocate data\n"); 820c73b992SMark Brown return -ENOMEM; 830c73b992SMark Brown } 840c73b992SMark Brown 850c73b992SMark Brown wm831x_on->wm831x = wm831x; 860c73b992SMark Brown INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); 870c73b992SMark Brown 880c73b992SMark Brown wm831x_on->dev = input_allocate_device(); 890c73b992SMark Brown if (!wm831x_on->dev) { 900c73b992SMark Brown dev_err(&pdev->dev, "Can't allocate input dev\n"); 910c73b992SMark Brown ret = -ENOMEM; 920c73b992SMark Brown goto err; 930c73b992SMark Brown } 940c73b992SMark Brown 950c73b992SMark Brown wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); 960c73b992SMark Brown wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); 970c73b992SMark Brown wm831x_on->dev->name = "wm831x_on"; 980c73b992SMark Brown wm831x_on->dev->phys = "wm831x_on/input0"; 990c73b992SMark Brown wm831x_on->dev->dev.parent = &pdev->dev; 1000c73b992SMark Brown 101afadb8e0SMark Brown ret = request_threaded_irq(irq, NULL, wm831x_on_irq, 102afadb8e0SMark Brown IRQF_TRIGGER_RISING, "wm831x_on", 103afadb8e0SMark Brown wm831x_on); 1040c73b992SMark Brown if (ret < 0) { 1050c73b992SMark Brown dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); 1060c73b992SMark Brown goto err_input_dev; 1070c73b992SMark Brown } 1080c73b992SMark Brown ret = input_register_device(wm831x_on->dev); 1090c73b992SMark Brown if (ret) { 1100c73b992SMark Brown dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); 1110c73b992SMark Brown goto err_irq; 1120c73b992SMark Brown } 1130c73b992SMark Brown 1140c73b992SMark Brown platform_set_drvdata(pdev, wm831x_on); 1150c73b992SMark Brown 1160c73b992SMark Brown return 0; 1170c73b992SMark Brown 1180c73b992SMark Brown err_irq: 119afadb8e0SMark Brown free_irq(irq, wm831x_on); 1200c73b992SMark Brown err_input_dev: 1210c73b992SMark Brown input_free_device(wm831x_on->dev); 1220c73b992SMark Brown err: 1230c73b992SMark Brown kfree(wm831x_on); 1240c73b992SMark Brown return ret; 1250c73b992SMark Brown } 1260c73b992SMark Brown 1270c73b992SMark Brown static int __devexit wm831x_on_remove(struct platform_device *pdev) 1280c73b992SMark Brown { 1290c73b992SMark Brown struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); 1300c73b992SMark Brown int irq = platform_get_irq(pdev, 0); 1310c73b992SMark Brown 132afadb8e0SMark Brown free_irq(irq, wm831x_on); 1330c73b992SMark Brown cancel_delayed_work_sync(&wm831x_on->work); 1340c73b992SMark Brown input_unregister_device(wm831x_on->dev); 1350c73b992SMark Brown kfree(wm831x_on); 1360c73b992SMark Brown 1370c73b992SMark Brown return 0; 1380c73b992SMark Brown } 1390c73b992SMark Brown 1400c73b992SMark Brown static struct platform_driver wm831x_on_driver = { 1410c73b992SMark Brown .probe = wm831x_on_probe, 1420c73b992SMark Brown .remove = __devexit_p(wm831x_on_remove), 1430c73b992SMark Brown .driver = { 1440c73b992SMark Brown .name = "wm831x-on", 1450c73b992SMark Brown .owner = THIS_MODULE, 1460c73b992SMark Brown }, 1470c73b992SMark Brown }; 1480c73b992SMark Brown 1490c73b992SMark Brown static int __init wm831x_on_init(void) 1500c73b992SMark Brown { 1510c73b992SMark Brown return platform_driver_register(&wm831x_on_driver); 1520c73b992SMark Brown } 1530c73b992SMark Brown module_init(wm831x_on_init); 1540c73b992SMark Brown 1550c73b992SMark Brown static void __exit wm831x_on_exit(void) 1560c73b992SMark Brown { 1570c73b992SMark Brown platform_driver_unregister(&wm831x_on_driver); 1580c73b992SMark Brown } 1590c73b992SMark Brown module_exit(wm831x_on_exit); 1600c73b992SMark Brown 1610c73b992SMark Brown MODULE_ALIAS("platform:wm831x-on"); 1620c73b992SMark Brown MODULE_DESCRIPTION("WM831x ON pin"); 1630c73b992SMark Brown MODULE_LICENSE("GPL"); 1640c73b992SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 1650c73b992SMark Brown 166