1 | // SPDX-License-Identifier: GPL-2.0-or-later |
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.) |
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 | void __iomem *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(addr: 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, addr: 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, val: 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, val: sw_config); |
49 | |
50 | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, val: 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, val: 1); |
60 | } |
61 | |
62 | static struct anarion_gmac *anarion_config_dt(struct platform_device *pdev) |
63 | { |
64 | struct anarion_gmac *gmac; |
65 | phy_interface_t phy_mode; |
66 | void __iomem *ctl_block; |
67 | int err; |
68 | |
69 | ctl_block = devm_platform_ioremap_resource(pdev, index: 1); |
70 | if (IS_ERR(ptr: ctl_block)) { |
71 | err = PTR_ERR(ptr: ctl_block); |
72 | dev_err(&pdev->dev, "Cannot get reset region (%d)!\n" , err); |
73 | return ERR_PTR(error: err); |
74 | } |
75 | |
76 | gmac = devm_kzalloc(dev: &pdev->dev, size: sizeof(*gmac), GFP_KERNEL); |
77 | if (!gmac) |
78 | return ERR_PTR(error: -ENOMEM); |
79 | |
80 | gmac->ctl_block = ctl_block; |
81 | |
82 | err = of_get_phy_mode(np: pdev->dev.of_node, interface: &phy_mode); |
83 | if (err) |
84 | return ERR_PTR(error: err); |
85 | |
86 | switch (phy_mode) { |
87 | case PHY_INTERFACE_MODE_RGMII: |
88 | fallthrough; |
89 | case PHY_INTERFACE_MODE_RGMII_ID: |
90 | case PHY_INTERFACE_MODE_RGMII_RXID: |
91 | case PHY_INTERFACE_MODE_RGMII_TXID: |
92 | gmac->phy_intf_sel = GMAC_CONFIG_INTF_RGMII; |
93 | break; |
94 | default: |
95 | dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n" , |
96 | phy_mode); |
97 | return ERR_PTR(error: -ENOTSUPP); |
98 | } |
99 | |
100 | return gmac; |
101 | } |
102 | |
103 | static int anarion_dwmac_probe(struct platform_device *pdev) |
104 | { |
105 | int ret; |
106 | struct anarion_gmac *gmac; |
107 | struct plat_stmmacenet_data *plat_dat; |
108 | struct stmmac_resources stmmac_res; |
109 | |
110 | ret = stmmac_get_platform_resources(pdev, stmmac_res: &stmmac_res); |
111 | if (ret) |
112 | return ret; |
113 | |
114 | gmac = anarion_config_dt(pdev); |
115 | if (IS_ERR(ptr: gmac)) |
116 | return PTR_ERR(ptr: gmac); |
117 | |
118 | plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac); |
119 | if (IS_ERR(ptr: plat_dat)) |
120 | return PTR_ERR(ptr: plat_dat); |
121 | |
122 | plat_dat->init = anarion_gmac_init; |
123 | plat_dat->exit = anarion_gmac_exit; |
124 | anarion_gmac_init(pdev, priv: gmac); |
125 | plat_dat->bsp_priv = gmac; |
126 | |
127 | return stmmac_dvr_probe(device: &pdev->dev, plat_dat, res: &stmmac_res); |
128 | } |
129 | |
130 | static const struct of_device_id anarion_dwmac_match[] = { |
131 | { .compatible = "adaptrum,anarion-gmac" }, |
132 | { } |
133 | }; |
134 | MODULE_DEVICE_TABLE(of, anarion_dwmac_match); |
135 | |
136 | static struct platform_driver anarion_dwmac_driver = { |
137 | .probe = anarion_dwmac_probe, |
138 | .remove_new = stmmac_pltfr_remove, |
139 | .driver = { |
140 | .name = "anarion-dwmac" , |
141 | .pm = &stmmac_pltfr_pm_ops, |
142 | .of_match_table = anarion_dwmac_match, |
143 | }, |
144 | }; |
145 | module_platform_driver(anarion_dwmac_driver); |
146 | |
147 | MODULE_DESCRIPTION("Adaptrum Anarion DWMAC specific glue layer" ); |
148 | MODULE_AUTHOR("Alexandru Gagniuc <mr.nuke.me@gmail.com>" ); |
149 | MODULE_LICENSE("GPL v2" ); |
150 | |