Commit | Line | Data |
---|---|---|
10145f7c | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2d1611af AG |
2 | /* |
3 | * Adaptrum Anarion DWMAC glue layer | |
4 | * | |
5 | * Copyright (C) 2017, Adaptrum, Inc. | |
6 | * (Written by Alexandru Gagniuc <alex.g at adaptrum.com> for Adaptrum, Inc.) | |
2d1611af AG |
7 | */ |
8 | ||
9 | #include <linux/io.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/of_net.h> | |
12 | #include <linux/stmmac.h> | |
13 | ||
14 | #include "stmmac.h" | |
15 | #include "stmmac_platform.h" | |
16 | ||
17 | #define GMAC_RESET_CONTROL_REG 0 | |
18 | #define GMAC_SW_CONFIG_REG 4 | |
19 | #define GMAC_CONFIG_INTF_SEL_MASK (0x7 << 0) | |
20 | #define GMAC_CONFIG_INTF_RGMII (0x1 << 0) | |
21 | ||
22 | struct anarion_gmac { | |
23 | uintptr_t ctl_block; | |
24 | uint32_t phy_intf_sel; | |
25 | }; | |
26 | ||
27 | static uint32_t gmac_read_reg(struct anarion_gmac *gmac, uint8_t reg) | |
28 | { | |
29 | return readl((void *)(gmac->ctl_block + reg)); | |
30 | }; | |
31 | ||
32 | static void gmac_write_reg(struct anarion_gmac *gmac, uint8_t reg, uint32_t val) | |
33 | { | |
34 | writel(val, (void *)(gmac->ctl_block + reg)); | |
35 | } | |
36 | ||
37 | static int anarion_gmac_init(struct platform_device *pdev, void *priv) | |
38 | { | |
39 | uint32_t sw_config; | |
40 | struct anarion_gmac *gmac = priv; | |
41 | ||
42 | /* Reset logic, configure interface mode, then release reset. SIMPLE! */ | |
43 | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); | |
44 | ||
45 | sw_config = gmac_read_reg(gmac, GMAC_SW_CONFIG_REG); | |
46 | sw_config &= ~GMAC_CONFIG_INTF_SEL_MASK; | |
47 | sw_config |= (gmac->phy_intf_sel & GMAC_CONFIG_INTF_SEL_MASK); | |
48 | gmac_write_reg(gmac, GMAC_SW_CONFIG_REG, sw_config); | |
49 | ||
50 | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 0); | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
55 | static void anarion_gmac_exit(struct platform_device *pdev, void *priv) | |
56 | { | |
57 | struct anarion_gmac *gmac = priv; | |
58 | ||
59 | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); | |
60 | } | |
61 | ||
62 | static struct anarion_gmac *anarion_config_dt(struct platform_device *pdev) | |
63 | { | |
2d1611af | 64 | struct anarion_gmac *gmac; |
0c65b2b9 AL |
65 | phy_interface_t phy_mode; |
66 | void __iomem *ctl_block; | |
67 | int err; | |
2d1611af | 68 | |
ad124aa3 | 69 | ctl_block = devm_platform_ioremap_resource(pdev, 1); |
2d1611af AG |
70 | if (IS_ERR(ctl_block)) { |
71 | dev_err(&pdev->dev, "Cannot get reset region (%ld)!\n", | |
72 | PTR_ERR(ctl_block)); | |
73 | return ctl_block; | |
74 | } | |
75 | ||
76 | gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL); | |
77 | if (!gmac) | |
78 | return ERR_PTR(-ENOMEM); | |
79 | ||
80 | gmac->ctl_block = (uintptr_t)ctl_block; | |
81 | ||
0c65b2b9 AL |
82 | err = of_get_phy_mode(pdev->dev.of_node, &phy_mode); |
83 | if (err) | |
84 | return ERR_PTR(err); | |
85 | ||
2d1611af AG |
86 | switch (phy_mode) { |
87 | case PHY_INTERFACE_MODE_RGMII: /* Fall through */ | |
88 | case PHY_INTERFACE_MODE_RGMII_ID /* Fall through */: | |
89 | case PHY_INTERFACE_MODE_RGMII_RXID: /* Fall through */ | |
90 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
91 | gmac->phy_intf_sel = GMAC_CONFIG_INTF_RGMII; | |
92 | break; | |
93 | default: | |
94 | dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", | |
95 | phy_mode); | |
96 | return ERR_PTR(-ENOTSUPP); | |
97 | } | |
98 | ||
99 | return gmac; | |
100 | } | |
101 | ||
102 | static int anarion_dwmac_probe(struct platform_device *pdev) | |
103 | { | |
104 | int ret; | |
105 | struct anarion_gmac *gmac; | |
106 | struct plat_stmmacenet_data *plat_dat; | |
107 | struct stmmac_resources stmmac_res; | |
108 | ||
109 | ret = stmmac_get_platform_resources(pdev, &stmmac_res); | |
110 | if (ret) | |
111 | return ret; | |
112 | ||
113 | gmac = anarion_config_dt(pdev); | |
114 | if (IS_ERR(gmac)) | |
115 | return PTR_ERR(gmac); | |
116 | ||
117 | plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); | |
118 | if (IS_ERR(plat_dat)) | |
119 | return PTR_ERR(plat_dat); | |
120 | ||
121 | plat_dat->init = anarion_gmac_init; | |
122 | plat_dat->exit = anarion_gmac_exit; | |
123 | anarion_gmac_init(pdev, gmac); | |
124 | plat_dat->bsp_priv = gmac; | |
125 | ||
126 | ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); | |
127 | if (ret) { | |
128 | stmmac_remove_config_dt(pdev, plat_dat); | |
129 | return ret; | |
130 | } | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | static const struct of_device_id anarion_dwmac_match[] = { | |
136 | { .compatible = "adaptrum,anarion-gmac" }, | |
137 | { } | |
138 | }; | |
139 | MODULE_DEVICE_TABLE(of, anarion_dwmac_match); | |
140 | ||
141 | static struct platform_driver anarion_dwmac_driver = { | |
142 | .probe = anarion_dwmac_probe, | |
143 | .remove = stmmac_pltfr_remove, | |
144 | .driver = { | |
145 | .name = "anarion-dwmac", | |
146 | .pm = &stmmac_pltfr_pm_ops, | |
147 | .of_match_table = anarion_dwmac_match, | |
148 | }, | |
149 | }; | |
150 | module_platform_driver(anarion_dwmac_driver); | |
151 | ||
152 | MODULE_DESCRIPTION("Adaptrum Anarion DWMAC specific glue layer"); | |
153 | MODULE_AUTHOR("Alexandru Gagniuc <mr.nuke.me@gmail.com>"); | |
154 | MODULE_LICENSE("GPL v2"); |