Commit | Line | Data |
---|---|---|
5892cd13 M |
1 | /* Texas Instruments Ethernet Switch Driver |
2 | * | |
3 | * Copyright (C) 2013 Texas Instruments | |
4 | * | |
b3c8ec35 PG |
5 | * Module Author: Mugunthan V N <mugunthanvnm@ti.com> |
6 | * | |
5892cd13 M |
7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License | |
9 | * version 2 as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
12 | * kind, whether express or implied; without even the implied warranty | |
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/platform_device.h> | |
b3c8ec35 | 18 | #include <linux/init.h> |
5892cd13 M |
19 | #include <linux/netdevice.h> |
20 | #include <linux/phy.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/of_device.h> | |
23 | ||
24 | #include "cpsw.h" | |
25 | ||
26 | /* AM33xx SoC specific definitions for the CONTROL port */ | |
27 | #define AM33XX_GMII_SEL_MODE_MII 0 | |
28 | #define AM33XX_GMII_SEL_MODE_RMII 1 | |
29 | #define AM33XX_GMII_SEL_MODE_RGMII 2 | |
30 | ||
31 | #define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7) | |
32 | #define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6) | |
33 | ||
d415fa1b M |
34 | #define GMII_SEL_MODE_MASK 0x3 |
35 | ||
5892cd13 M |
36 | struct cpsw_phy_sel_priv { |
37 | struct device *dev; | |
38 | u32 __iomem *gmii_sel; | |
39 | bool rmii_clock_external; | |
40 | void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv, | |
41 | phy_interface_t phy_mode, int slave); | |
42 | }; | |
43 | ||
44 | ||
45 | static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv, | |
46 | phy_interface_t phy_mode, int slave) | |
47 | { | |
48 | u32 reg; | |
49 | u32 mask; | |
50 | u32 mode = 0; | |
51 | ||
52 | reg = readl(priv->gmii_sel); | |
53 | ||
54 | switch (phy_mode) { | |
55 | case PHY_INTERFACE_MODE_RMII: | |
56 | mode = AM33XX_GMII_SEL_MODE_RMII; | |
57 | break; | |
58 | ||
59 | case PHY_INTERFACE_MODE_RGMII: | |
60 | case PHY_INTERFACE_MODE_RGMII_ID: | |
61 | case PHY_INTERFACE_MODE_RGMII_RXID: | |
62 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
63 | mode = AM33XX_GMII_SEL_MODE_RGMII; | |
64 | break; | |
65 | ||
5892cd13 | 66 | default: |
d148bbd3 DR |
67 | dev_warn(priv->dev, |
68 | "Unsupported PHY mode: \"%s\". Defaulting to MII.\n", | |
69 | phy_modes(phy_mode)); | |
70 | /* fallthrough */ | |
71 | case PHY_INTERFACE_MODE_MII: | |
5892cd13 M |
72 | mode = AM33XX_GMII_SEL_MODE_MII; |
73 | break; | |
74 | }; | |
75 | ||
d415fa1b | 76 | mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6); |
5892cd13 M |
77 | mode <<= slave * 2; |
78 | ||
79 | if (priv->rmii_clock_external) { | |
80 | if (slave == 0) | |
81 | mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN; | |
82 | else | |
83 | mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN; | |
84 | } | |
85 | ||
86 | reg &= ~mask; | |
87 | reg |= mode; | |
88 | ||
89 | writel(reg, priv->gmii_sel); | |
90 | } | |
91 | ||
d415fa1b M |
92 | static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv, |
93 | phy_interface_t phy_mode, int slave) | |
94 | { | |
95 | u32 reg; | |
96 | u32 mask; | |
97 | u32 mode = 0; | |
98 | ||
99 | reg = readl(priv->gmii_sel); | |
100 | ||
101 | switch (phy_mode) { | |
102 | case PHY_INTERFACE_MODE_RMII: | |
103 | mode = AM33XX_GMII_SEL_MODE_RMII; | |
104 | break; | |
105 | ||
106 | case PHY_INTERFACE_MODE_RGMII: | |
107 | case PHY_INTERFACE_MODE_RGMII_ID: | |
108 | case PHY_INTERFACE_MODE_RGMII_RXID: | |
109 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
110 | mode = AM33XX_GMII_SEL_MODE_RGMII; | |
111 | break; | |
112 | ||
d415fa1b | 113 | default: |
d148bbd3 DR |
114 | dev_warn(priv->dev, |
115 | "Unsupported PHY mode: \"%s\". Defaulting to MII.\n", | |
116 | phy_modes(phy_mode)); | |
117 | /* fallthrough */ | |
118 | case PHY_INTERFACE_MODE_MII: | |
d415fa1b M |
119 | mode = AM33XX_GMII_SEL_MODE_MII; |
120 | break; | |
121 | }; | |
122 | ||
123 | switch (slave) { | |
124 | case 0: | |
125 | mask = GMII_SEL_MODE_MASK; | |
126 | break; | |
127 | case 1: | |
128 | mask = GMII_SEL_MODE_MASK << 4; | |
129 | mode <<= 4; | |
130 | break; | |
131 | default: | |
132 | dev_err(priv->dev, "invalid slave number...\n"); | |
133 | return; | |
134 | } | |
135 | ||
136 | if (priv->rmii_clock_external) | |
137 | dev_err(priv->dev, "RMII External clock is not supported\n"); | |
138 | ||
139 | reg &= ~mask; | |
140 | reg |= mode; | |
141 | ||
142 | writel(reg, priv->gmii_sel); | |
143 | } | |
144 | ||
5892cd13 M |
145 | static struct platform_driver cpsw_phy_sel_driver; |
146 | static int match(struct device *dev, void *data) | |
147 | { | |
148 | struct device_node *node = (struct device_node *)data; | |
149 | return dev->of_node == node && | |
150 | dev->driver == &cpsw_phy_sel_driver.driver; | |
151 | } | |
152 | ||
153 | void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave) | |
154 | { | |
155 | struct device_node *node; | |
156 | struct cpsw_phy_sel_priv *priv; | |
157 | ||
158 | node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel"); | |
159 | if (!node) { | |
160 | dev_err(dev, "Phy mode driver DT not found\n"); | |
161 | return; | |
162 | } | |
163 | ||
164 | dev = bus_find_device(&platform_bus_type, NULL, node, match); | |
165 | priv = dev_get_drvdata(dev); | |
166 | ||
167 | priv->cpsw_phy_sel(priv, phy_mode, slave); | |
168 | } | |
169 | EXPORT_SYMBOL_GPL(cpsw_phy_sel); | |
170 | ||
171 | static const struct of_device_id cpsw_phy_sel_id_table[] = { | |
172 | { | |
173 | .compatible = "ti,am3352-cpsw-phy-sel", | |
174 | .data = &cpsw_gmii_sel_am3352, | |
175 | }, | |
d415fa1b M |
176 | { |
177 | .compatible = "ti,dra7xx-cpsw-phy-sel", | |
178 | .data = &cpsw_gmii_sel_dra7xx, | |
179 | }, | |
b80b9309 M |
180 | { |
181 | .compatible = "ti,am43xx-cpsw-phy-sel", | |
182 | .data = &cpsw_gmii_sel_am3352, | |
183 | }, | |
5892cd13 M |
184 | {} |
185 | }; | |
5892cd13 M |
186 | |
187 | static int cpsw_phy_sel_probe(struct platform_device *pdev) | |
188 | { | |
189 | struct resource *res; | |
190 | const struct of_device_id *of_id; | |
191 | struct cpsw_phy_sel_priv *priv; | |
192 | ||
193 | of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node); | |
194 | if (!of_id) | |
195 | return -EINVAL; | |
196 | ||
197 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
198 | if (!priv) { | |
199 | dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n"); | |
200 | return -ENOMEM; | |
201 | } | |
202 | ||
84ef36bd | 203 | priv->dev = &pdev->dev; |
5892cd13 M |
204 | priv->cpsw_phy_sel = of_id->data; |
205 | ||
206 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel"); | |
207 | priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res); | |
208 | if (IS_ERR(priv->gmii_sel)) | |
209 | return PTR_ERR(priv->gmii_sel); | |
210 | ||
211 | if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL)) | |
212 | priv->rmii_clock_external = true; | |
213 | ||
214 | dev_set_drvdata(&pdev->dev, priv); | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
219 | static struct platform_driver cpsw_phy_sel_driver = { | |
220 | .probe = cpsw_phy_sel_probe, | |
221 | .driver = { | |
222 | .name = "cpsw-phy-sel", | |
2afc6dff | 223 | .of_match_table = cpsw_phy_sel_id_table, |
5892cd13 M |
224 | }, |
225 | }; | |
b3c8ec35 | 226 | builtin_platform_driver(cpsw_phy_sel_driver); |