Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
c28bdea
Documentation
LICENSES
arch
block
certs
crypto
drivers
accel
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
comedi
connector
counter
cpufreq
cpuidle
crypto
cxl
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hte
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
parisc
parport
pci
pcmcia
peci
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
ufs
uio
usb
atm
c67x00
cdns3
chipidea
class
common
core
dwc2
dwc3
Kconfig
Makefile
core.c
core.h
debug.h
debugfs.c
drd.c
dwc3-am62.c
dwc3-exynos.c
dwc3-haps.c
dwc3-imx8mp.c
dwc3-keystone.c
dwc3-meson-g12a.c
dwc3-of-simple.c
dwc3-omap.c
dwc3-pci.c
dwc3-qcom.c
dwc3-st.c
dwc3-xilinx.c
ep0.c
gadget.c
gadget.h
host.c
io.h
trace.c
trace.h
ulpi.c
early
fotg210
gadget
host
image
isp1760
misc
mon
mtu3
musb
phy
renesas_usbhs
roles
serial
storage
typec
usbip
Kconfig
Makefile
usb-skeleton.c
vdpa
vfio
vhost
video
virt
virtio
vlynq
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
usb
/
dwc3
/
dwc3-xilinx.c
Copy path
Blame
Blame
Latest commit
History
History
405 lines (327 loc) · 9.75 KB
Breadcrumbs
linux
/
drivers
/
usb
/
dwc3
/
dwc3-xilinx.c
Top
File metadata and controls
Code
Blame
405 lines (327 loc) · 9.75 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver * * Authors: Manish Narani <manish.narani@xilinx.com> * Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com> */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/gpio/consumer.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/of_address.h> #include <linux/delay.h> #include <linux/firmware/xlnx-zynqmp.h> #include <linux/io.h> #include <linux/phy/phy.h> /* USB phy reset mask register */ #define XLNX_USB_PHY_RST_EN 0x001C #define XLNX_PHY_RST_MASK 0x1 /* Xilinx USB 3.0 IP Register */ #define XLNX_USB_TRAFFIC_ROUTE_CONFIG 0x005C #define XLNX_USB_TRAFFIC_ROUTE_FPD 0x1 /* Versal USB Reset ID */ #define VERSAL_USB_RESET_ID 0xC104036 #define XLNX_USB_FPD_PIPE_CLK 0x7c #define PIPE_CLK_DESELECT 1 #define PIPE_CLK_SELECT 0 #define XLNX_USB_FPD_POWER_PRSNT 0x80 #define FPD_POWER_PRSNT_OPTION BIT(0) struct dwc3_xlnx { int num_clocks; struct clk_bulk_data *clks; struct device *dev; void __iomem *regs; int (*pltfm_init)(struct dwc3_xlnx *data); struct phy *usb3_phy; }; static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask) { u32 reg; /* * Enable or disable ULPI PHY reset from USB Controller. * This does not actually reset the phy, but just controls * whether USB controller can or cannot reset ULPI PHY. */ reg = readl(priv_data->regs + XLNX_USB_PHY_RST_EN); if (mask) reg &= ~XLNX_PHY_RST_MASK; else reg |= XLNX_PHY_RST_MASK; writel(reg, priv_data->regs + XLNX_USB_PHY_RST_EN); } static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) { struct device *dev = priv_data->dev; int ret; dwc3_xlnx_mask_phy_rst(priv_data, false); /* Assert and De-assert reset */ ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID, PM_RESET_ACTION_ASSERT); if (ret < 0) { dev_err_probe(dev, ret, "failed to assert Reset\n"); return ret; } ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID, PM_RESET_ACTION_RELEASE); if (ret < 0) { dev_err_probe(dev, ret, "failed to De-assert Reset\n"); return ret; } dwc3_xlnx_mask_phy_rst(priv_data, true); return 0; } static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) { struct device *dev = priv_data->dev; struct reset_control *crst, *hibrst, *apbrst; struct gpio_desc *reset_gpio; int ret = 0; u32 reg; priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy"); if (IS_ERR(priv_data->usb3_phy)) { ret = PTR_ERR(priv_data->usb3_phy); dev_err_probe(dev, ret, "failed to get USB3 PHY\n"); goto err; } /* * The following core resets are not required unless a USB3 PHY * is used, and the subsequent register settings are not required * unless a core reset is performed (they should be set properly * by the first-stage boot loader, but may be reverted by a core * reset). They may also break the configuration if USB3 is actually * in use but the usb3-phy entry is missing from the device tree. * Therefore, skip these operations in this case. */ if (!priv_data->usb3_phy) goto skip_usb3_phy; crst = devm_reset_control_get_exclusive(dev, "usb_crst"); if (IS_ERR(crst)) { ret = PTR_ERR(crst); dev_err_probe(dev, ret, "failed to get core reset signal\n"); goto err; } hibrst = devm_reset_control_get_exclusive(dev, "usb_hibrst"); if (IS_ERR(hibrst)) { ret = PTR_ERR(hibrst); dev_err_probe(dev, ret, "failed to get hibernation reset signal\n"); goto err; } apbrst = devm_reset_control_get_exclusive(dev, "usb_apbrst"); if (IS_ERR(apbrst)) { ret = PTR_ERR(apbrst); dev_err_probe(dev, ret, "failed to get APB reset signal\n"); goto err; } ret = reset_control_assert(crst); if (ret < 0) { dev_err(dev, "Failed to assert core reset\n"); goto err; } ret = reset_control_assert(hibrst); if (ret < 0) { dev_err(dev, "Failed to assert hibernation reset\n"); goto err; } ret = reset_control_assert(apbrst); if (ret < 0) { dev_err(dev, "Failed to assert APB reset\n"); goto err; } ret = phy_init(priv_data->usb3_phy); if (ret < 0) { phy_exit(priv_data->usb3_phy); goto err; } ret = reset_control_deassert(apbrst); if (ret < 0) { dev_err(dev, "Failed to release APB reset\n"); goto err; } /* Set PIPE Power Present signal in FPD Power Present Register*/ writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); ret = reset_control_deassert(crst); if (ret < 0) { dev_err(dev, "Failed to release core reset\n"); goto err; } ret = reset_control_deassert(hibrst); if (ret < 0) { dev_err(dev, "Failed to release hibernation reset\n"); goto err; } ret = phy_power_on(priv_data->usb3_phy); if (ret < 0) { phy_exit(priv_data->usb3_phy); goto err; } skip_usb3_phy: /* ulpi reset via gpio-modepin or gpio-framework driver */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) { return dev_err_probe(dev, PTR_ERR(reset_gpio), "Failed to request reset GPIO\n"); } if (reset_gpio) { /* Toggle ulpi to reset the phy. */ gpiod_set_value_cansleep(reset_gpio, 1); usleep_range(5000, 10000); gpiod_set_value_cansleep(reset_gpio, 0); usleep_range(5000, 10000); } /* * This routes the USB DMA traffic to go through FPD path instead * of reaching DDR directly. This traffic routing is needed to * make SMMU and CCI work with USB DMA. */ if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) { reg = readl(priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); reg |= XLNX_USB_TRAFFIC_ROUTE_FPD; writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); } err: return ret; } static const struct of_device_id dwc3_xlnx_of_match[] = { { .compatible = "xlnx,zynqmp-dwc3", .data = &dwc3_xlnx_init_zynqmp, }, { .compatible = "xlnx,versal-dwc3", .data = &dwc3_xlnx_init_versal, }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match); static int dwc3_xlnx_probe(struct platform_device *pdev) { struct dwc3_xlnx *priv_data; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; const struct of_device_id *match; void __iomem *regs; int ret; priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); if (!priv_data) return -ENOMEM; regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { ret = PTR_ERR(regs); dev_err_probe(dev, ret, "failed to map registers\n"); return ret; } match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node); priv_data->pltfm_init = match->data; priv_data->regs = regs; priv_data->dev = dev; platform_set_drvdata(pdev, priv_data); ret = devm_clk_bulk_get_all(priv_data->dev, &priv_data->clks); if (ret < 0) return ret; priv_data->num_clocks = ret; ret = clk_bulk_prepare_enable(priv_data->num_clocks, priv_data->clks); if (ret) return ret; ret = priv_data->pltfm_init(priv_data); if (ret) goto err_clk_put; ret = of_platform_populate(np, NULL, NULL, dev); if (ret) goto err_clk_put; pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_suspend_ignore_children(dev, false); pm_runtime_get_sync(dev); return 0; err_clk_put: clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); return ret; } static int dwc3_xlnx_remove(struct platform_device *pdev) { struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; of_platform_depopulate(dev); clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); priv_data->num_clocks = 0; pm_runtime_disable(dev); pm_runtime_put_noidle(dev); pm_runtime_set_suspended(dev); return 0; } static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev) { struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); clk_bulk_disable(priv_data->num_clocks, priv_data->clks); return 0; } static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev) { struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); return clk_bulk_enable(priv_data->num_clocks, priv_data->clks); } static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) { pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; } static int __maybe_unused dwc3_xlnx_suspend(struct device *dev) { struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); phy_exit(priv_data->usb3_phy); /* Disable the clocks */ clk_bulk_disable(priv_data->num_clocks, priv_data->clks); return 0; } static int __maybe_unused dwc3_xlnx_resume(struct device *dev) { struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); int ret; ret = clk_bulk_enable(priv_data->num_clocks, priv_data->clks); if (ret) return ret; ret = phy_init(priv_data->usb3_phy); if (ret < 0) return ret; ret = phy_power_on(priv_data->usb3_phy); if (ret < 0) { phy_exit(priv_data->usb3_phy); return ret; } return 0; } static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume) SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend, dwc3_xlnx_runtime_resume, dwc3_xlnx_runtime_idle) }; static struct platform_driver dwc3_xlnx_driver = { .probe = dwc3_xlnx_probe, .remove = dwc3_xlnx_remove, .driver = { .name = "dwc3-xilinx", .of_match_table = dwc3_xlnx_of_match, .pm = &dwc3_xlnx_dev_pm_ops, }, }; module_platform_driver(dwc3_xlnx_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Xilinx DWC3 controller specific glue driver"); MODULE_AUTHOR("Manish Narani <manish.narani@xilinx.com>"); MODULE_AUTHOR("Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
You can’t perform that action at this time.