1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2023 Nuvoton Technology corporation. |
3 | |
4 | #include <linux/module.h> |
5 | #include <linux/platform_device.h> |
6 | #include <linux/pm_runtime.h> |
7 | #include <linux/usb/chipidea.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/io.h> |
10 | #include <linux/reset-controller.h> |
11 | #include <linux/of.h> |
12 | |
13 | #include "ci.h" |
14 | |
15 | struct npcm_udc_data { |
16 | struct platform_device *ci; |
17 | struct clk *core_clk; |
18 | struct ci_hdrc_platform_data pdata; |
19 | }; |
20 | |
21 | static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event) |
22 | { |
23 | struct device *dev = ci->dev->parent; |
24 | |
25 | switch (event) { |
26 | case CI_HDRC_CONTROLLER_RESET_EVENT: |
27 | /* clear all mode bits */ |
28 | hw_write(ci, reg: OP_USBMODE, mask: 0xffffffff, data: 0x0); |
29 | break; |
30 | default: |
31 | dev_dbg(dev, "unknown ci_hdrc event (%d)\n" ,event); |
32 | break; |
33 | } |
34 | |
35 | return 0; |
36 | } |
37 | |
38 | static int npcm_udc_probe(struct platform_device *pdev) |
39 | { |
40 | int ret; |
41 | struct npcm_udc_data *ci; |
42 | struct platform_device *plat_ci; |
43 | struct device *dev = &pdev->dev; |
44 | |
45 | ci = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ci), GFP_KERNEL); |
46 | if (!ci) |
47 | return -ENOMEM; |
48 | platform_set_drvdata(pdev, data: ci); |
49 | |
50 | ci->core_clk = devm_clk_get_optional(dev, NULL); |
51 | if (IS_ERR(ptr: ci->core_clk)) |
52 | return PTR_ERR(ptr: ci->core_clk); |
53 | |
54 | ret = clk_prepare_enable(clk: ci->core_clk); |
55 | if (ret) |
56 | return dev_err_probe(dev, err: ret, fmt: "failed to enable the clock: %d\n" , ret); |
57 | |
58 | ci->pdata.name = dev_name(dev); |
59 | ci->pdata.capoffset = DEF_CAPOFFSET; |
60 | ci->pdata.flags = CI_HDRC_REQUIRES_ALIGNED_DMA | |
61 | CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS; |
62 | ci->pdata.phy_mode = USBPHY_INTERFACE_MODE_UTMI; |
63 | ci->pdata.notify_event = npcm_udc_notify_event; |
64 | |
65 | plat_ci = ci_hdrc_add_device(dev, res: pdev->resource, nres: pdev->num_resources, |
66 | platdata: &ci->pdata); |
67 | if (IS_ERR(ptr: plat_ci)) { |
68 | ret = PTR_ERR(ptr: plat_ci); |
69 | dev_err(dev, "failed to register HDRC NPCM device: %d\n" , ret); |
70 | goto clk_err; |
71 | } |
72 | |
73 | pm_runtime_no_callbacks(dev); |
74 | pm_runtime_enable(dev); |
75 | |
76 | return 0; |
77 | |
78 | clk_err: |
79 | clk_disable_unprepare(clk: ci->core_clk); |
80 | return ret; |
81 | } |
82 | |
83 | static int npcm_udc_remove(struct platform_device *pdev) |
84 | { |
85 | struct npcm_udc_data *ci = platform_get_drvdata(pdev); |
86 | |
87 | pm_runtime_disable(dev: &pdev->dev); |
88 | ci_hdrc_remove_device(pdev: ci->ci); |
89 | clk_disable_unprepare(clk: ci->core_clk); |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | static const struct of_device_id npcm_udc_dt_match[] = { |
95 | { .compatible = "nuvoton,npcm750-udc" , }, |
96 | { .compatible = "nuvoton,npcm845-udc" , }, |
97 | { } |
98 | }; |
99 | MODULE_DEVICE_TABLE(of, npcm_udc_dt_match); |
100 | |
101 | static struct platform_driver npcm_udc_driver = { |
102 | .probe = npcm_udc_probe, |
103 | .remove = npcm_udc_remove, |
104 | .driver = { |
105 | .name = "npcm_udc" , |
106 | .of_match_table = npcm_udc_dt_match, |
107 | }, |
108 | }; |
109 | |
110 | module_platform_driver(npcm_udc_driver); |
111 | |
112 | MODULE_DESCRIPTION("NPCM USB device controller driver" ); |
113 | MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>" ); |
114 | MODULE_LICENSE("GPL v2" ); |
115 | |