2 * otg.c - ChipIdea USB IP core OTG driver
4 * Copyright (C) 2013 Freescale Semiconductor, Inc.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
14 * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP
18 #include <linux/usb/otg.h>
19 #include <linux/usb/gadget.h>
20 #include <linux/usb/chipidea.h>
28 * hw_read_otgsc returns otgsc register bits value.
29 * @mask: bitfield mask
31 u32
hw_read_otgsc(struct ci_hdrc
*ci
, u32 mask
)
33 struct ci_hdrc_cable
*cable
;
34 u32 val
= hw_read(ci
, OP_OTGSC
, mask
);
37 * If using extcon framework for VBUS and/or ID signal
38 * detection overwrite OTGSC register value
40 cable
= &ci
->platdata
->vbus_extcon
;
41 if (!IS_ERR(cable
->edev
)) {
47 cable
->changed
= false;
55 cable
= &ci
->platdata
->id_extcon
;
56 if (!IS_ERR(cable
->edev
)) {
62 cable
->changed
= false;
74 * hw_write_otgsc updates target bits of OTGSC register.
75 * @mask: bitfield mask
76 * @data: to be written
78 void hw_write_otgsc(struct ci_hdrc
*ci
, u32 mask
, u32 data
)
80 hw_write(ci
, OP_OTGSC
, mask
| OTGSC_INT_STATUS_BITS
, data
);
84 * ci_otg_role - pick role based on ID pin state
87 enum ci_role
ci_otg_role(struct ci_hdrc
*ci
)
89 enum ci_role role
= hw_read_otgsc(ci
, OTGSC_ID
)
96 void ci_handle_vbus_change(struct ci_hdrc
*ci
)
101 if (hw_read_otgsc(ci
, OTGSC_BSV
))
102 usb_gadget_vbus_connect(&ci
->gadget
);
104 usb_gadget_vbus_disconnect(&ci
->gadget
);
107 #define CI_VBUS_STABLE_TIMEOUT_MS 5000
108 static void ci_handle_id_switch(struct ci_hdrc
*ci
)
110 enum ci_role role
= ci_otg_role(ci
);
112 if (role
!= ci
->role
) {
113 dev_dbg(ci
->dev
, "switching from %s to %s\n",
114 ci_role(ci
)->name
, ci
->roles
[role
]->name
);
118 if (role
== CI_ROLE_GADGET
)
119 /* wait vbus lower than OTGSC_BSV */
120 hw_wait_reg(ci
, OP_OTGSC
, OTGSC_BSV
, 0,
121 CI_VBUS_STABLE_TIMEOUT_MS
);
123 ci_role_start(ci
, role
);
127 * ci_otg_work - perform otg (vbus/id) event handle
130 static void ci_otg_work(struct work_struct
*work
)
132 struct ci_hdrc
*ci
= container_of(work
, struct ci_hdrc
, work
);
134 if (ci_otg_is_fsm_mode(ci
) && !ci_otg_fsm_work(ci
)) {
139 pm_runtime_get_sync(ci
->dev
);
141 ci
->id_event
= false;
142 ci_handle_id_switch(ci
);
143 } else if (ci
->b_sess_valid_event
) {
144 ci
->b_sess_valid_event
= false;
145 ci_handle_vbus_change(ci
);
147 dev_err(ci
->dev
, "unexpected event occurs at %s\n", __func__
);
148 pm_runtime_put_sync(ci
->dev
);
155 * ci_hdrc_otg_init - initialize otg struct
158 int ci_hdrc_otg_init(struct ci_hdrc
*ci
)
160 INIT_WORK(&ci
->work
, ci_otg_work
);
161 ci
->wq
= create_singlethread_workqueue("ci_otg");
163 dev_err(ci
->dev
, "can't create workqueue\n");
167 if (ci_otg_is_fsm_mode(ci
))
168 return ci_hdrc_otg_fsm_init(ci
);
174 * ci_hdrc_otg_destroy - destroy otg struct
177 void ci_hdrc_otg_destroy(struct ci_hdrc
*ci
)
180 flush_workqueue(ci
->wq
);
181 destroy_workqueue(ci
->wq
);
183 /* Disable all OTG irq and clear status */
184 hw_write_otgsc(ci
, OTGSC_INT_EN_BITS
| OTGSC_INT_STATUS_BITS
,
185 OTGSC_INT_STATUS_BITS
);
186 if (ci_otg_is_fsm_mode(ci
))
187 ci_hdrc_otg_fsm_remove(ci
);