The usb_add_hcd and usb_remove_hcd can't be called together, otherwise, there is oops like below. To fix it, we add mutex to avoid calling role_start and role_stop at the same time. [3.336786] Can't support > 32 bit dma. [3.340676] xhci-cdns3: xHCI Host Controller [3.345052] xhci-cdns3: new USB bus registered, assigned bus number 1 [3.363462] xhci-cdns3: hcc params 0x200073c8 hci version 0x100 quirks 0x60010010 [3.374555] hub 1-0:1.0: USB hub found [3.378349] hub 1-0:1.0: 1 port detected [3.382573] xhci-cdns3: xHCI Host Controller [3.386948] xhci-cdns3: new USB bus registered, assigned bus number 2 [3.387707] cdns-usb3 5b110000.cdns3: current role is 0, switch to 1 [3.387711] xhci-cdns3: remove, state 0 [3.387721] Unable to handle kernel NULL pointer dereference at virtual address 000000d8 [3.387723] pgd = ffff0000095f5000 [3.387731] [000000d8] *pgd=00000008bfffe003, *pud=00000008bfffd003, *pmd=0000000000000000 [3.387735] Internal error: Oops: 96000004 [#1] PREEMPT SMP [3.387739] Modules linked in: [3.387746] CPU: 0 PID: 745 Comm: kworker/0:1 Not tainted 4.9.88-05224-geb65e0981da1 #2087 [3.387748] Hardware name: Freescale i.MX8QXP MEK (DT) [3.387765] Workqueue: events_freezable cdns3_role_switch [3.387768] task: ffff80083a1d7080 task.stack: ffff80083aa5c000 [3.387775] PC is at sysfs_remove_group+0x18/0x98 [3.387781] LR is at usb_remove_hcd+0x58/0x200 [3.387784] pc : [<ffff000008265688>] lr : [<ffff00000885ac28>] pstate: 80000145 [3.387786] sp : ffff80083aa5fc70 [3.387791] x29: ffff80083aa5fc70 x28: 0000000000000000 [3.387795] x27: 0000000000000000 x26: ffff0000093cb2a8 [3.387800] x25: ffff000009506191 x24: ffff80083ff37280 [3.387804] x23: ffff80083b1b0138 x22: ffff80083b238000 [3.387809] x21: ffff80083b238000 x20: ffff0000094a1610 [3.387813] x19: ffff80083b1b0000 x18: 0000000000000020 [3.387817] x17: 0000000000000001 x16: 0000000000000019 [3.387822] x15: ffffffffffffffff x14: 00000000fffffff0 [3.387827] x13: ffff0000095a2068 x12: ffff0000093ce600 [3.387831] x11: ffff0000093ce000 x10: ffff00000959f000 [3.387836] x9 : 0000000000000000 x8 : ffff80083fe4244c [3.387840] x7 : 0000000000000000 x6 : 0000000007cb5702 [3.387844] x5 : 00ffffffffffffff x4 : 0000000000000000 [3.387848] x3 : 0000000000000140 x2 : 00000000000008a6 [3.387852] x1 : 0000000000000000 x0 : 00000000000000a8 [3.387853] [3.387856] Process kworker/0:1 (pid: 745, stack limit = 0xffff80083aa5c020) [3.387860] Stack: (0xffff80083aa5fc70 to 0xffff80083aa60000) [3.387865] fc60: ffff80083aa5fca0 ffff00000885ac28 [3.387870] fc80: ffff80083b1b0000 ffff0000094a1000 ffff80083b238000 0000000000000000 [3.387875] fca0: ffff80083aa5fcf0 ffff00000889c5c8 ffff80083b1bf400 ffff80083b23e018 [3.387880] fcc0: ffff80083b238000 ffff80083b238000 ffff80083ff3bb00 ffff80083a1d7080 [3.387885] fce0: ffff000008ccc25c 0000000000000000 ffff80083aa5fd20 ffff000008897130 [3.387890] fd00: ffff80083b23e018 0000000000000001 0000000000000000 0000000000000000 [3.387895] fd20: ffff80083aa5fd50 ffff0000088972ec ffff80083b23e0d0 0000000000000000 [3.387901] fd40: ffff80083b23e018 ffff000008ccc25c ffff80083aa5fd80 ffff0000080dbc38 [3.387906] fd60: 0000000000000000 ffff80083ac3a600 ffff80083b23e0d0 ffff0000093b6000 [3.387911] fd80: ffff80083aa5fdc0 ffff0000080dbe40 ffff80083ac3a600 ffff80083ff37280 [3.387916] fda0: ffff80083ff37280 ffff80083ac3a630 ffff80083ff372a0 ffff0000093b6000 [3.387921] fdc0: ffff80083aa5fe20 ffff0000080e1c78 ffff80083a0fee80 ffff80083aa5c000 [3.387926] fde0: ffff00000908e2c8 ffff80083ac3a600 ffff0000080dbdf0 0000000000000000 [3.387931] fe00: 0000000000000000 0000000000000000 0000000000000000 ffff80083ac3a600 [3.387936] fe20: 0000000000000000 ffff000008083820 ffff0000080e1b98 ffff80083a0fee80 [3.387941] fe40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387946] fe60: ffff80083aa5fea0 0000000000000000 ffff0000080e1b98 ffff80083ac3a600 [3.387951] fe80: 0000000000000000 0000000000000000 ffff80083aa5fe90 ffff80083aa5fe90 [3.387956] fea0: 0000000000000000 ffff000000000000 ffff80083aa5feb0 ffff80083aa5feb0 [3.387960] fec0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387965] fee0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387970] ff00: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387974] ff20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387979] ff40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387983] ff60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387988] ff80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387992] ffa0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [3.387997] ffc0: 0000000000000000 0000000000000005 0000000000000000 0000000000000000 [3.388002] ffe0: 0000000000000000 0000000000000000 fefffac7ef9fea6e f6ef6c26eeee7272 [3.388004] Call trace: [3.388008] Exception stack(0xffff80083aa5faa0 to 0xffff80083aa5fbd0) [3.388013] faa0: ffff80083b1b0000 0000ffffffffffff ffff80083aa5fc70 ffff000008265688 [3.388019] fac0: ffff80083b238000 ffff80083b238000 ffff80083b1b0138 ffff80083ff37280 [3.388024] fae0: ffff000009506191 ffff0000093cb2a8 0000000000000000 ffff80083aa5fcb0 [3.388029] fb00: ffff80083aa5fc20 ffff80083aa5fc20 ffff80083aa5fbf0 00000000ffffffd8 [3.388034] fb20: ffff000009506191 ffff0000093cb2a8 0000000000000000 ffff80083a11f408 [3.388038] fb40: 00000000000000a8 0000000000000000 00000000000008a6 0000000000000140 [3.388043] fb60: 0000000000000000 00ffffffffffffff 0000000007cb5702 0000000000000000 [3.388048] fb80: ffff80083fe4244c 0000000000000000 ffff00000959f000 ffff0000093ce000 [3.388054] fba0: ffff0000093ce600 ffff0000095a2068 00000000fffffff0 ffffffffffffffff [3.388057] fbc0: 0000000000000019 0000000000000001 [3.388061] [<ffff000008265688>] sysfs_remove_group+0x18/0x98 [3.388066] [<ffff00000885ac28>] usb_remove_hcd+0x58/0x200 [3.388072] [<ffff00000889c5c8>] cdns3_host_stop+0x38/0xa0 [3.388077] [<ffff000008897130>] cdns3_do_role_switch+0x58/0x188 [3.388082] [<ffff0000088972ec>] cdns3_role_switch+0x8c/0xa0 [3.388089] [<ffff0000080dbc38>] process_one_work+0x1c8/0x380 [3.388094] [<ffff0000080dbe40>] worker_thread+0x50/0x4c0 [3.388099] [<ffff0000080e1c78>] kthread+0xe0/0xf8 [3.388104] [<ffff000008083820>] ret_from_fork+0x10/0x30 Signed-off-by: Peter Chen <peter.chen@nxp.com>
132 lines
3.7 KiB
C
132 lines
3.7 KiB
C
/**
|
|
* core.h - Cadence USB3 DRD Controller Core header file
|
|
*
|
|
* Copyright 2017 NXP
|
|
*
|
|
* Authors: 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. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef __DRIVERS_USB_CDNS3_CORE_H
|
|
#define __DRIVERS_USB_CDNS3_CORE_H
|
|
|
|
struct cdns3;
|
|
enum cdns3_roles {
|
|
CDNS3_ROLE_HOST = 0,
|
|
CDNS3_ROLE_GADGET,
|
|
CDNS3_ROLE_END,
|
|
};
|
|
|
|
/**
|
|
* struct cdns3_role_driver - host/gadget role driver
|
|
* @start: start this role
|
|
* @stop: stop this role
|
|
* @suspend: suspend callback for this role
|
|
* @resume: resume callback for this role
|
|
* @irq: irq handler for this role
|
|
* @name: role name string (host/gadget)
|
|
*/
|
|
struct cdns3_role_driver {
|
|
int (*start)(struct cdns3 *);
|
|
void (*stop)(struct cdns3 *);
|
|
int (*suspend)(struct cdns3 *, bool do_wakeup);
|
|
int (*resume)(struct cdns3 *, bool hibernated);
|
|
irqreturn_t (*irq)(struct cdns3 *);
|
|
const char *name;
|
|
};
|
|
|
|
#define CDNS3_NUM_OF_CLKS 5
|
|
/**
|
|
* struct cdns3 - Representation of Cadence USB3 DRD controller.
|
|
* @dev: pointer to Cadence device struct
|
|
* @xhci_regs: pointer to base of xhci registers
|
|
* @xhci_res: the resource for xhci
|
|
* @dev_regs: pointer to base of dev registers
|
|
* @none_core_regs: pointer to base of nxp wrapper registers
|
|
* @phy_regs: pointer to base of phy registers
|
|
* @otg_regs: pointer to base of otg registers
|
|
* @irq: irq number for controller
|
|
* @roles: array of supported roles for this controller
|
|
* @role: current role
|
|
* @host_dev: the child host device pointer for cdns3 core
|
|
* @gadget_dev: the child gadget device pointer for cdns3 core
|
|
* @usbphy: usbphy for this controller
|
|
* @cdns3_clks: Clock pointer array for cdns3 core
|
|
* @extcon: Type-C extern connector
|
|
* @extcon_nb: notifier block for Type-C extern connector
|
|
* @role_switch_wq: work queue item for role switch
|
|
* @in_lpm: the controller in low power mode
|
|
* @wakeup_int: the wakeup interrupt
|
|
* @mutex: the mutex for concurrent code at driver
|
|
*/
|
|
struct cdns3 {
|
|
struct device *dev;
|
|
void __iomem *xhci_regs;
|
|
struct resource *xhci_res;
|
|
struct usbss_dev_register_block_type __iomem *dev_regs;
|
|
void __iomem *none_core_regs;
|
|
void __iomem *phy_regs;
|
|
void __iomem *otg_regs;
|
|
int irq;
|
|
struct cdns3_role_driver *roles[CDNS3_ROLE_END];
|
|
enum cdns3_roles role;
|
|
struct device *host_dev;
|
|
struct device *gadget_dev;
|
|
struct usb_phy *usbphy;
|
|
struct clk *cdns3_clks[CDNS3_NUM_OF_CLKS];
|
|
struct extcon_dev *extcon;
|
|
struct notifier_block extcon_nb;
|
|
struct work_struct role_switch_wq;
|
|
bool in_lpm;
|
|
bool wakeup_int;
|
|
struct mutex mutex;
|
|
};
|
|
|
|
static inline struct cdns3_role_driver *cdns3_role(struct cdns3 *cdns)
|
|
{
|
|
WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
|
|
return cdns->roles[cdns->role];
|
|
}
|
|
|
|
static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
|
|
{
|
|
int ret;
|
|
if (role >= CDNS3_ROLE_END)
|
|
return 0;
|
|
|
|
if (!cdns->roles[role])
|
|
return -ENXIO;
|
|
|
|
mutex_lock(&cdns->mutex);
|
|
cdns->role = role;
|
|
ret = cdns->roles[role]->start(cdns);
|
|
mutex_unlock(&cdns->mutex);
|
|
return ret;
|
|
}
|
|
|
|
static inline void cdns3_role_stop(struct cdns3 *cdns)
|
|
{
|
|
enum cdns3_roles role = cdns->role;
|
|
|
|
if (role == CDNS3_ROLE_END)
|
|
return;
|
|
|
|
mutex_lock(&cdns->mutex);
|
|
cdns->roles[role]->stop(cdns);
|
|
cdns->role = CDNS3_ROLE_END;
|
|
mutex_unlock(&cdns->mutex);
|
|
}
|
|
|
|
#endif /* __DRIVERS_USB_CDNS3_CORE_H */
|