summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Chen <xinyu.chen@freescale.com>2008-03-15 09:08:32 +0800
committerDaniel Schaeffer <daniel.schaeffer@timesys.com>2008-08-25 15:20:46 -0400
commit32a265530f5da6f8f2e7c8044383b62eaa42c988 (patch)
tree3d08808aa9f773cb76e22b821b01c3118ba339d2
parent4a2a86506008286afdbd3c94d05cfac69c9d0a24 (diff)
ENGR00064694 3DS hwevent support
Support new HW event module for the notifications about phone jack, charger etc. Signed-off-by: Eric Chen <xinyu.chen@freescale.com>
-rw-r--r--arch/arm/configs/imx31_3stack_defconfig5
-rw-r--r--drivers/mxc/Kconfig1
-rw-r--r--drivers/mxc/Makefile1
-rw-r--r--drivers/mxc/hw_event/Kconfig11
-rw-r--r--drivers/mxc/hw_event/Makefile1
-rw-r--r--drivers/mxc/hw_event/mxc_hw_event.c265
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_power.c8
-rw-r--r--include/asm-arm/arch-mxc/hw_events.h65
8 files changed, 353 insertions, 4 deletions
diff --git a/arch/arm/configs/imx31_3stack_defconfig b/arch/arm/configs/imx31_3stack_defconfig
index 4889e2c7a2fc..b244e66f847b 100644
--- a/arch/arm/configs/imx31_3stack_defconfig
+++ b/arch/arm/configs/imx31_3stack_defconfig
@@ -1353,6 +1353,11 @@ CONFIG_MXC_HMP4E=y
# CONFIG_MXC_HMP4E_DEBUG is not set
#
+# MXC HARDWARE EVENT
+#
+CONFIG_MXC_HWEVENT=y
+
+#
# MXC VPU(Video Processing Unit) support
#
# CONFIG_MXC_VPU is not set
diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig
index f913e03af789..f27a9528f418 100644
--- a/drivers/mxc/Kconfig
+++ b/drivers/mxc/Kconfig
@@ -11,6 +11,7 @@ source "drivers/mxc/pmic/Kconfig"
source "drivers/mxc/pm/Kconfig"
source "drivers/mxc/security/Kconfig"
source "drivers/mxc/hmp4e/Kconfig"
+source "drivers/mxc/hw_event/Kconfig"
source "drivers/mxc/vpu/Kconfig"
endmenu
diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile
index 4a7029df5200..63eae156a43b 100644
--- a/drivers/mxc/Makefile
+++ b/drivers/mxc/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_MX27_DPTC) += pm/
obj-$(CONFIG_MXC_HMP4E) += hmp4e/
obj-y += security/
obj-$(CONFIG_MXC_VPU) += vpu/
+obj-$(CONFIG_MXC_HWEVENT) += hw_event/
diff --git a/drivers/mxc/hw_event/Kconfig b/drivers/mxc/hw_event/Kconfig
new file mode 100644
index 000000000000..bcf479689501
--- /dev/null
+++ b/drivers/mxc/hw_event/Kconfig
@@ -0,0 +1,11 @@
+menu "MXC HARDWARE EVENT"
+
+config MXC_HWEVENT
+ bool "MXC Hardware Event Handler"
+ default y
+ depends on ARCH_MXC
+ help
+ If you plan to use the Hardware Event Handler in the MXC, say
+ Y here. If unsure, select Y.
+
+endmenu
diff --git a/drivers/mxc/hw_event/Makefile b/drivers/mxc/hw_event/Makefile
new file mode 100644
index 000000000000..a53fe2b45e04
--- /dev/null
+++ b/drivers/mxc/hw_event/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MXC_HWEVENT) += mxc_hw_event.o
diff --git a/drivers/mxc/hw_event/mxc_hw_event.c b/drivers/mxc/hw_event/mxc_hw_event.c
new file mode 100644
index 000000000000..b30bb4934f4e
--- /dev/null
+++ b/drivers/mxc/hw_event/mxc_hw_event.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * mxc_hw_event.c
+ * Collect the hardware events, send to user by netlink
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/signal.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+
+#include <asm/arch/hw_events.h>
+
+#define EVENT_POOL_SIZE 10
+
+struct hw_event_elem {
+ struct mxc_hw_event event;
+ struct list_head list;
+};
+
+static struct sock *nl_event_sock; /* netlink socket */
+static struct list_head event_head;
+static struct list_head free_head;
+static struct hw_event_elem events_pool[EVENT_POOL_SIZE]; /* event pool */
+static DEFINE_SPINLOCK(list_lock);
+static DECLARE_WAIT_QUEUE_HEAD(event_wq);
+static unsigned int seq; /* send seq */
+static int initialized;
+static struct task_struct *hwevent_kthread;
+
+/*!
+ * main HW event handler thread
+ */
+static int hw_event_thread(void *data)
+{
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ unsigned int size;
+ struct hw_event_elem *event, *n;
+ LIST_HEAD(tmp_head);
+ DEFINE_WAIT(wait);
+
+ while (1) {
+
+ prepare_to_wait(&event_wq, &wait, TASK_INTERRUPTIBLE);
+ /* wait for event coming */
+ if (!freezing(current) && !kthread_should_stop() &&
+ list_empty(&event_head))
+ schedule();
+ finish_wait(&event_wq, &wait);
+
+ try_to_freeze();
+
+ if (kthread_should_stop())
+ break;
+
+ /* fetch event from list */
+ spin_lock_irq(&list_lock);
+ tmp_head = event_head;
+ tmp_head.prev->next = &tmp_head;
+ tmp_head.next->prev = &tmp_head;
+ /* clear the event list head */
+ INIT_LIST_HEAD(&event_head);
+ spin_unlock_irq(&list_lock);
+
+ list_for_each_entry_safe(event, n, &tmp_head, list) {
+
+ size = NLMSG_SPACE(sizeof(struct mxc_hw_event));
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ /* if failed alloc skb, we drop this event */
+ printk(KERN_WARNING
+ "mxc_hw_event: skb_alloc() failed\n");
+ goto alloc_failure;
+ }
+
+ /* put the netlink header struct to skb */
+ nlh =
+ NLMSG_PUT(skb, 0, seq++, NLMSG_DONE,
+ size - sizeof(*nlh));
+
+ /* fill the netlink data */
+ memcpy((struct mxc_hw_event *)NLMSG_DATA(nlh),
+ &event->event, sizeof(struct mxc_hw_event));
+
+ /* free the event node, set to unused */
+ spin_lock_irq(&list_lock);
+ list_move(&event->list, &free_head);
+ spin_unlock_irq(&list_lock);
+
+ /* send to all process that create this socket */
+ NETLINK_CB(skb).pid = 0; /* sender pid */
+ NETLINK_CB(skb).dst_group = HW_EVENT_GROUP;
+ /* broadcast the event */
+ netlink_broadcast(nl_event_sock, skb, 0, HW_EVENT_GROUP,
+ GFP_KERNEL);
+
+ continue;
+ nlmsg_failure:
+ printk(KERN_WARNING
+ "mxc_hw_event: No tailroom for NLMSG in skb\n");
+ alloc_failure:
+ /* free the event node, set to unused */
+ spin_lock_irq(&list_lock);
+ list_del(&event->list);
+ list_add_tail(&event->list, &free_head);
+ spin_unlock_irq(&list_lock);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ *
+ * @priority the event priority, REALTIME, EMERENCY, NORMAL
+ * @new_event event id to be send
+ */
+int hw_event_send(int priority, struct mxc_hw_event *new_event)
+{
+ unsigned int size;
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ struct mxc_hw_event *event;
+ struct hw_event_elem *event_elem;
+ int ret;
+ unsigned long flag;
+ struct list_head *list_node;
+
+ if (!initialized) {
+ pr_info("HW Event module has not been initialized\n");
+ return -1;
+ }
+
+ if (priority == HWE_HIGH_PRIORITY) {
+ /**
+ * the most high priority event,
+ * we send it immediatly.
+ */
+
+ size = NLMSG_SPACE(sizeof(struct mxc_hw_event));
+
+ /* alloc skb */
+ if (in_interrupt()) {
+ skb = alloc_skb(size, GFP_ATOMIC);
+ } else {
+ skb = alloc_skb(size, GFP_KERNEL);
+ }
+ if (!skb) {
+ /* if failed alloc skb, we drop this event */
+ printk(KERN_WARNING
+ "hw_event send: skb_alloc() failed\n");
+ goto send_later;
+ }
+
+ /* put the netlink header struct to skb */
+ nlh = NLMSG_PUT(skb, 0, seq++, NLMSG_DONE, size - sizeof(*nlh));
+
+ /* fill the netlink data */
+ event = (struct mxc_hw_event *)NLMSG_DATA(nlh);
+ memcpy(event, new_event, sizeof(struct mxc_hw_event));
+
+ /* send to all process that create this socket */
+ NETLINK_CB(skb).pid = 0; /* sender pid */
+ NETLINK_CB(skb).dst_group = HW_EVENT_GROUP;
+ /* broadcast the event */
+ ret = netlink_broadcast(nl_event_sock, skb, 0, HW_EVENT_GROUP,
+ in_interrupt()? GFP_ATOMIC :
+ GFP_KERNEL);
+ if (ret) {
+
+ nlmsg_failure:
+ /* send failed */
+ kfree_skb(skb);
+ goto send_later;
+ }
+
+ return 0;
+ }
+
+ send_later:
+ spin_lock_irqsave(&list_lock, flag);
+ if (list_empty(&free_head)) {
+ spin_unlock_irqrestore(&list_lock, flag);
+ /* no more free event node */
+ printk(KERN_WARNING "mxc_event send: no more free node\n");
+ return -1;
+ }
+
+ /* get a free node from free list, and added to event list */
+ list_node = free_head.next;
+ /* fill event */
+ event_elem = list_entry(list_node, struct hw_event_elem, list);
+ event_elem->event = *new_event;
+ list_move(list_node, &event_head);
+ spin_unlock_irqrestore(&list_lock, flag);
+
+ wake_up(&event_wq);
+
+ return 0;
+}
+
+static int __init mxc_hw_event_init(void)
+{
+ int i;
+
+ /* initial the list head for event and free */
+ INIT_LIST_HEAD(&free_head);
+ INIT_LIST_HEAD(&event_head);
+
+ /* initial the free list */
+ for (i = 0; i < EVENT_POOL_SIZE; i++)
+ list_add_tail(&events_pool[i].list, &free_head);
+
+ /* create netlink kernel sock */
+ nl_event_sock =
+ netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0, NULL, NULL,
+ THIS_MODULE);
+ if (!nl_event_sock) {
+ printk(KERN_WARNING
+ "mxc_hw_event: Fail to create netlink socket.\n");
+ return 1;
+ }
+
+ hwevent_kthread = kthread_run(hw_event_thread, NULL, "hwevent");
+ if (IS_ERR(hwevent_kthread)) {
+ printk(KERN_WARNING
+ "mxc_hw_event: Fail to create hwevent thread.\n");
+ return 1;
+ }
+
+ initialized = 1;
+
+ return 0;
+}
+
+static void __exit mxc_hw_event_exit(void)
+{
+ kthread_stop(hwevent_kthread);
+ /* wait for thread completion */
+ sock_release(nl_event_sock->sk_socket);
+}
+
+module_init(mxc_hw_event_init);
+module_exit(mxc_hw_event_exit);
+
+EXPORT_SYMBOL(hw_event_send);
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_power.c b/drivers/mxc/pmic/mc13783/pmic_power.c
index 40630332a3ab..33e77b412066 100644
--- a/drivers/mxc/pmic/mc13783/pmic_power.c
+++ b/drivers/mxc/pmic/mc13783/pmic_power.c
@@ -21,7 +21,7 @@
/*
* Includes
*/
-#define DEBUG
+
#include <linux/platform_device.h>
#include <asm/ioctl.h>
#include <asm/arch/pmic_power.h>
@@ -30,7 +30,7 @@
#include "pmic_power_defs.h"
#ifdef CONFIG_MXC_HWEVENT
-#include <../drivers/mxc/hw_event/mxc_hw_event.h>
+#include <asm/arch/hw_events.h>
#endif
#include <asm/mach-types.h>
@@ -3046,7 +3046,7 @@ PMIC_STATUS pmic_power_event_unsub(t_pwr_int event, void *callback)
void pmic_power_key_callback(void)
{
-#ifdef MXC_HWEVENT
+#ifdef CONFIG_MXC_HWEVENT
/*read the power key is pressed or up */
t_sensor_bits sense;
struct mxc_hw_event event = { HWE_POWER_KEY, 0 };
@@ -3060,7 +3060,7 @@ void pmic_power_key_callback(void)
event.args = PWRK_PRESS;
}
/* send hw event */
- hw_event_send(HWE_DEF_PRIORITY, event);
+ hw_event_send(HWE_DEF_PRIORITY, &event);
#endif
}
diff --git a/include/asm-arm/arch-mxc/hw_events.h b/include/asm-arm/arch-mxc/hw_events.h
new file mode 100644
index 000000000000..f0aa3ad7a266
--- /dev/null
+++ b/include/asm-arm/arch-mxc/hw_events.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * hw_events.h
+ * include the headset/cvbs interrupt detect
+ */
+
+#ifndef HW_EVENT_H
+#define HW_EVENT_H
+
+#define HW_EVENT_GROUP 2
+#define HWE_DEF_PRIORITY 1
+#define HWE_HIGH_PRIORITY 0
+
+typedef enum {
+
+ HWE_PHONEJACK_PLUG = 0,
+ HWE_BAT_CHARGER_PLUG,
+ HWE_BAT_CHARGER_OVERVOLTAGE,
+ HWE_BAT_BATTERY_LOW,
+ HWE_BAT_POWER_FAILED,
+ HWE_BAT_CHARGER_FULL,
+ HWE_POWER_KEY,
+} HW_EVENT_T;
+
+typedef enum {
+
+ PJT_NONE = 0,
+ PJT_CVBS,
+ PJT_HEADSET,
+} PHONEJACK_TYPE;
+
+typedef enum {
+
+ PWRK_UNPRESS = 0,
+ PWRK_PRESS,
+} POWERKEY_TYPE;
+
+typedef enum {
+
+ UNPLUG = 0,
+ PLUGGED,
+} PLUG_TYPE;
+
+struct mxc_hw_event {
+ unsigned int event;
+ int args;
+};
+
+#ifdef __KERNEL__
+extern int hw_event_send(int priority, struct mxc_hw_event *new_event);
+#endif
+
+#endif /* HW_EVENT_H */