diff mbox

[V4,18/26] usb: phy: mv_usb2_phy: add externel chip support

Message ID 1358762864-9249-19-git-send-email-chao.xie@marvell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Chao Xie Jan. 21, 2013, 10:07 a.m. UTC
For the vbus and idpin detection. It may be completed by some
external chip, for example the pmic chip 88pm860x in driver/mfd
can do it.
Although the usb controller can detect the vbus and id pin, but
it need clock on and PHY enabled to detect the
vbus/idpin. It will increase the power.
Using the external chip to detect vbus/idpin can save the power.

Signed-off-by: Chao Xie <chao.xie@marvell.com>
---
 drivers/usb/phy/mv_usb2_phy.c        |   54 +++++++++++++++++++++
 include/linux/platform_data/mv_usb.h |   15 ++----
 include/linux/usb/mv_usb2.h          |   85 ++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+), 10 deletions(-)

Comments

Russell King - ARM Linux Jan. 21, 2013, 3:51 p.m. UTC | #1
On Mon, Jan 21, 2013 at 05:07:36AM -0500, Chao Xie wrote:
> +	mv_phy->extern_chip.head = devm_kzalloc(&pdev->dev,
> +					sizeof(*mv_phy->extern_chip.head),
> +					GFP_KERNEL);
> +	if (mv_phy->extern_chip.head == NULL)
> +		return -ENOMEM;
> +	ATOMIC_INIT_NOTIFIER_HEAD(mv_phy->extern_chip.head);

Why do you need to allocate an atomic notifier list head as an entirely
separate data structure?
Chao Xie Jan. 22, 2013, 2:51 a.m. UTC | #2
On Mon, Jan 21, 2013 at 11:51 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Jan 21, 2013 at 05:07:36AM -0500, Chao Xie wrote:
>> +     mv_phy->extern_chip.head = devm_kzalloc(&pdev->dev,
>> +                                     sizeof(*mv_phy->extern_chip.head),
>> +                                     GFP_KERNEL);
>> +     if (mv_phy->extern_chip.head == NULL)
>> +             return -ENOMEM;
>> +     ATOMIC_INIT_NOTIFIER_HEAD(mv_phy->extern_chip.head);
>
> Why do you need to allocate an atomic notifier list head as an entirely
> separate data structure?
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Th reason is that the original code seperate the extern_chip and phy
support. So it depends
on the ->head to detect whether extern_chip is initialized or not.
Now it is combined with phy, the ->phy pointer can do the job.
Felipe Balbi Jan. 23, 2013, 11:47 a.m. UTC | #3
Hi,

On Tue, Jan 22, 2013 at 10:51:32AM +0800, Chao Xie wrote:
> On Mon, Jan 21, 2013 at 11:51 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Mon, Jan 21, 2013 at 05:07:36AM -0500, Chao Xie wrote:
> >> +     mv_phy->extern_chip.head = devm_kzalloc(&pdev->dev,
> >> +                                     sizeof(*mv_phy->extern_chip.head),
> >> +                                     GFP_KERNEL);
> >> +     if (mv_phy->extern_chip.head == NULL)
> >> +             return -ENOMEM;
> >> +     ATOMIC_INIT_NOTIFIER_HEAD(mv_phy->extern_chip.head);
> >
> > Why do you need to allocate an atomic notifier list head as an entirely
> > separate data structure?
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> Th reason is that the original code seperate the extern_chip and phy
> support. So it depends
> on the ->head to detect whether extern_chip is initialized or not.
> Now it is combined with phy, the ->phy pointer can do the job.

does that need to be dynamically allocated ?
Chao Xie Jan. 24, 2013, 1:33 a.m. UTC | #4
On Wed, Jan 23, 2013 at 7:47 PM, Felipe Balbi <balbi@ti.com> wrote:
> Hi,
>
> On Tue, Jan 22, 2013 at 10:51:32AM +0800, Chao Xie wrote:
>> On Mon, Jan 21, 2013 at 11:51 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> > On Mon, Jan 21, 2013 at 05:07:36AM -0500, Chao Xie wrote:
>> >> +     mv_phy->extern_chip.head = devm_kzalloc(&pdev->dev,
>> >> +                                     sizeof(*mv_phy->extern_chip.head),
>> >> +                                     GFP_KERNEL);
>> >> +     if (mv_phy->extern_chip.head == NULL)
>> >> +             return -ENOMEM;
>> >> +     ATOMIC_INIT_NOTIFIER_HEAD(mv_phy->extern_chip.head);
>> >
>> > Why do you need to allocate an atomic notifier list head as an entirely
>> > separate data structure?
>> > --
>> > To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> > the body of a message to majordomo@vger.kernel.org
>> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>> Th reason is that the original code seperate the extern_chip and phy
>> support. So it depends
>> on the ->head to detect whether extern_chip is initialized or not.
>> Now it is combined with phy, the ->phy pointer can do the job.
>
> does that need to be dynamically allocated ?
>
> --
> balbi
hi
It does not need to be dynamically allocated. I will modify it, and
send out V5 today. Thanks for Russell's review.
diff mbox

Patch

diff --git a/drivers/usb/phy/mv_usb2_phy.c b/drivers/usb/phy/mv_usb2_phy.c
index c2bccae..0bea901 100644
--- a/drivers/usb/phy/mv_usb2_phy.c
+++ b/drivers/usb/phy/mv_usb2_phy.c
@@ -136,6 +136,53 @@  struct mv_usb2_phy *mv_usb2_get_phy(void)
 }
 EXPORT_SYMBOL(mv_usb2_get_phy);
 
+int mv_usb2_register_notifier(struct mv_usb2_phy *phy,
+		struct notifier_block *nb)
+{
+	int ret;
+
+	if (!phy)
+		return -ENODEV;
+
+	ret = atomic_notifier_chain_register(phy->extern_chip.head, nb);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(mv_usb2_register_notifier);
+
+int mv_usb2_unregister_notifier(struct mv_usb2_phy *phy,
+		struct notifier_block *nb)
+{
+	int ret;
+
+	if (!phy)
+		return -ENODEV;
+
+	ret = atomic_notifier_chain_unregister(phy->extern_chip.head, nb);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(mv_usb2_unregister_notifier);
+
+int mv_usb2_notify(struct mv_usb2_phy *phy, unsigned long val, void *v)
+{
+	int ret;
+
+	if (!phy)
+		return -ENODEV;
+
+	ret = atomic_notifier_call_chain(phy->extern_chip.head, val, v);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(mv_usb2_notify);
+
 static unsigned int u2o_get(void __iomem *base, unsigned int offset)
 {
 	return readl(base + offset);
@@ -414,6 +461,13 @@  static int usb_phy_probe(struct platform_device *pdev)
 	mv_phy->init = usb_phy_init;
 	mv_phy->shutdown = usb_phy_shutdown;
 
+	mv_phy->extern_chip.head = devm_kzalloc(&pdev->dev,
+					sizeof(*mv_phy->extern_chip.head),
+					GFP_KERNEL);
+	if (mv_phy->extern_chip.head == NULL)
+		return -ENOMEM;
+	ATOMIC_INIT_NOTIFIER_HEAD(mv_phy->extern_chip.head);
+
 	platform_set_drvdata(pdev, mv_phy);
 
 	the_phy = mv_phy;
diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h
index fd3d1b4..dc25d60 100644
--- a/include/linux/platform_data/mv_usb.h
+++ b/include/linux/platform_data/mv_usb.h
@@ -28,16 +28,13 @@  enum {
 	VBUS_HIGH	= 1 << 0,
 };
 
-struct mv_usb_addon_irq {
-	unsigned int	irq;
-	int		(*poll)(void);
-};
-
+#define MV_USB_HAS_VBUS_DETECTION	(1 << 0)
+#define MV_USB_HAS_IDPIN_DETECTION	(1 << 1)
 struct mv_usb_platform_data {
 	unsigned int		clknum;
 	char			**clkname;
-	struct mv_usb_addon_irq	*id;	/* Only valid for OTG. ID pin change*/
-	struct mv_usb_addon_irq	*vbus;	/* valid for OTG/UDC. VBUS change*/
+
+	unsigned int		extern_attr;
 
 	/* only valid for HCD. OTG or Host only*/
 	unsigned int		mode;
@@ -45,9 +42,7 @@  struct mv_usb_platform_data {
 	/* This flag is used for that needs id pin checked by otg */
 	unsigned int    disable_otg_clock_gating:1;
 	/* Force a_bus_req to be asserted */
-	 unsigned int    otg_force_a_bus_req:1;
-
-	int	(*set_vbus)(unsigned int vbus);
+	unsigned int    otg_force_a_bus_req:1;
 };
 
 struct mv_usb_phy_platform_data {
diff --git a/include/linux/usb/mv_usb2.h b/include/linux/usb/mv_usb2.h
index 9744a97..e0246d9 100644
--- a/include/linux/usb/mv_usb2.h
+++ b/include/linux/usb/mv_usb2.h
@@ -17,6 +17,33 @@ 
 
 #include <linux/clk.h>
 
+enum {
+	EVENT_VBUS,
+	EVENT_ID,
+};
+
+struct pxa_usb_vbus_ops {
+	int (*get_vbus)(unsigned int *level);
+	int (*set_vbus)(unsigned int level);
+	int (*init)(void);
+};
+
+struct pxa_usb_idpin_ops {
+	int (*get_idpin)(unsigned int *level);
+	int (*init)(void);
+};
+
+struct pxa_usb_extern_ops {
+	struct pxa_usb_vbus_ops		vbus;
+	struct pxa_usb_idpin_ops	idpin;
+};
+
+struct mv_usb2_extern_chip {
+	unsigned int id;
+	struct pxa_usb_extern_ops ops;
+	struct atomic_notifier_head *head;
+};
+
 struct mv_usb2_phy {
 	struct platform_device	*pdev;
 	struct mutex		phy_lock;
@@ -26,6 +53,8 @@  struct mv_usb2_phy {
 	struct clk		**clks;
 	unsigned int		clks_num;
 
+	struct mv_usb2_extern_chip extern_chip;
+
 	int	(*init)(struct mv_usb2_phy *mv_phy);
 	void	(*shutdown)(struct mv_usb2_phy *mv_phy);
 };
@@ -34,10 +63,66 @@  struct mv_usb2_phy {
 
 extern struct mv_usb2_phy *mv_usb2_get_phy(void);
 
+#define mv_usb2_has_extern_call(phy, o, f, arg...)( \
+{ \
+	int ret;					\
+	ret = (!phy ? 0 : ((phy->extern_chip.ops.o.f) ?	\
+		1 : 0));				\
+	ret;						\
+} \
+)
+
+#define mv_usb2_extern_call(phy, o, f, args...)( \
+{ \
+	int ret;						\
+	ret = (!phy ? -ENODEV : ((phy->extern_chip.ops.o.f) ?	\
+		phy->extern_chip.ops.o.f(args) : -ENOIOCTLCMD));\
+	ret;							\
+} \
+)
+
+#define mv_usb2_set_extern_call(phy, o, f, p)( \
+{ \
+	int ret;							\
+	ret = !phy ? -ENODEV : ((phy->extern_chip.ops.o.f) ?		\
+		-EINVAL : ({phy->extern_chip.ops.o.f = p; 0; }));	\
+	ret;								\
+} \
+)
+
+extern int mv_usb2_register_notifier(struct mv_usb2_phy *phy,
+					struct notifier_block *nb);
+extern int mv_usb2_unregister_notifier(struct mv_usb2_phy *phy,
+					struct notifier_block *nb);
+extern int mv_usb2_notify(struct mv_usb2_phy *phy, unsigned long val, void *v);
+
 #else
 
 struct mv_usb2_phy *mv_usb2_get_phy(void) { return NULL; }
 
+#define mv_usb2_has_extern_call(phy, o, f, arg...)(0)
+
+#define mv_usb2_extern_call(phy, o, f, args...)(0)
+
+#define mv_usb2_set_extern_call(phy, o, f, p)(0)
+
+int mv_usb2_register_notifier(struct mv_usb2_phy *phy,
+					struct notifier_block *nb)
+{
+	return -EINVAL;
+}
+
+int mv_usb2_unregister_notifier(struct mv_usb2_phy *phy,
+					struct notifier_block *nb)
+{
+	return -EINVAL;
+}
+
+int mv_usb2_notify(struct mv_usb2_phy *phy, unsigned long val, void *v)
+{
+	return 0;
+}
+
 #endif
 
 #endif