1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2023, Intel Corporation
4 * stmmac EST(802.3 Qbv) handling
5 */
6 #include <linux/iopoll.h>
7 #include <linux/types.h>
8 #include "stmmac.h"
9 #include "stmmac_est.h"
10
est_write(void __iomem * est_addr,u32 reg,u32 val,bool gcl)11 static int est_write(void __iomem *est_addr, u32 reg, u32 val, bool gcl)
12 {
13 u32 ctrl;
14
15 writel(val, est_addr + EST_GCL_DATA);
16
17 ctrl = (reg << EST_ADDR_SHIFT);
18 ctrl |= gcl ? 0 : EST_GCRR;
19 writel(ctrl, est_addr + EST_GCL_CONTROL);
20
21 ctrl |= EST_SRWO;
22 writel(ctrl, est_addr + EST_GCL_CONTROL);
23
24 return readl_poll_timeout(est_addr + EST_GCL_CONTROL, ctrl,
25 !(ctrl & EST_SRWO), 100, 5000);
26 }
27
est_configure(struct stmmac_priv * priv,struct stmmac_est * cfg,unsigned int ptp_rate)28 static int est_configure(struct stmmac_priv *priv, struct stmmac_est *cfg,
29 unsigned int ptp_rate)
30 {
31 void __iomem *est_addr = priv->estaddr;
32 int i, ret = 0;
33 u32 ctrl;
34
35 if (!ptp_rate) {
36 netdev_warn(priv->dev, "Invalid PTP rate");
37 return -EINVAL;
38 }
39
40 ret |= est_write(est_addr, EST_BTR_LOW, cfg->btr[0], false);
41 ret |= est_write(est_addr, EST_BTR_HIGH, cfg->btr[1], false);
42 ret |= est_write(est_addr, EST_TER, cfg->ter, false);
43 ret |= est_write(est_addr, EST_LLR, cfg->gcl_size, false);
44 ret |= est_write(est_addr, EST_CTR_LOW, cfg->ctr[0], false);
45 ret |= est_write(est_addr, EST_CTR_HIGH, cfg->ctr[1], false);
46 if (ret)
47 return ret;
48
49 for (i = 0; i < cfg->gcl_size; i++) {
50 ret = est_write(est_addr, i, cfg->gcl[i], true);
51 if (ret)
52 return ret;
53 }
54
55 ctrl = readl(est_addr + EST_CONTROL);
56 if (priv->plat->has_xgmac) {
57 ctrl &= ~EST_XGMAC_PTOV;
58 ctrl |= ((NSEC_PER_SEC / ptp_rate) * EST_XGMAC_PTOV_MUL) <<
59 EST_XGMAC_PTOV_SHIFT;
60 } else {
61 ctrl &= ~EST_GMAC5_PTOV;
62 ctrl |= ((NSEC_PER_SEC / ptp_rate) * EST_GMAC5_PTOV_MUL) <<
63 EST_GMAC5_PTOV_SHIFT;
64 }
65 if (cfg->enable)
66 ctrl |= EST_EEST | EST_SSWL;
67 else
68 ctrl &= ~EST_EEST;
69
70 writel(ctrl, est_addr + EST_CONTROL);
71
72 /* Configure EST interrupt */
73 if (cfg->enable)
74 ctrl = EST_IECGCE | EST_IEHS | EST_IEHF | EST_IEBE | EST_IECC;
75 else
76 ctrl = 0;
77
78 writel(ctrl, est_addr + EST_INT_EN);
79
80 return 0;
81 }
82
est_irq_status(struct stmmac_priv * priv,struct net_device * dev,struct stmmac_extra_stats * x,u32 txqcnt)83 static void est_irq_status(struct stmmac_priv *priv, struct net_device *dev,
84 struct stmmac_extra_stats *x, u32 txqcnt)
85 {
86 u32 status, value, feqn, hbfq, hbfs, btrl, btrl_max;
87 void __iomem *est_addr = priv->estaddr;
88 u32 txqcnt_mask = BIT(txqcnt) - 1;
89 int i;
90
91 status = readl(est_addr + EST_STATUS);
92
93 value = EST_CGCE | EST_HLBS | EST_HLBF | EST_BTRE | EST_SWLC;
94
95 /* Return if there is no error */
96 if (!(status & value))
97 return;
98
99 if (status & EST_CGCE) {
100 /* Clear Interrupt */
101 writel(EST_CGCE, est_addr + EST_STATUS);
102
103 x->mtl_est_cgce++;
104 }
105
106 if (status & EST_HLBS) {
107 value = readl(est_addr + EST_SCH_ERR);
108 value &= txqcnt_mask;
109
110 x->mtl_est_hlbs++;
111
112 /* Clear Interrupt */
113 writel(value, est_addr + EST_SCH_ERR);
114
115 /* Collecting info to shows all the queues that has HLBS
116 * issue. The only way to clear this is to clear the
117 * statistic
118 */
119 if (net_ratelimit())
120 netdev_err(dev, "EST: HLB(sched) Queue 0x%x\n", value);
121 }
122
123 if (status & EST_HLBF) {
124 value = readl(est_addr + EST_FRM_SZ_ERR);
125 feqn = value & txqcnt_mask;
126
127 value = readl(est_addr + EST_FRM_SZ_CAP);
128 hbfq = (value & EST_SZ_CAP_HBFQ_MASK(txqcnt)) >>
129 EST_SZ_CAP_HBFQ_SHIFT;
130 hbfs = value & EST_SZ_CAP_HBFS_MASK;
131
132 x->mtl_est_hlbf++;
133
134 for (i = 0; i < txqcnt; i++) {
135 if (feqn & BIT(i))
136 x->mtl_est_txq_hlbf[i]++;
137 }
138
139 /* Clear Interrupt */
140 writel(feqn, est_addr + EST_FRM_SZ_ERR);
141
142 if (net_ratelimit())
143 netdev_err(dev, "EST: HLB(size) Queue %u Size %u\n",
144 hbfq, hbfs);
145 }
146
147 if (status & EST_BTRE) {
148 if (priv->plat->has_xgmac) {
149 btrl = FIELD_GET(EST_XGMAC_BTRL, status);
150 btrl_max = FIELD_MAX(EST_XGMAC_BTRL);
151 } else {
152 btrl = FIELD_GET(EST_GMAC5_BTRL, status);
153 btrl_max = FIELD_MAX(EST_GMAC5_BTRL);
154 }
155 if (btrl == btrl_max)
156 x->mtl_est_btrlm++;
157 else
158 x->mtl_est_btre++;
159
160 if (net_ratelimit())
161 netdev_info(dev, "EST: BTR Error Loop Count %u\n",
162 btrl);
163
164 writel(EST_BTRE, est_addr + EST_STATUS);
165 }
166
167 if (status & EST_SWLC) {
168 writel(EST_SWLC, est_addr + EST_STATUS);
169 netdev_info(dev, "EST: SWOL has been switched\n");
170 }
171 }
172
173 const struct stmmac_est_ops dwmac510_est_ops = {
174 .configure = est_configure,
175 .irq_status = est_irq_status,
176 };
177