Bump imx6 edge to 6.0.y (#4339)
This commit is contained in:
parent
5a82aea458
commit
f2c2bba069
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ case $BRANCH in
|
||||
|
||||
edge)
|
||||
|
||||
KERNELBRANCH='branch:linux-5.19.y'
|
||||
KERNELBRANCH='branch:linux-6.0.y'
|
||||
|
||||
;;
|
||||
|
||||
|
||||
287
patch/kernel/archive/imx6-6.0/-
Normal file
287
patch/kernel/archive/imx6-6.0/-
Normal file
@ -0,0 +1,287 @@
|
||||
From 3ec70749ae3cb072f19d886981a217121f776415 Mon Sep 17 00:00:00 2001
|
||||
From: Igor Pecovnik <igor.pecovnik@gmail.com>
|
||||
Date: Sat, 6 Nov 2021 19:15:23 +0100
|
||||
Subject: [PATCH] Revert "net: Remove net/ipx.h and uapi/linux/ipx.h header
|
||||
files"
|
||||
|
||||
This reverts commit 6c9b40844751ea30c72f7a2f92f4d704bc6b2927.
|
||||
---
|
||||
include/net/ipx.h | 171 +++++++++++++++++++++++++++++++++++++++
|
||||
include/uapi/linux/ipx.h | 87 ++++++++++++++++++++
|
||||
2 files changed, 258 insertions(+)
|
||||
create mode 100644 include/net/ipx.h
|
||||
create mode 100644 include/uapi/linux/ipx.h
|
||||
|
||||
diff --git a/include/net/ipx.h b/include/net/ipx.h
|
||||
new file mode 100644
|
||||
index 000000000000..9d1342807b59
|
||||
--- /dev/null
|
||||
+++ b/include/net/ipx.h
|
||||
@@ -0,0 +1,171 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+#ifndef _NET_INET_IPX_H_
|
||||
+#define _NET_INET_IPX_H_
|
||||
+/*
|
||||
+ * The following information is in its entirety obtained from:
|
||||
+ *
|
||||
+ * Novell 'IPX Router Specification' Version 1.10
|
||||
+ * Part No. 107-000029-001
|
||||
+ *
|
||||
+ * Which is available from ftp.novell.com
|
||||
+ */
|
||||
+
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <net/datalink.h>
|
||||
+#include <linux/ipx.h>
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/refcount.h>
|
||||
+
|
||||
+struct ipx_address {
|
||||
+ __be32 net;
|
||||
+ __u8 node[IPX_NODE_LEN];
|
||||
+ __be16 sock;
|
||||
+};
|
||||
+
|
||||
+#define ipx_broadcast_node "\377\377\377\377\377\377"
|
||||
+#define ipx_this_node "\0\0\0\0\0\0"
|
||||
+
|
||||
+#define IPX_MAX_PPROP_HOPS 8
|
||||
+
|
||||
+struct ipxhdr {
|
||||
+ __be16 ipx_checksum __packed;
|
||||
+#define IPX_NO_CHECKSUM cpu_to_be16(0xFFFF)
|
||||
+ __be16 ipx_pktsize __packed;
|
||||
+ __u8 ipx_tctrl;
|
||||
+ __u8 ipx_type;
|
||||
+#define IPX_TYPE_UNKNOWN 0x00
|
||||
+#define IPX_TYPE_RIP 0x01 /* may also be 0 */
|
||||
+#define IPX_TYPE_SAP 0x04 /* may also be 0 */
|
||||
+#define IPX_TYPE_SPX 0x05 /* SPX protocol */
|
||||
+#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */
|
||||
+#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast */
|
||||
+ struct ipx_address ipx_dest __packed;
|
||||
+ struct ipx_address ipx_source __packed;
|
||||
+};
|
||||
+
|
||||
+/* From af_ipx.c */
|
||||
+extern int sysctl_ipx_pprop_broadcasting;
|
||||
+
|
||||
+struct ipx_interface {
|
||||
+ /* IPX address */
|
||||
+ __be32 if_netnum;
|
||||
+ unsigned char if_node[IPX_NODE_LEN];
|
||||
+ refcount_t refcnt;
|
||||
+
|
||||
+ /* physical device info */
|
||||
+ struct net_device *if_dev;
|
||||
+ struct datalink_proto *if_dlink;
|
||||
+ __be16 if_dlink_type;
|
||||
+
|
||||
+ /* socket support */
|
||||
+ unsigned short if_sknum;
|
||||
+ struct hlist_head if_sklist;
|
||||
+ spinlock_t if_sklist_lock;
|
||||
+
|
||||
+ /* administrative overhead */
|
||||
+ int if_ipx_offset;
|
||||
+ unsigned char if_internal;
|
||||
+ unsigned char if_primary;
|
||||
+
|
||||
+ struct list_head node; /* node in ipx_interfaces list */
|
||||
+};
|
||||
+
|
||||
+struct ipx_route {
|
||||
+ __be32 ir_net;
|
||||
+ struct ipx_interface *ir_intrfc;
|
||||
+ unsigned char ir_routed;
|
||||
+ unsigned char ir_router_node[IPX_NODE_LEN];
|
||||
+ struct list_head node; /* node in ipx_routes list */
|
||||
+ refcount_t refcnt;
|
||||
+};
|
||||
+
|
||||
+struct ipx_cb {
|
||||
+ u8 ipx_tctrl;
|
||||
+ __be32 ipx_dest_net;
|
||||
+ __be32 ipx_source_net;
|
||||
+ struct {
|
||||
+ __be32 netnum;
|
||||
+ int index;
|
||||
+ } last_hop;
|
||||
+};
|
||||
+
|
||||
+#include <net/sock.h>
|
||||
+
|
||||
+struct ipx_sock {
|
||||
+ /* struct sock has to be the first member of ipx_sock */
|
||||
+ struct sock sk;
|
||||
+ struct ipx_address dest_addr;
|
||||
+ struct ipx_interface *intrfc;
|
||||
+ __be16 port;
|
||||
+#ifdef CONFIG_IPX_INTERN
|
||||
+ unsigned char node[IPX_NODE_LEN];
|
||||
+#endif
|
||||
+ unsigned short type;
|
||||
+ /*
|
||||
+ * To handle special ncp connection-handling sockets for mars_nwe,
|
||||
+ * the connection number must be stored in the socket.
|
||||
+ */
|
||||
+ unsigned short ipx_ncp_conn;
|
||||
+};
|
||||
+
|
||||
+static inline struct ipx_sock *ipx_sk(struct sock *sk)
|
||||
+{
|
||||
+ return (struct ipx_sock *)sk;
|
||||
+}
|
||||
+
|
||||
+#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0]))
|
||||
+
|
||||
+#define IPX_MIN_EPHEMERAL_SOCKET 0x4000
|
||||
+#define IPX_MAX_EPHEMERAL_SOCKET 0x7fff
|
||||
+
|
||||
+extern struct list_head ipx_routes;
|
||||
+extern rwlock_t ipx_routes_lock;
|
||||
+
|
||||
+extern struct list_head ipx_interfaces;
|
||||
+struct ipx_interface *ipx_interfaces_head(void);
|
||||
+extern spinlock_t ipx_interfaces_lock;
|
||||
+
|
||||
+extern struct ipx_interface *ipx_primary_net;
|
||||
+
|
||||
+int ipx_proc_init(void);
|
||||
+void ipx_proc_exit(void);
|
||||
+
|
||||
+const char *ipx_frame_name(__be16);
|
||||
+const char *ipx_device_name(struct ipx_interface *intrfc);
|
||||
+
|
||||
+static __inline__ void ipxitf_hold(struct ipx_interface *intrfc)
|
||||
+{
|
||||
+ refcount_inc(&intrfc->refcnt);
|
||||
+}
|
||||
+
|
||||
+void ipxitf_down(struct ipx_interface *intrfc);
|
||||
+struct ipx_interface *ipxitf_find_using_net(__be32 net);
|
||||
+int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node);
|
||||
+__be16 ipx_cksum(struct ipxhdr *packet, int length);
|
||||
+int ipxrtr_add_route(__be32 network, struct ipx_interface *intrfc,
|
||||
+ unsigned char *node);
|
||||
+void ipxrtr_del_routes(struct ipx_interface *intrfc);
|
||||
+int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
|
||||
+ struct msghdr *msg, size_t len, int noblock);
|
||||
+int ipxrtr_route_skb(struct sk_buff *skb);
|
||||
+struct ipx_route *ipxrtr_lookup(__be32 net);
|
||||
+int ipxrtr_ioctl(unsigned int cmd, void __user *arg);
|
||||
+
|
||||
+static __inline__ void ipxitf_put(struct ipx_interface *intrfc)
|
||||
+{
|
||||
+ if (refcount_dec_and_test(&intrfc->refcnt))
|
||||
+ ipxitf_down(intrfc);
|
||||
+}
|
||||
+
|
||||
+static __inline__ void ipxrtr_hold(struct ipx_route *rt)
|
||||
+{
|
||||
+ refcount_inc(&rt->refcnt);
|
||||
+}
|
||||
+
|
||||
+static __inline__ void ipxrtr_put(struct ipx_route *rt)
|
||||
+{
|
||||
+ if (refcount_dec_and_test(&rt->refcnt))
|
||||
+ kfree(rt);
|
||||
+}
|
||||
+#endif /* _NET_INET_IPX_H_ */
|
||||
diff --git a/include/uapi/linux/ipx.h b/include/uapi/linux/ipx.h
|
||||
new file mode 100644
|
||||
index 000000000000..3168137adae8
|
||||
--- /dev/null
|
||||
+++ b/include/uapi/linux/ipx.h
|
||||
@@ -0,0 +1,87 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
+#ifndef _IPX_H_
|
||||
+#define _IPX_H_
|
||||
+#include <linux/libc-compat.h> /* for compatibility with glibc netipx/ipx.h */
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/sockios.h>
|
||||
+#include <linux/socket.h>
|
||||
+#define IPX_NODE_LEN 6
|
||||
+#define IPX_MTU 576
|
||||
+
|
||||
+#if __UAPI_DEF_SOCKADDR_IPX
|
||||
+struct sockaddr_ipx {
|
||||
+ __kernel_sa_family_t sipx_family;
|
||||
+ __be16 sipx_port;
|
||||
+ __be32 sipx_network;
|
||||
+ unsigned char sipx_node[IPX_NODE_LEN];
|
||||
+ __u8 sipx_type;
|
||||
+ unsigned char sipx_zero; /* 16 byte fill */
|
||||
+};
|
||||
+#endif /* __UAPI_DEF_SOCKADDR_IPX */
|
||||
+
|
||||
+/*
|
||||
+ * So we can fit the extra info for SIOCSIFADDR into the address nicely
|
||||
+ */
|
||||
+#define sipx_special sipx_port
|
||||
+#define sipx_action sipx_zero
|
||||
+#define IPX_DLTITF 0
|
||||
+#define IPX_CRTITF 1
|
||||
+
|
||||
+#if __UAPI_DEF_IPX_ROUTE_DEFINITION
|
||||
+struct ipx_route_definition {
|
||||
+ __be32 ipx_network;
|
||||
+ __be32 ipx_router_network;
|
||||
+ unsigned char ipx_router_node[IPX_NODE_LEN];
|
||||
+};
|
||||
+#endif /* __UAPI_DEF_IPX_ROUTE_DEFINITION */
|
||||
+
|
||||
+#if __UAPI_DEF_IPX_INTERFACE_DEFINITION
|
||||
+struct ipx_interface_definition {
|
||||
+ __be32 ipx_network;
|
||||
+ unsigned char ipx_device[16];
|
||||
+ unsigned char ipx_dlink_type;
|
||||
+#define IPX_FRAME_NONE 0
|
||||
+#define IPX_FRAME_SNAP 1
|
||||
+#define IPX_FRAME_8022 2
|
||||
+#define IPX_FRAME_ETHERII 3
|
||||
+#define IPX_FRAME_8023 4
|
||||
+#define IPX_FRAME_TR_8022 5 /* obsolete */
|
||||
+ unsigned char ipx_special;
|
||||
+#define IPX_SPECIAL_NONE 0
|
||||
+#define IPX_PRIMARY 1
|
||||
+#define IPX_INTERNAL 2
|
||||
+ unsigned char ipx_node[IPX_NODE_LEN];
|
||||
+};
|
||||
+#endif /* __UAPI_DEF_IPX_INTERFACE_DEFINITION */
|
||||
+
|
||||
+#if __UAPI_DEF_IPX_CONFIG_DATA
|
||||
+struct ipx_config_data {
|
||||
+ unsigned char ipxcfg_auto_select_primary;
|
||||
+ unsigned char ipxcfg_auto_create_interfaces;
|
||||
+};
|
||||
+#endif /* __UAPI_DEF_IPX_CONFIG_DATA */
|
||||
+
|
||||
+/*
|
||||
+ * OLD Route Definition for backward compatibility.
|
||||
+ */
|
||||
+
|
||||
+#if __UAPI_DEF_IPX_ROUTE_DEF
|
||||
+struct ipx_route_def {
|
||||
+ __be32 ipx_network;
|
||||
+ __be32 ipx_router_network;
|
||||
+#define IPX_ROUTE_NO_ROUTER 0
|
||||
+ unsigned char ipx_router_node[IPX_NODE_LEN];
|
||||
+ unsigned char ipx_device[16];
|
||||
+ unsigned short ipx_flags;
|
||||
+#define IPX_RT_SNAP 8
|
||||
+#define IPX_RT_8022 4
|
||||
+#define IPX_RT_BLUEBOOK 2
|
||||
+#define IPX_RT_ROUTED 1
|
||||
+};
|
||||
+#endif /* __UAPI_DEF_IPX_ROUTE_DEF */
|
||||
+
|
||||
+#define SIOCAIPXITFCRT (SIOCPROTOPRIVATE)
|
||||
+#define SIOCAIPXPRISLT (SIOCPROTOPRIVATE + 1)
|
||||
+#define SIOCIPXCFGDATA (SIOCPROTOPRIVATE + 2)
|
||||
+#define SIOCIPXNCPCONN (SIOCPROTOPRIVATE + 3)
|
||||
+#endif /* _IPX_H_ */
|
||||
--
|
||||
2.25.1
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
From f5528e96b7dd2b30e1accc518df85d14baad6bae Mon Sep 17 00:00:00 2001
|
||||
From: Peter Chen <peter.chen@nxp.com>
|
||||
Date: Thu, 18 May 2017 08:48:57 +0800
|
||||
Subject: [PATCH 1/9] binding-doc: power: pwrseq-generic: add binding doc for
|
||||
generic power sequence library
|
||||
|
||||
Add binding doc for generic power sequence library.
|
||||
|
||||
Signed-off-by: Peter Chen <peter.chen@nxp.com>
|
||||
Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
|
||||
Acked-by: Rob Herring <robh@kernel.org>
|
||||
---
|
||||
.../bindings/power/pwrseq/pwrseq-generic.txt | 48 +++++++++++++++++++
|
||||
1 file changed, 48 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
|
||||
|
||||
diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
|
||||
new file mode 100644
|
||||
index 000000000000..ebf0d477b688
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
|
||||
@@ -0,0 +1,48 @@
|
||||
+The generic power sequence library
|
||||
+
|
||||
+Some hard-wired devices (eg USB/MMC) need to do power sequence before
|
||||
+the device can be enumerated on the bus, the typical power sequence
|
||||
+like: enable USB PHY clock, toggle reset pin, etc. But current
|
||||
+Linux device driver lacks of such code to do it, it may cause some
|
||||
+hard-wired devices works abnormal or can't be recognized by
|
||||
+controller at all. The power sequence will be done before this device
|
||||
+can be found at the bus.
|
||||
+
|
||||
+The power sequence properties is under the device node.
|
||||
+
|
||||
+Optional properties:
|
||||
+- clocks: the input clocks for device.
|
||||
+- reset-gpios: Should specify the GPIO for reset.
|
||||
+- reset-duration-us: the duration in microsecond for assert reset signal.
|
||||
+
|
||||
+Below is the example of USB power sequence properties on USB device
|
||||
+nodes which have two level USB hubs.
|
||||
+
|
||||
+&usbotg1 {
|
||||
+ vbus-supply = <®_usb_otg1_vbus>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pinctrl_usb_otg1_id>;
|
||||
+ status = "okay";
|
||||
+
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+ genesys: hub@1 {
|
||||
+ compatible = "usb5e3,608";
|
||||
+ reg = <1>;
|
||||
+
|
||||
+ clocks = <&clks IMX6SX_CLK_CKO>;
|
||||
+ reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
|
||||
+ reset-duration-us = <10>;
|
||||
+
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+ asix: ethernet@1 {
|
||||
+ compatible = "usbb95,1708";
|
||||
+ reg = <1>;
|
||||
+
|
||||
+ clocks = <&clks IMX6SX_CLK_IPG>;
|
||||
+ reset-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>; /* ethernet_rst */
|
||||
+ reset-duration-us = <15>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
--
|
||||
2.20.1
|
||||
|
||||
@ -0,0 +1,853 @@
|
||||
From e42fbf22376c41b275d47b9cfac360c66ee718dc Mon Sep 17 00:00:00 2001
|
||||
From: Peter Chen <peter.chen@nxp.com>
|
||||
Date: Thu, 18 May 2017 08:48:58 +0800
|
||||
Subject: [PATCH 2/9] power: add power sequence library
|
||||
|
||||
We have an well-known problem that the device needs to do some power
|
||||
sequence before it can be recognized by related host, the typical
|
||||
example like hard-wired mmc devices and usb devices.
|
||||
|
||||
This power sequence is hard to be described at device tree and handled by
|
||||
related host driver, so we have created a common power sequence
|
||||
library to cover this requirement. The core code has supplied
|
||||
some common helpers for host driver, and individual power sequence
|
||||
libraries handle kinds of power sequence for devices. The pwrseq
|
||||
librares always need to allocate extra instance for compatible
|
||||
string match.
|
||||
|
||||
pwrseq_generic is intended for general purpose of power sequence, which
|
||||
handles gpios and clocks currently, and can cover other controls in
|
||||
future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
|
||||
if only one power sequence is needed, else call of_pwrseq_on_list
|
||||
/of_pwrseq_off_list instead (eg, USB hub driver).
|
||||
|
||||
For new power sequence library, it can add its compatible string
|
||||
to pwrseq_of_match_table, then the pwrseq core will match it with
|
||||
DT's, and choose this library at runtime.
|
||||
|
||||
Signed-off-by: Peter Chen <peter.chen@nxp.com>
|
||||
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
|
||||
Tested-by Joshua Clayton <stillcompiling@gmail.com>
|
||||
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
|
||||
Tested-by: Matthias Kaehlcke <mka@chromium.org>
|
||||
---
|
||||
Documentation/power/power-sequence/design.rst | 54 +++
|
||||
MAINTAINERS | 9 +
|
||||
drivers/power/Kconfig | 1 +
|
||||
drivers/power/Makefile | 1 +
|
||||
drivers/power/pwrseq/Kconfig | 20 ++
|
||||
drivers/power/pwrseq/Makefile | 2 +
|
||||
drivers/power/pwrseq/core.c | 335 ++++++++++++++++++
|
||||
drivers/power/pwrseq/pwrseq_generic.c | 234 ++++++++++++
|
||||
include/linux/power/pwrseq.h | 81 +++++
|
||||
9 files changed, 737 insertions(+)
|
||||
create mode 100644 Documentation/power/power-sequence/design.rst
|
||||
create mode 100644 drivers/power/pwrseq/Kconfig
|
||||
create mode 100644 drivers/power/pwrseq/Makefile
|
||||
create mode 100644 drivers/power/pwrseq/core.c
|
||||
create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
|
||||
create mode 100644 include/linux/power/pwrseq.h
|
||||
|
||||
diff --git a/Documentation/power/power-sequence/design.rst b/Documentation/power/power-sequence/design.rst
|
||||
new file mode 100644
|
||||
index 000000000000..554608e5f3b6
|
||||
--- /dev/null
|
||||
+++ b/Documentation/power/power-sequence/design.rst
|
||||
@@ -0,0 +1,54 @@
|
||||
+====================================
|
||||
+Power Sequence Library
|
||||
+====================================
|
||||
+
|
||||
+:Date: Feb, 2017
|
||||
+:Author: Peter Chen <peter.chen@nxp.com>
|
||||
+
|
||||
+
|
||||
+Introduction
|
||||
+============
|
||||
+
|
||||
+We have an well-known problem that the device needs to do a power
|
||||
+sequence before it can be recognized by related host, the typical
|
||||
+examples are hard-wired mmc devices and usb devices. The host controller
|
||||
+can't know what kinds of this device is in its bus if the power
|
||||
+sequence has not done, since the related devices driver's probe calling
|
||||
+is determined by runtime according to eunumeration results. Besides,
|
||||
+the devices may have custom power sequence, so the power sequence library
|
||||
+which is independent with the devices is needed.
|
||||
+
|
||||
+Design
|
||||
+============
|
||||
+
|
||||
+The power sequence library includes the core file and customer power
|
||||
+sequence library. The core file exports interfaces are called by
|
||||
+host controller driver for power sequence and customer power sequence
|
||||
+library files to register its power sequence instance to global
|
||||
+power sequence list. The custom power sequence library creates power
|
||||
+sequence instance and implement custom power sequence.
|
||||
+
|
||||
+Since the power sequence describes hardware design, the description is
|
||||
+located at board description file, eg, device tree dts file. And
|
||||
+a specific power sequence belongs to device, so its description
|
||||
+is under the device node, please refer to:
|
||||
+Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
|
||||
+
|
||||
+Custom power sequence library allocates one power sequence instance at
|
||||
+bootup periods using postcore_initcall, this static allocated instance is
|
||||
+used to compare with device-tree (DT) node to see if this library can be
|
||||
+used for the node or not. When the result is matched, the core API will
|
||||
+try to get resourses (->get, implemented at each library) for power
|
||||
+sequence, if all resources are got, it will try to allocate another
|
||||
+instance for next possible request from host driver.
|
||||
+
|
||||
+Then, the host controller driver can carry out power sequence on for this
|
||||
+DT node, the library will do corresponding operations, like open clocks,
|
||||
+toggle gpio, etc. The power sequence off routine will close and free the
|
||||
+resources, and is called when the parent is removed. And the power
|
||||
+sequence suspend and resume routine can be called at host driver's
|
||||
+suspend and resume routine if needed.
|
||||
+
|
||||
+The exported interfaces
|
||||
+.. kernel-doc:: drivers/power/pwrseq/core.c
|
||||
+ :export:
|
||||
diff --git a/MAINTAINERS b/MAINTAINERS
|
||||
index 429c6c624861..88fd31d1870f 100644
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -12599,6 +12599,15 @@ F: drivers/firmware/psci/
|
||||
F: include/linux/psci.h
|
||||
F: include/uapi/linux/psci.h
|
||||
|
||||
+POWER SEQUENCE LIBRARY
|
||||
+M: Peter Chen <Peter.Chen@nxp.com>
|
||||
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
|
||||
+L: linux-pm@vger.kernel.org
|
||||
+S: Maintained
|
||||
+F: Documentation/devicetree/bindings/power/pwrseq/
|
||||
+F: drivers/power/pwrseq/
|
||||
+F: include/linux/power/pwrseq.h
|
||||
+
|
||||
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
|
||||
M: Sebastian Reichel <sre@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
|
||||
index ff0350ca3b74..78b6fa270cf9 100644
|
||||
--- a/drivers/power/Kconfig
|
||||
+++ b/drivers/power/Kconfig
|
||||
@@ -2,3 +2,4 @@
|
||||
source "drivers/power/avs/Kconfig"
|
||||
source "drivers/power/reset/Kconfig"
|
||||
source "drivers/power/supply/Kconfig"
|
||||
+source "drivers/power/pwrseq/Kconfig"
|
||||
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
|
||||
index b7c2e372186b..13046c7fb499 100644
|
||||
--- a/drivers/power/Makefile
|
||||
+++ b/drivers/power/Makefile
|
||||
@@ -2,3 +2,4 @@
|
||||
obj-$(CONFIG_POWER_AVS) += avs/
|
||||
obj-$(CONFIG_POWER_RESET) += reset/
|
||||
obj-$(CONFIG_POWER_SUPPLY) += supply/
|
||||
+obj-$(CONFIG_POWER_SEQUENCE) += pwrseq/
|
||||
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
|
||||
new file mode 100644
|
||||
index 000000000000..c6b356926cca
|
||||
--- /dev/null
|
||||
+++ b/drivers/power/pwrseq/Kconfig
|
||||
@@ -0,0 +1,20 @@
|
||||
+#
|
||||
+# Power Sequence library
|
||||
+#
|
||||
+
|
||||
+menuconfig POWER_SEQUENCE
|
||||
+ bool "Power sequence control"
|
||||
+ help
|
||||
+ It is used for drivers which needs to do power sequence
|
||||
+ (eg, turn on clock, toggle reset gpio) before the related
|
||||
+ devices can be found by hardware, eg, USB bus.
|
||||
+
|
||||
+if POWER_SEQUENCE
|
||||
+
|
||||
+config PWRSEQ_GENERIC
|
||||
+ bool "Generic power sequence control"
|
||||
+ depends on OF
|
||||
+ help
|
||||
+ This is the generic power sequence control library, and is
|
||||
+ supposed to support common power sequence usage.
|
||||
+endif
|
||||
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
|
||||
new file mode 100644
|
||||
index 000000000000..ad82389028c2
|
||||
--- /dev/null
|
||||
+++ b/drivers/power/pwrseq/Makefile
|
||||
@@ -0,0 +1,2 @@
|
||||
+obj-$(CONFIG_POWER_SEQUENCE) += core.o
|
||||
+obj-$(CONFIG_PWRSEQ_GENERIC) += pwrseq_generic.o
|
||||
diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
|
||||
new file mode 100644
|
||||
index 000000000000..3d19e62a2e76
|
||||
--- /dev/null
|
||||
+++ b/drivers/power/pwrseq/core.c
|
||||
@@ -0,0 +1,335 @@
|
||||
+/*
|
||||
+ * core.c power sequence core file
|
||||
+ *
|
||||
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
+ * Author: Peter Chen <peter.chen@nxp.com>
|
||||
+ *
|
||||
+ * This program is free software: you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 of
|
||||
+ * the License as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/power/pwrseq.h>
|
||||
+
|
||||
+static DEFINE_MUTEX(pwrseq_list_mutex);
|
||||
+static LIST_HEAD(pwrseq_list);
|
||||
+
|
||||
+static int pwrseq_get(struct device_node *np, struct pwrseq *p)
|
||||
+{
|
||||
+ if (p && p->get)
|
||||
+ return p->get(np, p);
|
||||
+
|
||||
+ return -ENOTSUPP;
|
||||
+}
|
||||
+
|
||||
+static int pwrseq_on(struct pwrseq *p)
|
||||
+{
|
||||
+ if (p && p->on)
|
||||
+ return p->on(p);
|
||||
+
|
||||
+ return -ENOTSUPP;
|
||||
+}
|
||||
+
|
||||
+static void pwrseq_off(struct pwrseq *p)
|
||||
+{
|
||||
+ if (p && p->off)
|
||||
+ p->off(p);
|
||||
+}
|
||||
+
|
||||
+static void pwrseq_put(struct pwrseq *p)
|
||||
+{
|
||||
+ if (p && p->put)
|
||||
+ p->put(p);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_register - Add pwrseq instance to global pwrseq list
|
||||
+ *
|
||||
+ * @pwrseq: the pwrseq instance
|
||||
+ */
|
||||
+void pwrseq_register(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ mutex_lock(&pwrseq_list_mutex);
|
||||
+ list_add(&pwrseq->node, &pwrseq_list);
|
||||
+ mutex_unlock(&pwrseq_list_mutex);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwrseq_register);
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_unregister - Remove pwrseq instance from global pwrseq list
|
||||
+ *
|
||||
+ * @pwrseq: the pwrseq instance
|
||||
+ */
|
||||
+void pwrseq_unregister(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ mutex_lock(&pwrseq_list_mutex);
|
||||
+ list_del(&pwrseq->node);
|
||||
+ mutex_unlock(&pwrseq_list_mutex);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwrseq_unregister);
|
||||
+
|
||||
+static struct pwrseq *pwrseq_find_available_instance(struct device_node *np)
|
||||
+{
|
||||
+ struct pwrseq *pwrseq;
|
||||
+
|
||||
+ mutex_lock(&pwrseq_list_mutex);
|
||||
+ list_for_each_entry(pwrseq, &pwrseq_list, node) {
|
||||
+ if (pwrseq->used)
|
||||
+ continue;
|
||||
+
|
||||
+ /* compare compatible string for pwrseq node */
|
||||
+ if (of_match_node(pwrseq->pwrseq_of_match_table, np)) {
|
||||
+ pwrseq->used = true;
|
||||
+ mutex_unlock(&pwrseq_list_mutex);
|
||||
+ return pwrseq;
|
||||
+ }
|
||||
+
|
||||
+ /* return generic pwrseq instance */
|
||||
+ if (!strcmp(pwrseq->pwrseq_of_match_table->compatible,
|
||||
+ "generic")) {
|
||||
+ pr_debug("using generic pwrseq instance for %s\n",
|
||||
+ np->full_name);
|
||||
+ pwrseq->used = true;
|
||||
+ mutex_unlock(&pwrseq_list_mutex);
|
||||
+ return pwrseq;
|
||||
+ }
|
||||
+ }
|
||||
+ mutex_unlock(&pwrseq_list_mutex);
|
||||
+ pr_debug("Can't find any pwrseq instances for %s\n", np->full_name);
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * of_pwrseq_on - Carry out power sequence on for device node
|
||||
+ *
|
||||
+ * @np: the device node would like to power on
|
||||
+ *
|
||||
+ * Carry out a single device power on. If multiple devices
|
||||
+ * need to be handled, use of_pwrseq_on_list() instead.
|
||||
+ *
|
||||
+ * Return a pointer to the power sequence instance on success,
|
||||
+ * or an error code otherwise.
|
||||
+ */
|
||||
+struct pwrseq *of_pwrseq_on(struct device_node *np)
|
||||
+{
|
||||
+ struct pwrseq *pwrseq;
|
||||
+ int ret;
|
||||
+
|
||||
+ pwrseq = pwrseq_find_available_instance(np);
|
||||
+ if (!pwrseq)
|
||||
+ return ERR_PTR(-ENOENT);
|
||||
+
|
||||
+ ret = pwrseq_get(np, pwrseq);
|
||||
+ if (ret) {
|
||||
+ /* Mark current pwrseq as unused */
|
||||
+ pwrseq->used = false;
|
||||
+ return ERR_PTR(ret);
|
||||
+ }
|
||||
+
|
||||
+ ret = pwrseq_on(pwrseq);
|
||||
+ if (ret)
|
||||
+ goto pwr_put;
|
||||
+
|
||||
+ return pwrseq;
|
||||
+
|
||||
+pwr_put:
|
||||
+ pwrseq_put(pwrseq);
|
||||
+ return ERR_PTR(ret);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(of_pwrseq_on);
|
||||
+
|
||||
+/**
|
||||
+ * of_pwrseq_off - Carry out power sequence off for this pwrseq instance
|
||||
+ *
|
||||
+ * @pwrseq: the pwrseq instance which related device would like to be off
|
||||
+ *
|
||||
+ * This API is used to power off single device, it is the opposite
|
||||
+ * operation for of_pwrseq_on.
|
||||
+ */
|
||||
+void of_pwrseq_off(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ pwrseq_off(pwrseq);
|
||||
+ pwrseq_put(pwrseq);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(of_pwrseq_off);
|
||||
+
|
||||
+/**
|
||||
+ * of_pwrseq_on_list - Carry out power sequence on for list
|
||||
+ *
|
||||
+ * @np: the device node would like to power on
|
||||
+ * @head: the list head for pwrseq list on this bus
|
||||
+ *
|
||||
+ * This API is used to power on multiple devices at single bus.
|
||||
+ * If there are several devices on bus (eg, USB bus), uses this
|
||||
+ * this API. Otherwise, use of_pwrseq_on instead. After the device
|
||||
+ * is powered on successfully, it will be added to pwrseq list for
|
||||
+ * this bus. The caller needs to use mutex_lock for concurrent.
|
||||
+ *
|
||||
+ * Return 0 on success, or an error value otherwise.
|
||||
+ */
|
||||
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
|
||||
+{
|
||||
+ struct pwrseq *pwrseq;
|
||||
+ struct pwrseq_list_per_dev *pwrseq_list_node;
|
||||
+
|
||||
+ pwrseq_list_node = kzalloc(sizeof(*pwrseq_list_node), GFP_KERNEL);
|
||||
+ if (!pwrseq_list_node)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ pwrseq = of_pwrseq_on(np);
|
||||
+ if (IS_ERR(pwrseq)) {
|
||||
+ kfree(pwrseq_list_node);
|
||||
+ return PTR_ERR(pwrseq);
|
||||
+ }
|
||||
+
|
||||
+ pwrseq_list_node->pwrseq = pwrseq;
|
||||
+ list_add(&pwrseq_list_node->list, head);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(of_pwrseq_on_list);
|
||||
+
|
||||
+/**
|
||||
+ * of_pwrseq_off_list - Carry out power sequence off for the list
|
||||
+ *
|
||||
+ * @head: the list head for pwrseq instance list on this bus
|
||||
+ *
|
||||
+ * This API is used to power off all devices on this bus, it is
|
||||
+ * the opposite operation for of_pwrseq_on_list.
|
||||
+ * The caller needs to use mutex_lock for concurrent.
|
||||
+ */
|
||||
+void of_pwrseq_off_list(struct list_head *head)
|
||||
+{
|
||||
+ struct pwrseq *pwrseq;
|
||||
+ struct pwrseq_list_per_dev *pwrseq_list_node, *tmp_node;
|
||||
+
|
||||
+ list_for_each_entry_safe(pwrseq_list_node, tmp_node, head, list) {
|
||||
+ pwrseq = pwrseq_list_node->pwrseq;
|
||||
+ of_pwrseq_off(pwrseq);
|
||||
+ list_del(&pwrseq_list_node->list);
|
||||
+ kfree(pwrseq_list_node);
|
||||
+ }
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(of_pwrseq_off_list);
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_suspend - Carry out power sequence suspend for this pwrseq instance
|
||||
+ *
|
||||
+ * @pwrseq: the pwrseq instance
|
||||
+ *
|
||||
+ * This API is used to do suspend operation on pwrseq instance.
|
||||
+ *
|
||||
+ * Return 0 on success, or an error value otherwise.
|
||||
+ */
|
||||
+int pwrseq_suspend(struct pwrseq *p)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (p && p->suspend)
|
||||
+ ret = p->suspend(p);
|
||||
+ else
|
||||
+ return ret;
|
||||
+
|
||||
+ if (!ret)
|
||||
+ p->suspended = true;
|
||||
+ else
|
||||
+ pr_err("%s failed\n", __func__);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwrseq_suspend);
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_resume - Carry out power sequence resume for this pwrseq instance
|
||||
+ *
|
||||
+ * @pwrseq: the pwrseq instance
|
||||
+ *
|
||||
+ * This API is used to do resume operation on pwrseq instance.
|
||||
+ *
|
||||
+ * Return 0 on success, or an error value otherwise.
|
||||
+ */
|
||||
+int pwrseq_resume(struct pwrseq *p)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (p && p->resume)
|
||||
+ ret = p->resume(p);
|
||||
+ else
|
||||
+ return ret;
|
||||
+
|
||||
+ if (!ret)
|
||||
+ p->suspended = false;
|
||||
+ else
|
||||
+ pr_err("%s failed\n", __func__);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwrseq_resume);
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_suspend_list - Carry out power sequence suspend for list
|
||||
+ *
|
||||
+ * @head: the list head for pwrseq instance list on this bus
|
||||
+ *
|
||||
+ * This API is used to do suspend on all power sequence instances on this bus.
|
||||
+ * The caller needs to use mutex_lock for concurrent.
|
||||
+ */
|
||||
+int pwrseq_suspend_list(struct list_head *head)
|
||||
+{
|
||||
+ struct pwrseq *pwrseq;
|
||||
+ struct pwrseq_list_per_dev *pwrseq_list_node;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ list_for_each_entry(pwrseq_list_node, head, list) {
|
||||
+ ret = pwrseq_suspend(pwrseq_list_node->pwrseq);
|
||||
+ if (ret)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (ret) {
|
||||
+ list_for_each_entry(pwrseq_list_node, head, list) {
|
||||
+ pwrseq = pwrseq_list_node->pwrseq;
|
||||
+ if (pwrseq->suspended)
|
||||
+ pwrseq_resume(pwrseq);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwrseq_suspend_list);
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_resume_list - Carry out power sequence resume for the list
|
||||
+ *
|
||||
+ * @head: the list head for pwrseq instance list on this bus
|
||||
+ *
|
||||
+ * This API is used to do resume on all power sequence instances on this bus.
|
||||
+ * The caller needs to use mutex_lock for concurrent.
|
||||
+ */
|
||||
+int pwrseq_resume_list(struct list_head *head)
|
||||
+{
|
||||
+ struct pwrseq_list_per_dev *pwrseq_list_node;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ list_for_each_entry(pwrseq_list_node, head, list) {
|
||||
+ ret = pwrseq_resume(pwrseq_list_node->pwrseq);
|
||||
+ if (ret)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwrseq_resume_list);
|
||||
diff --git a/drivers/power/pwrseq/pwrseq_generic.c b/drivers/power/pwrseq/pwrseq_generic.c
|
||||
new file mode 100644
|
||||
index 000000000000..4e7c09086cfb
|
||||
--- /dev/null
|
||||
+++ b/drivers/power/pwrseq/pwrseq_generic.c
|
||||
@@ -0,0 +1,234 @@
|
||||
+/*
|
||||
+ * pwrseq_generic.c Generic power sequence handling
|
||||
+ *
|
||||
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
+ * Author: Peter Chen <peter.chen@nxp.com>
|
||||
+ *
|
||||
+ * This program is free software: you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 of
|
||||
+ * the License as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/gpio/consumer.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_gpio.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+#include <linux/power/pwrseq.h>
|
||||
+
|
||||
+struct pwrseq_generic {
|
||||
+ struct pwrseq pwrseq;
|
||||
+ struct gpio_desc *gpiod_reset;
|
||||
+ struct clk *clks[PWRSEQ_MAX_CLKS];
|
||||
+ u32 duration_us;
|
||||
+ bool suspended;
|
||||
+};
|
||||
+
|
||||
+#define to_generic_pwrseq(p) container_of(p, struct pwrseq_generic, pwrseq)
|
||||
+
|
||||
+static int pwrseq_generic_alloc_instance(void);
|
||||
+static const struct of_device_id generic_id_table[] = {
|
||||
+ { .compatible = "generic",},
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+
|
||||
+static int pwrseq_generic_suspend(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
|
||||
+ int clk;
|
||||
+
|
||||
+ for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
|
||||
+
|
||||
+ pwrseq_gen->suspended = true;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int pwrseq_generic_resume(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
|
||||
+ int clk, ret = 0;
|
||||
+
|
||||
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
|
||||
+ ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
|
||||
+ if (ret) {
|
||||
+ pr_err("Can't enable clock, ret=%d\n", ret);
|
||||
+ goto err_disable_clks;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pwrseq_gen->suspended = false;
|
||||
+ return ret;
|
||||
+
|
||||
+err_disable_clks:
|
||||
+ while (--clk >= 0)
|
||||
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void pwrseq_generic_put(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
|
||||
+ int clk;
|
||||
+
|
||||
+ if (pwrseq_gen->gpiod_reset)
|
||||
+ gpiod_put(pwrseq_gen->gpiod_reset);
|
||||
+
|
||||
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++)
|
||||
+ clk_put(pwrseq_gen->clks[clk]);
|
||||
+
|
||||
+ pwrseq_unregister(&pwrseq_gen->pwrseq);
|
||||
+ kfree(pwrseq_gen);
|
||||
+}
|
||||
+
|
||||
+static void pwrseq_generic_off(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
|
||||
+ int clk;
|
||||
+
|
||||
+ if (pwrseq_gen->suspended)
|
||||
+ return;
|
||||
+
|
||||
+ for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
|
||||
+}
|
||||
+
|
||||
+static int pwrseq_generic_on(struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
|
||||
+ int clk, ret = 0;
|
||||
+ struct gpio_desc *gpiod_reset = pwrseq_gen->gpiod_reset;
|
||||
+
|
||||
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
|
||||
+ ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
|
||||
+ if (ret) {
|
||||
+ pr_err("Can't enable clock, ret=%d\n", ret);
|
||||
+ goto err_disable_clks;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (gpiod_reset) {
|
||||
+ u32 duration_us = pwrseq_gen->duration_us;
|
||||
+
|
||||
+ if (duration_us <= 10)
|
||||
+ udelay(10);
|
||||
+ else
|
||||
+ usleep_range(duration_us, duration_us + 100);
|
||||
+ gpiod_set_value(gpiod_reset, 0);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+
|
||||
+err_disable_clks:
|
||||
+ while (--clk >= 0)
|
||||
+ clk_disable_unprepare(pwrseq_gen->clks[clk]);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int pwrseq_generic_get(struct device_node *np, struct pwrseq *pwrseq)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
|
||||
+ enum of_gpio_flags flags;
|
||||
+ int reset_gpio, clk, ret = 0;
|
||||
+
|
||||
+ for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) {
|
||||
+ pwrseq_gen->clks[clk] = of_clk_get(np, clk);
|
||||
+ if (IS_ERR(pwrseq_gen->clks[clk])) {
|
||||
+ ret = PTR_ERR(pwrseq_gen->clks[clk]);
|
||||
+ if (ret != -ENOENT)
|
||||
+ goto err_put_clks;
|
||||
+ pwrseq_gen->clks[clk] = NULL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
|
||||
+ if (gpio_is_valid(reset_gpio)) {
|
||||
+ unsigned long gpio_flags;
|
||||
+
|
||||
+ if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
+ gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW;
|
||||
+ else
|
||||
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
|
||||
+
|
||||
+ ret = gpio_request_one(reset_gpio, gpio_flags,
|
||||
+ "pwrseq-reset-gpios");
|
||||
+ if (ret)
|
||||
+ goto err_put_clks;
|
||||
+
|
||||
+ pwrseq_gen->gpiod_reset = gpio_to_desc(reset_gpio);
|
||||
+ of_property_read_u32(np, "reset-duration-us",
|
||||
+ &pwrseq_gen->duration_us);
|
||||
+ } else if (reset_gpio == -ENOENT) {
|
||||
+ ; /* no such gpio */
|
||||
+ } else {
|
||||
+ ret = reset_gpio;
|
||||
+ pr_err("Failed to get reset gpio on %s, err = %d\n",
|
||||
+ np->full_name, reset_gpio);
|
||||
+ goto err_put_clks;
|
||||
+ }
|
||||
+
|
||||
+ /* allocate new one for later pwrseq instance request */
|
||||
+ ret = pwrseq_generic_alloc_instance();
|
||||
+ if (ret)
|
||||
+ goto err_put_gpio;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_put_gpio:
|
||||
+ if (pwrseq_gen->gpiod_reset)
|
||||
+ gpiod_put(pwrseq_gen->gpiod_reset);
|
||||
+err_put_clks:
|
||||
+ while (--clk >= 0)
|
||||
+ clk_put(pwrseq_gen->clks[clk]);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pwrseq_generic_alloc_instance - power sequence instance allocation
|
||||
+ *
|
||||
+ * This function is used to allocate one generic power sequence instance,
|
||||
+ * it is called when the system boots up and after one power sequence
|
||||
+ * instance is got successfully.
|
||||
+ *
|
||||
+ * Return zero on success or an error code otherwise.
|
||||
+ */
|
||||
+static int pwrseq_generic_alloc_instance(void)
|
||||
+{
|
||||
+ struct pwrseq_generic *pwrseq_gen;
|
||||
+
|
||||
+ pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
|
||||
+ if (!pwrseq_gen)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
|
||||
+ pwrseq_gen->pwrseq.get = pwrseq_generic_get;
|
||||
+ pwrseq_gen->pwrseq.on = pwrseq_generic_on;
|
||||
+ pwrseq_gen->pwrseq.off = pwrseq_generic_off;
|
||||
+ pwrseq_gen->pwrseq.put = pwrseq_generic_put;
|
||||
+ pwrseq_gen->pwrseq.suspend = pwrseq_generic_suspend;
|
||||
+ pwrseq_gen->pwrseq.resume = pwrseq_generic_resume;
|
||||
+
|
||||
+ pwrseq_register(&pwrseq_gen->pwrseq);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* Allocate one pwrseq instance during boots up */
|
||||
+static int __init pwrseq_generic_register(void)
|
||||
+{
|
||||
+ return pwrseq_generic_alloc_instance();
|
||||
+}
|
||||
+postcore_initcall(pwrseq_generic_register)
|
||||
diff --git a/include/linux/power/pwrseq.h b/include/linux/power/pwrseq.h
|
||||
new file mode 100644
|
||||
index 000000000000..cbc344cdf9d2
|
||||
--- /dev/null
|
||||
+++ b/include/linux/power/pwrseq.h
|
||||
@@ -0,0 +1,81 @@
|
||||
+#ifndef __LINUX_PWRSEQ_H
|
||||
+#define __LINUX_PWRSEQ_H
|
||||
+
|
||||
+#include <linux/of.h>
|
||||
+
|
||||
+#define PWRSEQ_MAX_CLKS 3
|
||||
+
|
||||
+/**
|
||||
+ * struct pwrseq - the power sequence structure
|
||||
+ * @pwrseq_of_match_table: the OF device id table this pwrseq library supports
|
||||
+ * @node: the list pointer to be added to pwrseq list
|
||||
+ * @get: the API is used to get pwrseq instance from the device node
|
||||
+ * @on: do power on for this pwrseq instance
|
||||
+ * @off: do power off for this pwrseq instance
|
||||
+ * @put: release the resources on this pwrseq instance
|
||||
+ * @suspend: do suspend operation on this pwrseq instance
|
||||
+ * @resume: do resume operation on this pwrseq instance
|
||||
+ * @used: this pwrseq instance is used by device
|
||||
+ */
|
||||
+struct pwrseq {
|
||||
+ const struct of_device_id *pwrseq_of_match_table;
|
||||
+ struct list_head node;
|
||||
+ int (*get)(struct device_node *np, struct pwrseq *p);
|
||||
+ int (*on)(struct pwrseq *p);
|
||||
+ void (*off)(struct pwrseq *p);
|
||||
+ void (*put)(struct pwrseq *p);
|
||||
+ int (*suspend)(struct pwrseq *p);
|
||||
+ int (*resume)(struct pwrseq *p);
|
||||
+ bool used;
|
||||
+ bool suspended;
|
||||
+};
|
||||
+
|
||||
+/* used for power sequence instance list in one driver */
|
||||
+struct pwrseq_list_per_dev {
|
||||
+ struct pwrseq *pwrseq;
|
||||
+ struct list_head list;
|
||||
+};
|
||||
+
|
||||
+#if IS_ENABLED(CONFIG_POWER_SEQUENCE)
|
||||
+void pwrseq_register(struct pwrseq *pwrseq);
|
||||
+void pwrseq_unregister(struct pwrseq *pwrseq);
|
||||
+struct pwrseq *of_pwrseq_on(struct device_node *np);
|
||||
+void of_pwrseq_off(struct pwrseq *pwrseq);
|
||||
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head);
|
||||
+void of_pwrseq_off_list(struct list_head *head);
|
||||
+int pwrseq_suspend(struct pwrseq *p);
|
||||
+int pwrseq_resume(struct pwrseq *p);
|
||||
+int pwrseq_suspend_list(struct list_head *head);
|
||||
+int pwrseq_resume_list(struct list_head *head);
|
||||
+#else
|
||||
+static inline void pwrseq_register(struct pwrseq *pwrseq) {}
|
||||
+static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
|
||||
+static inline struct pwrseq *of_pwrseq_on(struct device_node *np)
|
||||
+{
|
||||
+ return NULL;
|
||||
+}
|
||||
+static void of_pwrseq_off(struct pwrseq *pwrseq) {}
|
||||
+static int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+static void of_pwrseq_off_list(struct list_head *head) {}
|
||||
+static int pwrseq_suspend(struct pwrseq *p)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+static int pwrseq_resume(struct pwrseq *p)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+static int pwrseq_suspend_list(struct list_head *head)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+static int pwrseq_resume_list(struct list_head *head)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif /* CONFIG_POWER_SEQUENCE */
|
||||
+
|
||||
+#endif /* __LINUX_PWRSEQ_H */
|
||||
--
|
||||
2.20.1
|
||||
|
||||
@ -0,0 +1,165 @@
|
||||
From a42362841fe263a6c97a1793ccd4b9246ac2b108 Mon Sep 17 00:00:00 2001
|
||||
From: Peter Chen <peter.chen@nxp.com>
|
||||
Date: Thu, 18 May 2017 08:49:00 +0800
|
||||
Subject: [PATCH 4/9] usb: core: add power sequence handling for USB devices
|
||||
|
||||
Some hard-wired USB devices need to do power sequence to let the
|
||||
device work normally, the typical power sequence like: enable USB
|
||||
PHY clock, toggle reset pin, etc. But current Linux USB driver
|
||||
lacks of such code to do it, it may cause some hard-wired USB devices
|
||||
works abnormal or can't be recognized by controller at all.
|
||||
|
||||
In this patch, it calls power sequence library APIs to finish
|
||||
the power sequence events. It will do power on sequence at hub's
|
||||
probe for all devices under this hub (includes root hub).
|
||||
At hub_disconnect, it will do power off sequence which is at powered
|
||||
on list.
|
||||
|
||||
Signed-off-by: Peter Chen <peter.chen@nxp.com>
|
||||
Tested-by Joshua Clayton <stillcompiling@gmail.com>
|
||||
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
|
||||
Reviewed-by: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
|
||||
---
|
||||
drivers/usb/Kconfig | 1 +
|
||||
drivers/usb/core/hub.c | 49 ++++++++++++++++++++++++++++++++++++++----
|
||||
drivers/usb/core/hub.h | 1 +
|
||||
3 files changed, 47 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
|
||||
index 6e59d370ef81..2162fd85b32d 100644
|
||||
--- a/drivers/usb/Kconfig
|
||||
+++ b/drivers/usb/Kconfig
|
||||
@@ -47,6 +47,7 @@ config USB
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select GENERIC_ALLOCATOR
|
||||
select USB_COMMON
|
||||
+ select POWER_SEQUENCE
|
||||
select NLS # for UTF-8 strings
|
||||
---help---
|
||||
Universal Serial Bus (USB) is a specification for a serial bus
|
||||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
||||
index 236313f41f4a..3db75b0d2426 100644
|
||||
--- a/drivers/usb/core/hub.c
|
||||
+++ b/drivers/usb/core/hub.c
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/kobject.h>
|
||||
+#include <linux/power/pwrseq.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
@@ -1705,6 +1706,7 @@ static void hub_disconnect(struct usb_interface *intf)
|
||||
hub->error = 0;
|
||||
hub_quiesce(hub, HUB_DISCONNECT);
|
||||
|
||||
+ of_pwrseq_off_list(&hub->pwrseq_on_list);
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
|
||||
/* Avoid races with recursively_mark_NOTATTACHED() */
|
||||
@@ -1751,11 +1753,41 @@ static bool hub_descriptor_is_sane(struct usb_host_interface *desc)
|
||||
return true;
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_OF
|
||||
+static int hub_of_pwrseq_on(struct usb_hub *hub)
|
||||
+{
|
||||
+ struct device *parent;
|
||||
+ struct usb_device *hdev = hub->hdev;
|
||||
+ struct device_node *np;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (hdev->parent)
|
||||
+ parent = &hdev->dev;
|
||||
+ else
|
||||
+ parent = bus_to_hcd(hdev->bus)->self.sysdev;
|
||||
+
|
||||
+ for_each_child_of_node(parent->of_node, np) {
|
||||
+ ret = of_pwrseq_on_list(np, &hub->pwrseq_on_list);
|
||||
+ /* Maybe no power sequence library is chosen */
|
||||
+ if (ret && ret != -ENOENT)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#else
|
||||
+static int hub_of_pwrseq_on(struct usb_hub *hub)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *desc;
|
||||
struct usb_device *hdev;
|
||||
struct usb_hub *hub;
|
||||
+ int ret = -ENODEV;
|
||||
|
||||
desc = intf->cur_altsetting;
|
||||
hdev = interface_to_usbdev(intf);
|
||||
@@ -1846,6 +1878,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
INIT_DELAYED_WORK(&hub->leds, led_work);
|
||||
INIT_DELAYED_WORK(&hub->init_work, NULL);
|
||||
INIT_WORK(&hub->events, hub_event);
|
||||
+ INIT_LIST_HEAD(&hub->pwrseq_on_list);
|
||||
spin_lock_init(&hub->irq_urb_lock);
|
||||
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
|
||||
usb_get_intf(intf);
|
||||
@@ -1861,11 +1894,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
|
||||
hub->quirk_check_port_auto_suspend = 1;
|
||||
|
||||
- if (hub_configure(hub, &desc->endpoint[0].desc) >= 0)
|
||||
- return 0;
|
||||
+ if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
|
||||
+ ret = hub_of_pwrseq_on(hub);
|
||||
+ if (!ret)
|
||||
+ return 0;
|
||||
+ }
|
||||
|
||||
hub_disconnect(intf);
|
||||
- return -ENODEV;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -3720,7 +3756,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
|
||||
/* stop hub_wq and related activity */
|
||||
hub_quiesce(hub, HUB_SUSPEND);
|
||||
- return 0;
|
||||
+ return pwrseq_suspend_list(&hub->pwrseq_on_list);
|
||||
}
|
||||
|
||||
/* Report wakeup requests from the ports of a resuming root hub */
|
||||
@@ -3760,8 +3796,13 @@ static void report_wakeup_requests(struct usb_hub *hub)
|
||||
static int hub_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||
+ int ret;
|
||||
|
||||
dev_dbg(&intf->dev, "%s\n", __func__);
|
||||
+ ret = pwrseq_resume_list(&hub->pwrseq_on_list);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
hub_activate(hub, HUB_RESUME);
|
||||
|
||||
/*
|
||||
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
|
||||
index a9e24e4b8df1..feab956a1414 100644
|
||||
--- a/drivers/usb/core/hub.h
|
||||
+++ b/drivers/usb/core/hub.h
|
||||
@@ -72,6 +72,7 @@ struct usb_hub {
|
||||
spinlock_t irq_urb_lock;
|
||||
struct timer_list irq_urb_retry;
|
||||
struct usb_port **ports;
|
||||
+ struct list_head pwrseq_on_list; /* powered pwrseq node list */
|
||||
};
|
||||
|
||||
/**
|
||||
--
|
||||
2.20.1
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
From bf1b3a63aa2f3438750f5acdf372705f8a1beb41 Mon Sep 17 00:00:00 2001
|
||||
From: Joshua Clayton <stillcompiling@gmail.com>
|
||||
Date: Thu, 18 May 2017 08:49:01 +0800
|
||||
Subject: [PATCH 5/9] ARM: dts: imx6qdl: Enable usb node children with <reg>
|
||||
|
||||
Give usb nodes #address and #size attributes, so that a child node
|
||||
representing a permanently connected device such as an onboard hub may
|
||||
be addressed with a <reg> attribute
|
||||
|
||||
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
|
||||
Signed-off-by: Peter Chen <peter.chen@nxp.com>
|
||||
---
|
||||
arch/arm/boot/dts/imx6qdl.dtsi | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
|
||||
index fe17a3405edc..a5f2f981255f 100644
|
||||
--- a/arch/arm/boot/dts/imx6qdl.dtsi
|
||||
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
|
||||
@@ -977,6 +977,8 @@
|
||||
|
||||
usbh1: usb@2184200 {
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
reg = <0x02184200 0x200>;
|
||||
interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6QDL_CLK_USBOH3>;
|
||||
@@ -991,6 +993,8 @@
|
||||
|
||||
usbh2: usb@2184400 {
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
reg = <0x02184400 0x200>;
|
||||
interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6QDL_CLK_USBOH3>;
|
||||
@@ -1006,6 +1010,8 @@
|
||||
|
||||
usbh3: usb@2184600 {
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
reg = <0x02184600 0x200>;
|
||||
interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6QDL_CLK_USBOH3>;
|
||||
--
|
||||
2.20.1
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
From 0ae59d1767a9cf9875b35a026b5df13eeb3694db Mon Sep 17 00:00:00 2001
|
||||
From: Joshua Clayton <stillcompiling@gmail.com>
|
||||
Date: Thu, 18 May 2017 08:49:03 +0800
|
||||
Subject: [PATCH 7/9] ARM: dts: imx6q-evi: Fix onboard hub reset line
|
||||
|
||||
Previously the onboard hub was made to work by treating its
|
||||
reset gpio as a regulator enable.
|
||||
Get rid of that kludge now that pwseq has added reset gpio support
|
||||
Move pin muxing the hub reset pin into the usbh1 group
|
||||
|
||||
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
|
||||
Signed-off-by: Peter Chen <peter.chen@nxp.com>
|
||||
---
|
||||
arch/arm/boot/dts/imx6q-evi.dts | 25 +++++++------------------
|
||||
1 file changed, 7 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
|
||||
index c63f371ede8b..546d9d4e8ca1 100644
|
||||
--- a/arch/arm/boot/dts/imx6q-evi.dts
|
||||
+++ b/arch/arm/boot/dts/imx6q-evi.dts
|
||||
@@ -55,18 +55,6 @@
|
||||
reg = <0x10000000 0x40000000>;
|
||||
};
|
||||
|
||||
- reg_usbh1_vbus: regulator-usbhubreset {
|
||||
- compatible = "regulator-fixed";
|
||||
- regulator-name = "usbh1_vbus";
|
||||
- regulator-min-microvolt = <5000000>;
|
||||
- regulator-max-microvolt = <5000000>;
|
||||
- enable-active-high;
|
||||
- startup-delay-us = <2>;
|
||||
- pinctrl-names = "default";
|
||||
- pinctrl-0 = <&pinctrl_usbh1_hubreset>;
|
||||
- gpio = <&gpio7 12 GPIO_ACTIVE_HIGH>;
|
||||
- };
|
||||
-
|
||||
reg_usb_otg_vbus: regulator-usbotgvbus {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "usb_otg_vbus";
|
||||
@@ -214,12 +202,18 @@
|
||||
};
|
||||
|
||||
&usbh1 {
|
||||
- vbus-supply = <®_usbh1_vbus>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usbh1>;
|
||||
dr_mode = "host";
|
||||
disable-over-current;
|
||||
status = "okay";
|
||||
+
|
||||
+ usb2415host: hub@1 {
|
||||
+ compatible = "usb424,2513";
|
||||
+ reg = <1>;
|
||||
+ reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
|
||||
+ reset-duration-us = <3000>;
|
||||
+ };
|
||||
};
|
||||
|
||||
&usbotg {
|
||||
@@ -482,11 +476,6 @@
|
||||
MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0
|
||||
/* usbh1_b OC */
|
||||
MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x1b0b0
|
||||
- >;
|
||||
- };
|
||||
-
|
||||
- pinctrl_usbh1_hubreset: usbh1hubresetgrp {
|
||||
- fsl,pins = <
|
||||
MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
|
||||
>;
|
||||
};
|
||||
--
|
||||
2.20.1
|
||||
|
||||
@ -0,0 +1,511 @@
|
||||
From 9a4d8e886600c7c330590a2435dd74eb16d480ce Mon Sep 17 00:00:00 2001
|
||||
From: Steve Arnold <nerdboy@gentoo.org>
|
||||
Date: Fri, 15 Dec 2017 16:43:22 -0800
|
||||
Subject: [PATCH 8/9] ARM: dts,driver: imx6,udooqdl: add arduino manager driver
|
||||
and update dts
|
||||
|
||||
* note this is required to upload sketches to sam3 from arduino IDE
|
||||
|
||||
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
|
||||
---
|
||||
arch/arm/boot/dts/imx6qdl-udoo.dtsi | 20 ++
|
||||
drivers/misc/Kconfig | 7 +
|
||||
drivers/misc/Makefile | 1 +
|
||||
drivers/misc/udoo_ard.c | 417 ++++++++++++++++++++++++++++
|
||||
4 files changed, 445 insertions(+)
|
||||
create mode 100755 drivers/misc/udoo_ard.c
|
||||
|
||||
diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
|
||||
index 4781a9e04338..554f601eb72a 100644
|
||||
--- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi
|
||||
+++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
|
||||
@@ -84,6 +84,17 @@
|
||||
mux-int-port = <1>;
|
||||
mux-ext-port = <6>;
|
||||
};
|
||||
+
|
||||
+ udoo_ard: udoo_ard_manager {
|
||||
+ compatible = "udoo,imx6q-udoo-ard";
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pinctrl_udooard>;
|
||||
+ bossac-clk-gpio = <&gpio6 3 0>;
|
||||
+ bossac-dat-gpio = <&gpio5 18 0>;
|
||||
+ bossac-erase-gpio = <&gpio4 21 0>;
|
||||
+ bossac-reset-gpio = <&gpio1 0 0>;
|
||||
+ status = "okay";
|
||||
+ };
|
||||
};
|
||||
|
||||
&fec {
|
||||
@@ -201,6 +212,15 @@
|
||||
>;
|
||||
};
|
||||
|
||||
+ pinctrl_udooard: udooardgrp {
|
||||
+ fsl,pins = <
|
||||
+ MX6QDL_PAD_DISP0_DAT0__GPIO4_IO21 0x80000000
|
||||
+ MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x80000000
|
||||
+ MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x80000000
|
||||
+ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x80000000
|
||||
+ >;
|
||||
+ };
|
||||
+
|
||||
pinctrl_usdhc3: usdhc3grp {
|
||||
fsl,pins = <
|
||||
MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
|
||||
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
||||
index 2cf9db44e4b2..f4616fc61808 100644
|
||||
--- a/drivers/misc/Kconfig
|
||||
+++ b/drivers/misc/Kconfig
|
||||
@@ -487,6 +487,13 @@ config PVPANIC
|
||||
a paravirtualized device provided by QEMU; it lets a virtual machine
|
||||
(guest) communicate panic events to the host.
|
||||
|
||||
+config UDOO_ARD
|
||||
+ tristate "UDOO-Arduino erase/reset Driver"
|
||||
+ default y
|
||||
+ help
|
||||
+ This driver is used to erase and reset arduino board via command sent
|
||||
+ over USB-to-SERIAL connection.
|
||||
+
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
||||
index dcc801e8834e..13e132dd62c9 100644
|
||||
--- a/drivers/misc/Makefile
|
||||
+++ b/drivers/misc/Makefile
|
||||
@@ -11,6 +11,7 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
|
||||
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
|
||||
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
|
||||
obj-$(CONFIG_ICS932S401) += ics932s401.o
|
||||
+obj-$(CONFIG_UDOO_ARD) += udoo_ard.o
|
||||
obj-$(CONFIG_LKDTM) += lkdtm/
|
||||
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
|
||||
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
|
||||
diff --git a/drivers/misc/udoo_ard.c b/drivers/misc/udoo_ard.c
|
||||
new file mode 100755
|
||||
index 000000000000..2210738e09c0
|
||||
--- /dev/null
|
||||
+++ b/drivers/misc/udoo_ard.c
|
||||
@@ -0,0 +1,417 @@
|
||||
+/*
|
||||
+ * udoo_ard.c
|
||||
+ * UDOO quad/dual Arduino flash erase / CPU resetter
|
||||
+ *
|
||||
+ * Copyright (C) 2013-2015 Aidilab srl
|
||||
+ * Author: UDOO Team <social@udoo.org>
|
||||
+ * Author: Giuseppe Pagano <giuseppe.pagano@seco.com>
|
||||
+ * Author: Francesco Montefoschi <francesco.monte@gmail.com>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published by
|
||||
+ * the Free Software Foundation.
|
||||
+ *
|
||||
+ * 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, see <http://www.gnu.org/licenses/>.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/sched/clock.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/workqueue.h>
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/of_gpio.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+
|
||||
+#define DRIVER_NAME "udoo_ard"
|
||||
+#define PINCTRL_DEFAULT "default"
|
||||
+#define AUTH_TOKEN 0x5A5A
|
||||
+#define MAX_MSEC_SINCE_LAST_IRQ 400
|
||||
+#define GRAY_TIME_BETWEEN_RESET 10000 // In this time we can't accept new erase/reset code
|
||||
+
|
||||
+static struct workqueue_struct *erase_reset_wq;
|
||||
+typedef struct {
|
||||
+ struct work_struct erase_reset_work;
|
||||
+ struct pinctrl *pinctrl;
|
||||
+ struct pinctrl_state *pins_default;
|
||||
+ int step;
|
||||
+ int cmdcode;
|
||||
+ int erase_reset_lock;
|
||||
+ int gpio_bossac_clk;
|
||||
+ int gpio_bossac_dat;
|
||||
+ int gpio_ard_erase;
|
||||
+ int gpio_ard_reset;
|
||||
+ unsigned long last_int_time_in_ns;
|
||||
+ unsigned long last_int_time_in_sec;
|
||||
+} erase_reset_work_t;
|
||||
+
|
||||
+erase_reset_work_t *work;
|
||||
+static u32 origTX, origRX; // original UART4 TX/RX pad control registers
|
||||
+static int major; // for /dev/udoo_ard
|
||||
+static struct class *udoo_class;
|
||||
+
|
||||
+static struct platform_device_id udoo_ard_devtype[] = {
|
||||
+ {
|
||||
+ /* keep it for coldfire */
|
||||
+ .name = DRIVER_NAME,
|
||||
+ .driver_data = 0,
|
||||
+ }, {
|
||||
+ /* sentinel */
|
||||
+ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(platform, udoo_ard_devtype);
|
||||
+
|
||||
+static const struct of_device_id udoo_ard_dt_ids[] = {
|
||||
+ { .compatible = "udoo,imx6q-udoo-ard", .data = &udoo_ard_devtype[0], },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, udoo_ard_dt_ids);
|
||||
+
|
||||
+static void disable_serial(void)
|
||||
+{
|
||||
+ u32 addrTX;
|
||||
+ void __iomem *_addrTX;
|
||||
+
|
||||
+ printk("[bossac] Disable UART4 serial port.\n");
|
||||
+
|
||||
+ addrTX = 0x20E01F8;
|
||||
+ _addrTX = ioremap(addrTX, 8);
|
||||
+
|
||||
+ origTX = __raw_readl(_addrTX);
|
||||
+ origRX = __raw_readl(_addrTX + 0x4);
|
||||
+
|
||||
+ __raw_writel(0x15, _addrTX);
|
||||
+ __raw_writel(0x15, _addrTX + 0x4);
|
||||
+
|
||||
+ iounmap(_addrTX);
|
||||
+}
|
||||
+
|
||||
+static void enable_serial(void)
|
||||
+{
|
||||
+ u32 addrTX;
|
||||
+ void __iomem *_addrTX;
|
||||
+
|
||||
+ printk("[bossac] Enable UART4 serial port.\n");
|
||||
+
|
||||
+ addrTX = 0x20E01F8;
|
||||
+ _addrTX = ioremap(addrTX, 8);
|
||||
+
|
||||
+ __raw_writel(origTX, _addrTX);
|
||||
+ __raw_writel(origRX, _addrTX + 0x4);
|
||||
+
|
||||
+ iounmap(_addrTX);
|
||||
+}
|
||||
+
|
||||
+static void erase_reset(void)
|
||||
+{
|
||||
+ printk("[bossac] UDOO ERASE and RESET on Sam3x started.\n");
|
||||
+
|
||||
+ gpio_direction_input(work->gpio_ard_erase);
|
||||
+ gpio_set_value(work->gpio_ard_reset, 1);
|
||||
+ msleep(1);
|
||||
+
|
||||
+ gpio_direction_output(work->gpio_ard_erase, 1);
|
||||
+ msleep(300);
|
||||
+ gpio_direction_input(work->gpio_ard_erase);
|
||||
+
|
||||
+ msleep(10);
|
||||
+ gpio_set_value(work->gpio_ard_reset, 0);
|
||||
+
|
||||
+ msleep(80);
|
||||
+ gpio_set_value(work->gpio_ard_reset, 1);
|
||||
+
|
||||
+ printk("[bossac] UDOO ERASE and RESET on Sam3x EXECUTED.\n");
|
||||
+}
|
||||
+
|
||||
+static void shutdown_sam3x(void)
|
||||
+{
|
||||
+ printk("[bossac] RESET on Sam3x.\n");
|
||||
+
|
||||
+ gpio_set_value(work->gpio_ard_reset, 0);
|
||||
+}
|
||||
+
|
||||
+static void erase_reset_wq_function( struct work_struct *work2)
|
||||
+{
|
||||
+ disable_serial();
|
||||
+ erase_reset();
|
||||
+ msleep(GRAY_TIME_BETWEEN_RESET);
|
||||
+
|
||||
+ work->erase_reset_lock = 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Called everytime the gpio_bossac_clk signal toggles.
|
||||
+ * If the auth token (16 bit) is found, we look for the command code (4 bit).
|
||||
+ * The code 0x0F is sent by Bossac to trigger an erase/reset (to achieve this,
|
||||
+ * erase_reset_wq is scheduled). Before starting to program the flash, we disable
|
||||
+ * the UART4 serial port, otherwise there is too noise on the serial lines (the
|
||||
+ * programming port and UART4 port are connected together, see hw schematics).
|
||||
+ * When Bossac finishes to flash/verify, the code 0x00 is sent which re-enables
|
||||
+ * the UART4 port.
|
||||
+ */
|
||||
+static irqreturn_t udoo_bossac_req(int irq, void *dev_id)
|
||||
+{
|
||||
+ int retval, auth_bit, expected_bit, msec_since_last_irq;
|
||||
+ u64 nowsec;
|
||||
+ unsigned long rem_nsec;
|
||||
+ erase_reset_work_t *erase_reset_work;
|
||||
+
|
||||
+ auth_bit = 0;
|
||||
+ if (gpio_get_value(work->gpio_bossac_dat) != 0x0) {
|
||||
+ auth_bit = 1;
|
||||
+ }
|
||||
+
|
||||
+ erase_reset_work = (erase_reset_work_t *)work;
|
||||
+
|
||||
+ nowsec = local_clock();
|
||||
+ rem_nsec = do_div(nowsec, 1000000000) ;
|
||||
+ msec_since_last_irq = (((unsigned long)nowsec * 1000) + rem_nsec/1000000 ) - (((unsigned long)erase_reset_work->last_int_time_in_sec * 1000) + erase_reset_work->last_int_time_in_ns/1000000);
|
||||
+
|
||||
+ if (msec_since_last_irq > MAX_MSEC_SINCE_LAST_IRQ) {
|
||||
+ erase_reset_work->step = 0;
|
||||
+#ifdef DEBUG
|
||||
+ printk("[bossac] Reset authentication timeout!\n");
|
||||
+#endif
|
||||
+ }
|
||||
+
|
||||
+#ifdef DEBUG
|
||||
+ printk("[bossac] STEP %d -> 0x%d \n", erase_reset_work->step, auth_bit);
|
||||
+#endif
|
||||
+ erase_reset_work->last_int_time_in_ns = rem_nsec;
|
||||
+ erase_reset_work->last_int_time_in_sec = nowsec;
|
||||
+
|
||||
+ if ( erase_reset_work->step < 16 ) { // Authenticating received token bit.
|
||||
+ expected_bit = (( AUTH_TOKEN >> erase_reset_work->step ) & 0x01 );
|
||||
+ if ( auth_bit == expected_bit ) {
|
||||
+ erase_reset_work->step = erase_reset_work->step + 1;
|
||||
+ } else {
|
||||
+ erase_reset_work->step = 0;
|
||||
+ }
|
||||
+ } else { // Passed all authentication step. Receiving command code.
|
||||
+ erase_reset_work->cmdcode = erase_reset_work->cmdcode | (auth_bit << (erase_reset_work->step - 16));
|
||||
+ erase_reset_work->step = erase_reset_work->step + 1;
|
||||
+ }
|
||||
+
|
||||
+#ifdef DEBUG
|
||||
+ printk("erase_reset_work->erase_reset_lock = %d \n", erase_reset_work->erase_reset_lock);
|
||||
+#endif
|
||||
+ if ( erase_reset_work->step == 20 ) { // Passed authentication and code acquiring step.
|
||||
+#ifdef DEBUG
|
||||
+ printk("[bossac] Received code = 0x%04x \n", erase_reset_work->cmdcode);
|
||||
+#endif
|
||||
+ if (erase_reset_work->cmdcode == 0xF) {
|
||||
+ if (erase_reset_work->erase_reset_lock == 0) {
|
||||
+ erase_reset_work->erase_reset_lock = 1;
|
||||
+ retval = queue_work( erase_reset_wq, (struct work_struct *)work );
|
||||
+ } else {
|
||||
+#ifdef DEBUG
|
||||
+ printk("Erase and reset operation already in progress. Do nothing.\n");
|
||||
+#endif
|
||||
+ }
|
||||
+ } else {
|
||||
+ enable_serial();
|
||||
+ }
|
||||
+ erase_reset_work->step = 0;
|
||||
+ erase_reset_work->cmdcode = 0;
|
||||
+ }
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Takes control of clock, data, erase, reset GPIOs.
|
||||
+ */
|
||||
+static int gpio_setup(void)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = gpio_request(work->gpio_bossac_clk, "BOSSA_CLK");
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR "request BOSSA_CLK IRQ\n");
|
||||
+ return -1;
|
||||
+ } else {
|
||||
+ gpio_direction_input(work->gpio_bossac_clk);
|
||||
+ }
|
||||
+
|
||||
+ ret = gpio_request(work->gpio_bossac_dat, "BOSSA_DAT");
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR "request BOSSA_DAT IRQ\n");
|
||||
+ return -1;
|
||||
+ } else {
|
||||
+ gpio_direction_input(work->gpio_bossac_dat);
|
||||
+ }
|
||||
+
|
||||
+ ret = gpio_request(work->gpio_ard_erase, "BOSSAC");
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR "request GPIO FOR ARDUINO ERASE\n");
|
||||
+ return -1;
|
||||
+ } else {
|
||||
+ gpio_direction_input(work->gpio_ard_erase);
|
||||
+ }
|
||||
+
|
||||
+ ret = gpio_request(work->gpio_ard_reset, "BOSSAC");
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR "request GPIO FOR ARDUINO RESET\n");
|
||||
+ return -1;
|
||||
+ } else {
|
||||
+ gpio_direction_output(work->gpio_ard_reset, 1);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t *off)
|
||||
+{
|
||||
+ char msg[10];
|
||||
+ long res;
|
||||
+
|
||||
+ if (len > 10)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+
|
||||
+ res = copy_from_user(msg, buff, len);
|
||||
+ if (res) {
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+ msg[len] = '\0';
|
||||
+
|
||||
+ if (strcmp(msg, "erase")==0) {
|
||||
+ erase_reset();
|
||||
+ } else if (strcmp(msg, "shutdown")==0) {
|
||||
+ shutdown_sam3x();
|
||||
+ } else if (strcmp(msg, "uartoff")==0) {
|
||||
+ disable_serial();
|
||||
+ } else if (strcmp(msg, "uarton")==0) {
|
||||
+ enable_serial();
|
||||
+ } else {
|
||||
+ printk("[bossac] udoo_ard invalid operation! %s", msg);
|
||||
+ }
|
||||
+
|
||||
+ return len;
|
||||
+}
|
||||
+
|
||||
+static struct file_operations fops = {
|
||||
+ .write = device_write,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * If a fdt udoo_ard entry is found, we register an IRQ on bossac clock line
|
||||
+ * and we create /dev/udoo_ard
|
||||
+ */
|
||||
+static int udoo_ard_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int retval;
|
||||
+ struct device *temp_class;
|
||||
+ struct platform_device *bdev;
|
||||
+ struct device_node *np;
|
||||
+
|
||||
+ bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
|
||||
+ np = pdev->dev.of_node;
|
||||
+
|
||||
+ if (!np)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ work = (erase_reset_work_t *)kmalloc(sizeof(erase_reset_work_t), GFP_KERNEL);
|
||||
+ if (work) {
|
||||
+ work->gpio_ard_reset = of_get_named_gpio(np, "bossac-reset-gpio", 0);
|
||||
+ work->gpio_ard_erase = of_get_named_gpio(np, "bossac-erase-gpio", 0);
|
||||
+ work->gpio_bossac_clk = of_get_named_gpio(np, "bossac-clk-gpio", 0);
|
||||
+ work->gpio_bossac_dat = of_get_named_gpio(np, "bossac-dat-gpio", 0);
|
||||
+ work->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
+ work->pins_default = pinctrl_lookup_state(work->pinctrl, PINCTRL_DEFAULT);
|
||||
+ } else {
|
||||
+ printk("[bossac] Failed to allocate data structure.");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ pinctrl_select_state(work->pinctrl, work->pins_default);
|
||||
+ gpio_setup();
|
||||
+
|
||||
+ printk("[bossac] Registering IRQ %d for BOSSAC Arduino erase/reset operation\n", gpio_to_irq(work->gpio_bossac_clk));
|
||||
+ retval = request_irq(gpio_to_irq(work->gpio_bossac_clk), udoo_bossac_req, IRQF_TRIGGER_FALLING, "UDOO", bdev);
|
||||
+
|
||||
+ major = register_chrdev(major, "udoo_ard", &fops);
|
||||
+ if (major < 0) {
|
||||
+ printk(KERN_ERR "[bossac] Cannot get major for UDOO Ard\n");
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+
|
||||
+ udoo_class = class_create(THIS_MODULE, "udoo_ard");
|
||||
+ if (IS_ERR(udoo_class)) {
|
||||
+ return PTR_ERR(udoo_class);
|
||||
+ }
|
||||
+
|
||||
+ temp_class = device_create(udoo_class, NULL, MKDEV(major, 0), NULL, "udoo_ard");
|
||||
+ if (IS_ERR(temp_class)) {
|
||||
+ return PTR_ERR(temp_class);
|
||||
+ }
|
||||
+
|
||||
+ printk("[bossac] Created device file /dev/udoo_ard\n");
|
||||
+
|
||||
+ erase_reset_wq = create_workqueue("erase_reset_queue");
|
||||
+ if (erase_reset_wq) {
|
||||
+
|
||||
+ /* Queue some work (item 1) */
|
||||
+ if (work) {
|
||||
+ INIT_WORK( (struct work_struct *)work, erase_reset_wq_function );
|
||||
+ work->step = 1;
|
||||
+ work->cmdcode = 0;
|
||||
+ work->last_int_time_in_ns = 0;
|
||||
+ work->last_int_time_in_sec = 0;
|
||||
+ work->erase_reset_lock = 0;
|
||||
+ // retval = queue_work( erase_reset_wq, (struct work_struct *)work );
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int udoo_ard_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ printk("[bossac] Unloading UDOO ard driver.\n");
|
||||
+ free_irq(gpio_to_irq(work->gpio_bossac_clk), NULL);
|
||||
+
|
||||
+ gpio_free(work->gpio_ard_reset);
|
||||
+ gpio_free(work->gpio_ard_erase);
|
||||
+ gpio_free(work->gpio_bossac_clk);
|
||||
+ gpio_free(work->gpio_bossac_dat);
|
||||
+
|
||||
+ device_destroy(udoo_class, MKDEV(major, 0));
|
||||
+ class_destroy(udoo_class);
|
||||
+ unregister_chrdev(major, "udoo_ard");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver udoo_ard_driver = {
|
||||
+ .driver = {
|
||||
+ .name = DRIVER_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = udoo_ard_dt_ids,
|
||||
+ },
|
||||
+ .id_table = udoo_ard_devtype,
|
||||
+ .probe = udoo_ard_probe,
|
||||
+ .remove = udoo_ard_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(udoo_ard_driver);
|
||||
+
|
||||
+MODULE_ALIAS("platform:"DRIVER_NAME);
|
||||
+MODULE_LICENSE("GPL");
|
||||
--
|
||||
2.20.1
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
--- ./arch/arm/mm/cache-feroceon-l2.c.orig 2013-04-26 13:18:32.000000000 -0600
|
||||
+++ ./arch/arm/mm/cache-feroceon-l2.c 2013-04-28 04:01:09.815592333 -0600
|
||||
@@ -117,7 +117,7 @@ static inline void l2_inv_pa_range(unsig
|
||||
l2_put_va(va_start);
|
||||
}
|
||||
|
||||
-static inline void l2_inv_all(void)
|
||||
+static void l2_inv_all(void)
|
||||
{
|
||||
__asm__("mcr p15, 1, %0, c15, c11, 0" : : "r" (0));
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
--- ./arch/arm/boot/dts/imx6qdl-hummingboard.dtsi.orig 2015-01-08 11:30:41.000000000 -0700
|
||||
+++ ./arch/arm/boot/dts/imx6qdl-hummingboard.dtsi 2015-01-13 14:19:29.696485445 -0700
|
||||
@@ -94,6 +94,31 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
+&i2c3 {
|
||||
+ clock-frequency = <100000>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pinctrl_hummingboard_i2c3>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&ecspi2 {
|
||||
+ fsl,spi-num-chipselects = <2>;
|
||||
+ cs-gpios = <&gpio2 26 1>, <&gpio2 27 1>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pinctrl_hummingboard_spi>;
|
||||
+ status = "okay";
|
||||
+ spidev@0x00 {
|
||||
+ compatible = "spidev";
|
||||
+ spi-max-frequency = <5000000>;
|
||||
+ reg = <0>;
|
||||
+ };
|
||||
+ spidev@0x01 {
|
||||
+ compatible = "spidev";
|
||||
+ spi-max-frequency = <5000000>;
|
||||
+ reg = <1>;
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
&iomuxc {
|
||||
hummingboard {
|
||||
pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 {
|
||||
@@ -103,6 +128,17 @@
|
||||
>;
|
||||
};
|
||||
|
||||
+ pinctrl_hummingboard_spi: hummingboard_spi {
|
||||
+ fsl,pins = <
|
||||
+ MX6QDL_PAD_EIM_OE__ECSPI2_MISO 0x100b1
|
||||
+ MX6QDL_PAD_EIM_CS1__ECSPI2_MOSI 0x100b1
|
||||
+ MX6QDL_PAD_EIM_CS0__ECSPI2_SCLK 0x100b1
|
||||
+ /* MX6QDL_PAD_EIM_RW__ECSPI2_SS0 0x100b1 */
|
||||
+ MX6QDL_PAD_EIM_RW__GPIO2_IO26 0x100b1
|
||||
+ MX6QDL_PAD_EIM_LBA__ECSPI2_SS1 0x100b1
|
||||
+ >;
|
||||
+ };
|
||||
+
|
||||
pinctrl_hummingboard_gpio3_5: hummingboard-gpio3_5 {
|
||||
fsl,pins = <
|
||||
MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x1b0b1
|
||||
@@ -129,6 +165,13 @@
|
||||
>;
|
||||
};
|
||||
|
||||
+ pinctrl_hummingboard_i2c3: hummingboard-i2c3 {
|
||||
+ fsl,pins = <
|
||||
+ MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1
|
||||
+ MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1
|
||||
+ >;
|
||||
+ };
|
||||
+
|
||||
pinctrl_hummingboard_spdif: hummingboard-spdif {
|
||||
fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>;
|
||||
};
|
||||
@ -0,0 +1,11 @@
|
||||
--- ./fs/timerfd.c.orig 2015-11-09 15:37:56.000000000 -0700
|
||||
+++ ./fs/timerfd.c 2015-11-14 08:20:51.720068530 -0700
|
||||
@@ -134,7 +134,7 @@ static void timerfd_setup_cancel(struct
|
||||
{
|
||||
if ((ctx->clockid == CLOCK_REALTIME ||
|
||||
ctx->clockid == CLOCK_REALTIME_ALARM) &&
|
||||
- (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
|
||||
+ (flags & TFD_TIMER_CANCEL_ON_SET)) {
|
||||
if (!ctx->might_cancel) {
|
||||
ctx->might_cancel = true;
|
||||
spin_lock(&cancel_lock);
|
||||
@ -1 +1 @@
|
||||
archive/imx6-5.15
|
||||
archive/imx6-6.0
|
||||
Loading…
Reference in New Issue
Block a user