aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2016-09-19 11:49:07 -0400
committerArnd Bergmann <arnd@arndb.de>2016-09-19 11:49:07 -0400
commit53570cbc189257d2e95c79e6baa9e12b0a3b3cdf (patch)
tree2f7a1a02ac46c6ecd9df793979c71456bbbbd04c /drivers/net
parentbac6dd36e355d5b1f089ed507b6579938e4c07c1 (diff)
parentdfdd7d4af6ebee027be7bf2636b2314937948da6 (diff)
Merge tag 'amlogic-drivers-2' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic into next/late
Pull "Amlogic driver updates for v4.9, 2nd round" from Kevin Hilman: - media: update IR support for newer SoCs - firmware: add secure monitor driver - net: new stmmac glue driver - usb: udd DWC2 support for meson-gxbb - clocks: expose more clock IDs for use by DT - DT binding updates * tag 'amlogic-drivers-2' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic: (21 commits) clk: gxbb: expose i2c clocks clk: gxbb: expose USB clocks clk: gxbb: expose spifc clock clk: gxbb: expose MPLL2 clock for use by DT Documentation: dt-bindings: Add documentation for the Meson USB2 PHYs usb: dwc2: add support for Meson8b and GXBB SoCs net: stmmac: update the module description of the dwmac-meson driver net: stmmac: add a glue driver for the Amlogic Meson 8b / GXBB DWMAC stmmac: introduce get_stmmac_bsp_priv() helper net: dt-bindings: Document the new Meson8b and GXBB DWMAC bindings clk: meson-gxbb: Export PWM related clocks for DT meson: clk: Add support for clock gates gxbb: clk: Adjust MESON_GATE macro to be shared with meson8b clk: meson: Copy meson8b CLKID defines to private header file meson: clk: Rename register names according to Amlogic datasheet meson: clk: Move register definitions to meson8b.h clk: meson: Rename meson8b-clkc.c to reflect gxbb naming convention nvmem: amlogic: Add Amlogic Meson EFUSE driver firmware: Amlogic: Add secure monitor driver media: rc: meson-ir: Add support for newer versions of the IR decoder ...
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c324
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h8
5 files changed, 338 insertions, 6 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 8f06a6621ab1..54de17529c97 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -61,13 +61,13 @@ config DWMAC_LPC18XX
61config DWMAC_MESON 61config DWMAC_MESON
62 tristate "Amlogic Meson dwmac support" 62 tristate "Amlogic Meson dwmac support"
63 default ARCH_MESON 63 default ARCH_MESON
64 depends on OF && (ARCH_MESON || COMPILE_TEST) 64 depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST)
65 help 65 help
66 Support for Ethernet controller on Amlogic Meson SoCs. 66 Support for Ethernet controller on Amlogic Meson SoCs.
67 67
68 This selects the Amlogic Meson SoC glue layer support for 68 This selects the Amlogic Meson SoC glue layer support for
69 the stmmac device driver. This driver is used for Meson6 and 69 the stmmac device driver. This driver is used for Meson6,
70 Meson8 SoCs. 70 Meson8, Meson8b and GXBB SoCs.
71 71
72config DWMAC_ROCKCHIP 72config DWMAC_ROCKCHIP
73 tristate "Rockchip dwmac support" 73 tristate "Rockchip dwmac support"
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 44b630cd1755..f77edb9c2fa9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -9,7 +9,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
9obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o 9obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
10obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o 10obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
11obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o 11obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
12obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o 12obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
13obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o 13obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o
14obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o 14obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o
15obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o 15obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
index c1bac1912b37..309d99536a2c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Amlogic Meson DWMAC glue layer 2 * Amlogic Meson6 and Meson8 DWMAC glue layer
3 * 3 *
4 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> 4 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
5 * 5 *
@@ -96,5 +96,5 @@ static struct platform_driver meson6_dwmac_driver = {
96module_platform_driver(meson6_dwmac_driver); 96module_platform_driver(meson6_dwmac_driver);
97 97
98MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); 98MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
99MODULE_DESCRIPTION("Amlogic Meson DWMAC glue layer"); 99MODULE_DESCRIPTION("Amlogic Meson6 and Meson8 DWMAC glue layer");
100MODULE_LICENSE("GPL v2"); 100MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
new file mode 100644
index 000000000000..250e4ceafc8d
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
@@ -0,0 +1,324 @@
1/*
2 * Amlogic Meson8b and GXBB DWMAC glue layer
3 *
4 * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * You should have received a copy of the GNU General Public License
11 * along with this program. If not, see <http://www.gnu.org/licenses/>.
12 */
13
14#include <linux/clk.h>
15#include <linux/clk-provider.h>
16#include <linux/device.h>
17#include <linux/ethtool.h>
18#include <linux/io.h>
19#include <linux/ioport.h>
20#include <linux/module.h>
21#include <linux/of_net.h>
22#include <linux/mfd/syscon.h>
23#include <linux/platform_device.h>
24#include <linux/stmmac.h>
25
26#include "stmmac_platform.h"
27
28#define PRG_ETH0 0x0
29
30#define PRG_ETH0_RGMII_MODE BIT(0)
31
32/* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
33#define PRG_ETH0_CLK_M250_SEL_SHIFT 4
34#define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4)
35
36#define PRG_ETH0_TXDLY_SHIFT 5
37#define PRG_ETH0_TXDLY_MASK GENMASK(6, 5)
38#define PRG_ETH0_TXDLY_OFF (0x0 << PRG_ETH0_TXDLY_SHIFT)
39#define PRG_ETH0_TXDLY_QUARTER (0x1 << PRG_ETH0_TXDLY_SHIFT)
40#define PRG_ETH0_TXDLY_HALF (0x2 << PRG_ETH0_TXDLY_SHIFT)
41#define PRG_ETH0_TXDLY_THREE_QUARTERS (0x3 << PRG_ETH0_TXDLY_SHIFT)
42
43/* divider for the result of m250_sel */
44#define PRG_ETH0_CLK_M250_DIV_SHIFT 7
45#define PRG_ETH0_CLK_M250_DIV_WIDTH 3
46
47/* divides the result of m25_sel by either 5 (bit unset) or 10 (bit set) */
48#define PRG_ETH0_CLK_M25_DIV_SHIFT 10
49#define PRG_ETH0_CLK_M25_DIV_WIDTH 1
50
51#define PRG_ETH0_INVERTED_RMII_CLK BIT(11)
52#define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12)
53
54#define MUX_CLK_NUM_PARENTS 2
55
56struct meson8b_dwmac {
57 struct platform_device *pdev;
58
59 void __iomem *regs;
60
61 phy_interface_t phy_mode;
62
63 struct clk_mux m250_mux;
64 struct clk *m250_mux_clk;
65 struct clk *m250_mux_parent[MUX_CLK_NUM_PARENTS];
66
67 struct clk_divider m250_div;
68 struct clk *m250_div_clk;
69
70 struct clk_divider m25_div;
71 struct clk *m25_div_clk;
72};
73
74static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
75 u32 mask, u32 value)
76{
77 u32 data;
78
79 data = readl(dwmac->regs + reg);
80 data &= ~mask;
81 data |= (value & mask);
82
83 writel(data, dwmac->regs + reg);
84}
85
86static int meson8b_init_clk(struct meson8b_dwmac *dwmac)
87{
88 struct clk_init_data init;
89 int i, ret;
90 struct device *dev = &dwmac->pdev->dev;
91 char clk_name[32];
92 const char *clk_div_parents[1];
93 const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
94 static struct clk_div_table clk_25m_div_table[] = {
95 { .val = 0, .div = 5 },
96 { .val = 1, .div = 10 },
97 { /* sentinel */ },
98 };
99
100 /* get the mux parents from DT */
101 for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
102 char name[16];
103
104 snprintf(name, sizeof(name), "clkin%d", i);
105 dwmac->m250_mux_parent[i] = devm_clk_get(dev, name);
106 if (IS_ERR(dwmac->m250_mux_parent[i])) {
107 ret = PTR_ERR(dwmac->m250_mux_parent[i]);
108 if (ret != -EPROBE_DEFER)
109 dev_err(dev, "Missing clock %s\n", name);
110 return ret;
111 }
112
113 mux_parent_names[i] =
114 __clk_get_name(dwmac->m250_mux_parent[i]);
115 }
116
117 /* create the m250_mux */
118 snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev));
119 init.name = clk_name;
120 init.ops = &clk_mux_ops;
121 init.flags = 0;
122 init.parent_names = mux_parent_names;
123 init.num_parents = MUX_CLK_NUM_PARENTS;
124
125 dwmac->m250_mux.reg = dwmac->regs + PRG_ETH0;
126 dwmac->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
127 dwmac->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
128 dwmac->m250_mux.flags = 0;
129 dwmac->m250_mux.table = NULL;
130 dwmac->m250_mux.hw.init = &init;
131
132 dwmac->m250_mux_clk = devm_clk_register(dev, &dwmac->m250_mux.hw);
133 if (WARN_ON(IS_ERR(dwmac->m250_mux_clk)))
134 return PTR_ERR(dwmac->m250_mux_clk);
135
136 /* create the m250_div */
137 snprintf(clk_name, sizeof(clk_name), "%s#m250_div", dev_name(dev));
138 init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
139 init.ops = &clk_divider_ops;
140 init.flags = CLK_SET_RATE_PARENT;
141 clk_div_parents[0] = __clk_get_name(dwmac->m250_mux_clk);
142 init.parent_names = clk_div_parents;
143 init.num_parents = ARRAY_SIZE(clk_div_parents);
144
145 dwmac->m250_div.reg = dwmac->regs + PRG_ETH0;
146 dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
147 dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
148 dwmac->m250_div.hw.init = &init;
149 dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO;
150
151 dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw);
152 if (WARN_ON(IS_ERR(dwmac->m250_div_clk)))
153 return PTR_ERR(dwmac->m250_div_clk);
154
155 /* create the m25_div */
156 snprintf(clk_name, sizeof(clk_name), "%s#m25_div", dev_name(dev));
157 init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
158 init.ops = &clk_divider_ops;
159 init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
160 clk_div_parents[0] = __clk_get_name(dwmac->m250_div_clk);
161 init.parent_names = clk_div_parents;
162 init.num_parents = ARRAY_SIZE(clk_div_parents);
163
164 dwmac->m25_div.reg = dwmac->regs + PRG_ETH0;
165 dwmac->m25_div.shift = PRG_ETH0_CLK_M25_DIV_SHIFT;
166 dwmac->m25_div.width = PRG_ETH0_CLK_M25_DIV_WIDTH;
167 dwmac->m25_div.table = clk_25m_div_table;
168 dwmac->m25_div.hw.init = &init;
169 dwmac->m25_div.flags = CLK_DIVIDER_ALLOW_ZERO;
170
171 dwmac->m25_div_clk = devm_clk_register(dev, &dwmac->m25_div.hw);
172 if (WARN_ON(IS_ERR(dwmac->m25_div_clk)))
173 return PTR_ERR(dwmac->m25_div_clk);
174
175 return 0;
176}
177
178static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
179{
180 int ret;
181 unsigned long clk_rate;
182
183 switch (dwmac->phy_mode) {
184 case PHY_INTERFACE_MODE_RGMII:
185 case PHY_INTERFACE_MODE_RGMII_ID:
186 case PHY_INTERFACE_MODE_RGMII_RXID:
187 case PHY_INTERFACE_MODE_RGMII_TXID:
188 /* Generate a 25MHz clock for the PHY */
189 clk_rate = 25 * 1000 * 1000;
190
191 /* enable RGMII mode */
192 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
193 PRG_ETH0_RGMII_MODE);
194
195 /* only relevant for RMII mode -> disable in RGMII mode */
196 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
197 PRG_ETH0_INVERTED_RMII_CLK, 0);
198
199 /* TX clock delay - all known boards use a 1/4 cycle delay */
200 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
201 PRG_ETH0_TXDLY_QUARTER);
202 break;
203
204 case PHY_INTERFACE_MODE_RMII:
205 /* Use the rate of the mux clock for the internal RMII PHY */
206 clk_rate = clk_get_rate(dwmac->m250_mux_clk);
207
208 /* disable RGMII mode -> enables RMII mode */
209 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
210 0);
211
212 /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
213 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
214 PRG_ETH0_INVERTED_RMII_CLK,
215 PRG_ETH0_INVERTED_RMII_CLK);
216
217 /* TX clock delay cannot be configured in RMII mode */
218 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
219 0);
220
221 break;
222
223 default:
224 dev_err(&dwmac->pdev->dev, "unsupported phy-mode %s\n",
225 phy_modes(dwmac->phy_mode));
226 return -EINVAL;
227 }
228
229 ret = clk_prepare_enable(dwmac->m25_div_clk);
230 if (ret) {
231 dev_err(&dwmac->pdev->dev, "failed to enable the PHY clock\n");
232 return ret;
233 }
234
235 ret = clk_set_rate(dwmac->m25_div_clk, clk_rate);
236 if (ret) {
237 clk_disable_unprepare(dwmac->m25_div_clk);
238
239 dev_err(&dwmac->pdev->dev, "failed to set PHY clock\n");
240 return ret;
241 }
242
243 /* enable TX_CLK and PHY_REF_CLK generator */
244 meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
245 PRG_ETH0_TX_AND_PHY_REF_CLK);
246
247 return 0;
248}
249
250static int meson8b_dwmac_probe(struct platform_device *pdev)
251{
252 struct plat_stmmacenet_data *plat_dat;
253 struct stmmac_resources stmmac_res;
254 struct resource *res;
255 struct meson8b_dwmac *dwmac;
256 int ret;
257
258 ret = stmmac_get_platform_resources(pdev, &stmmac_res);
259 if (ret)
260 return ret;
261
262 plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
263 if (IS_ERR(plat_dat))
264 return PTR_ERR(plat_dat);
265
266 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
267 if (!dwmac)
268 return -ENOMEM;
269
270 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
271 dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
272 if (IS_ERR(dwmac->regs))
273 return PTR_ERR(dwmac->regs);
274
275 dwmac->pdev = pdev;
276 dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
277 if (dwmac->phy_mode < 0) {
278 dev_err(&pdev->dev, "missing phy-mode property\n");
279 return -EINVAL;
280 }
281
282 ret = meson8b_init_clk(dwmac);
283 if (ret)
284 return ret;
285
286 ret = meson8b_init_prg_eth(dwmac);
287 if (ret)
288 return ret;
289
290 plat_dat->bsp_priv = dwmac;
291
292 return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
293}
294
295static int meson8b_dwmac_remove(struct platform_device *pdev)
296{
297 struct meson8b_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
298
299 clk_disable_unprepare(dwmac->m25_div_clk);
300
301 return stmmac_pltfr_remove(pdev);
302}
303
304static const struct of_device_id meson8b_dwmac_match[] = {
305 { .compatible = "amlogic,meson8b-dwmac" },
306 { .compatible = "amlogic,meson-gxbb-dwmac" },
307 { }
308};
309MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
310
311static struct platform_driver meson8b_dwmac_driver = {
312 .probe = meson8b_dwmac_probe,
313 .remove = meson8b_dwmac_remove,
314 .driver = {
315 .name = "meson8b-dwmac",
316 .pm = &stmmac_pltfr_pm_ops,
317 .of_match_table = meson8b_dwmac_match,
318 },
319};
320module_platform_driver(meson8b_dwmac_driver);
321
322MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
323MODULE_DESCRIPTION("Amlogic Meson8b and GXBB DWMAC glue layer");
324MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
index ffeb8d9e2b2e..64e147f53a9c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
@@ -30,4 +30,12 @@ int stmmac_get_platform_resources(struct platform_device *pdev,
30int stmmac_pltfr_remove(struct platform_device *pdev); 30int stmmac_pltfr_remove(struct platform_device *pdev);
31extern const struct dev_pm_ops stmmac_pltfr_pm_ops; 31extern const struct dev_pm_ops stmmac_pltfr_pm_ops;
32 32
33static inline void *get_stmmac_bsp_priv(struct device *dev)
34{
35 struct net_device *ndev = dev_get_drvdata(dev);
36 struct stmmac_priv *priv = netdev_priv(ndev);
37
38 return priv->plat->bsp_priv;
39}
40
33#endif /* __STMMAC_PLATFORM_H__ */ 41#endif /* __STMMAC_PLATFORM_H__ */