forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
3.8 KiB
150 lines
3.8 KiB
/* |
|
* wm831x-on.c - WM831X ON pin driver |
|
* |
|
* Copyright (C) 2009 Wolfson Microelectronics plc |
|
* |
|
* This file is subject to the terms and conditions of the GNU General |
|
* Public License. See the file "COPYING" in the main directory of this |
|
* archive for more details. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
*/ |
|
|
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
#include <linux/kernel.h> |
|
#include <linux/errno.h> |
|
#include <linux/input.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/workqueue.h> |
|
#include <linux/mfd/wm831x/core.h> |
|
|
|
struct wm831x_on { |
|
struct input_dev *dev; |
|
struct delayed_work work; |
|
struct wm831x *wm831x; |
|
}; |
|
|
|
/* |
|
* The chip gives us an interrupt when the ON pin is asserted but we |
|
* then need to poll to see when the pin is deasserted. |
|
*/ |
|
static void wm831x_poll_on(struct work_struct *work) |
|
{ |
|
struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, |
|
work.work); |
|
struct wm831x *wm831x = wm831x_on->wm831x; |
|
int poll, ret; |
|
|
|
ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); |
|
if (ret >= 0) { |
|
poll = !(ret & WM831X_ON_PIN_STS); |
|
|
|
input_report_key(wm831x_on->dev, KEY_POWER, poll); |
|
input_sync(wm831x_on->dev); |
|
} else { |
|
dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); |
|
poll = 1; |
|
} |
|
|
|
if (poll) |
|
schedule_delayed_work(&wm831x_on->work, 100); |
|
} |
|
|
|
static irqreturn_t wm831x_on_irq(int irq, void *data) |
|
{ |
|
struct wm831x_on *wm831x_on = data; |
|
|
|
schedule_delayed_work(&wm831x_on->work, 0); |
|
|
|
return IRQ_HANDLED; |
|
} |
|
|
|
static int wm831x_on_probe(struct platform_device *pdev) |
|
{ |
|
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); |
|
struct wm831x_on *wm831x_on; |
|
int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); |
|
int ret; |
|
|
|
wm831x_on = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_on), |
|
GFP_KERNEL); |
|
if (!wm831x_on) { |
|
dev_err(&pdev->dev, "Can't allocate data\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
wm831x_on->wm831x = wm831x; |
|
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); |
|
|
|
wm831x_on->dev = devm_input_allocate_device(&pdev->dev); |
|
if (!wm831x_on->dev) { |
|
dev_err(&pdev->dev, "Can't allocate input dev\n"); |
|
ret = -ENOMEM; |
|
goto err; |
|
} |
|
|
|
wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); |
|
wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); |
|
wm831x_on->dev->name = "wm831x_on"; |
|
wm831x_on->dev->phys = "wm831x_on/input0"; |
|
wm831x_on->dev->dev.parent = &pdev->dev; |
|
|
|
ret = request_threaded_irq(irq, NULL, wm831x_on_irq, |
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
|
"wm831x_on", |
|
wm831x_on); |
|
if (ret < 0) { |
|
dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); |
|
goto err_input_dev; |
|
} |
|
ret = input_register_device(wm831x_on->dev); |
|
if (ret) { |
|
dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); |
|
goto err_irq; |
|
} |
|
|
|
platform_set_drvdata(pdev, wm831x_on); |
|
|
|
return 0; |
|
|
|
err_irq: |
|
free_irq(irq, wm831x_on); |
|
err_input_dev: |
|
err: |
|
return ret; |
|
} |
|
|
|
static int wm831x_on_remove(struct platform_device *pdev) |
|
{ |
|
struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); |
|
int irq = platform_get_irq(pdev, 0); |
|
|
|
free_irq(irq, wm831x_on); |
|
cancel_delayed_work_sync(&wm831x_on->work); |
|
|
|
return 0; |
|
} |
|
|
|
static struct platform_driver wm831x_on_driver = { |
|
.probe = wm831x_on_probe, |
|
.remove = wm831x_on_remove, |
|
.driver = { |
|
.name = "wm831x-on", |
|
}, |
|
}; |
|
module_platform_driver(wm831x_on_driver); |
|
|
|
MODULE_ALIAS("platform:wm831x-on"); |
|
MODULE_DESCRIPTION("WM831x ON pin"); |
|
MODULE_LICENSE("GPL"); |
|
MODULE_AUTHOR("Mark Brown <[email protected]>"); |
|
|
|
|