Skip to content

Commit f22e365

Browse files
roy-nexthoplotus-nexthop
authored andcommitted
add mdio protocol support
1 parent d9f56b4 commit f22e365

File tree

9 files changed

+842
-10
lines changed

9 files changed

+842
-10
lines changed

platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
obj-m := pddf_custom_fpga_algo.o
2+
obj-m += pddf_custom_mdio_algo.o
23
obj-m += nh_tda38740.o
34
obj-m += nh_isl68137.o
45
obj-m += nh_pmbus_core.o
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Copyright (C) 2025 Nexthop Systems Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
*/
10+
11+
#define __STDC_WANT_LIB_EXT1__ 1
12+
13+
#include <linux/delay.h>
14+
#include <linux/errno.h>
15+
#include <linux/jiffies.h>
16+
#include <linux/kernel.h>
17+
#include <linux/mdio.h>
18+
#include <linux/mii.h>
19+
#include <linux/module.h>
20+
#include <linux/moduleparam.h>
21+
#include <linux/phy.h>
22+
#include <linux/string.h>
23+
#include "../../../../pddf/i2c/modules/include/pddf_multifpgapci_defs.h"
24+
#include "../../../../pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h"
25+
26+
// MDIO Control Register
27+
#define FPGA_MDIO_CTRL_REG_OFFSET 0x0000
28+
29+
// Bit definitions for MDIO Control Register
30+
#define FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS 31
31+
#define FPGA_MDIO_CTRL_READ_CMD_BIT_POS 30
32+
33+
#define FPGA_MDIO_CTRL_OP_SHIFT 26
34+
#define FPGA_MDIO_CTRL_OP_MASK 0x3
35+
36+
#define FPGA_MDIO_OP_ADDRESS_VAL 0x0
37+
#define FPGA_MDIO_OP_WRITE_VAL 0x1
38+
#define FPGA_MDIO_OP_READ_VAL 0x3
39+
#define FPGA_MDIO_OP_POSTREAD_VAL 0x2
40+
41+
#define FPGA_MDIO_CTRL_PHY_ADDR_SHIFT 21
42+
#define FPGA_MDIO_CTRL_DEV_ADDR_SHIFT 16
43+
#define FPGA_MDIO_CTRL_DATA_SHIFT 0
44+
#define FPGA_MDIO_CTRL_DATA_MASK 0xFFFF
45+
46+
// MDIO Read Data Register
47+
#define FPGA_MDIO_READ_DATA_REG_OFFSET 0x0004
48+
49+
// Bit definitions for MDIO Read Data Register
50+
#define FPGA_MDIO_READ_DATA_VALID_BIT BIT(31)
51+
#define FPGA_MDIO_READ_DATA_BUSY_BIT BIT(30)
52+
#define FPGA_MDIO_READ_DATA_MASK 0xFFFF
53+
54+
#define MDIO_DEVAD_SHIFT 16
55+
56+
static int fpga_mdio_wait_for_idle(struct fpga_mdio_priv *priv)
57+
{
58+
// Timeout after 100ms if busy bit has not cleared
59+
unsigned long timeout = jiffies + msecs_to_jiffies(100);
60+
uint32_t read_data_reg;
61+
62+
do {
63+
read_data_reg = ioread32(priv->reg_base +
64+
FPGA_MDIO_READ_DATA_REG_OFFSET);
65+
if (!(read_data_reg & FPGA_MDIO_READ_DATA_BUSY_BIT)) {
66+
// Busy bit is cleared, transaction is complete
67+
return 0;
68+
}
69+
usleep_range(5, 10);
70+
} while (time_before(jiffies, timeout));
71+
72+
pddf_dbg(
73+
MULTIFPGA,
74+
KERN_ERR
75+
"[%s]: MDIO transaction timed out waiting for busy bit to clear\n",
76+
__FUNCTION__);
77+
return -ETIMEDOUT;
78+
}
79+
80+
static int fpga_mdio_do_transaction(struct fpga_mdio_priv *priv, int phy_addr,
81+
int dev_addr, uint32_t op_val,
82+
uint16_t data_val, uint32_t cmd_bit)
83+
{
84+
uint32_t cmd_val =
85+
(BIT(cmd_bit) | (op_val << FPGA_MDIO_CTRL_OP_SHIFT) |
86+
((phy_addr & 0x1F) << FPGA_MDIO_CTRL_PHY_ADDR_SHIFT) |
87+
((dev_addr & 0x1F) << FPGA_MDIO_CTRL_DEV_ADDR_SHIFT) |
88+
((data_val & FPGA_MDIO_CTRL_DATA_MASK)
89+
<< FPGA_MDIO_CTRL_DATA_SHIFT));
90+
91+
iowrite32(cmd_val, priv->reg_base + FPGA_MDIO_CTRL_REG_OFFSET);
92+
int ret = fpga_mdio_wait_for_idle(priv);
93+
if (ret < 0)
94+
return ret;
95+
96+
iowrite32(0x0, priv->reg_base + FPGA_MDIO_CTRL_REG_OFFSET);
97+
return fpga_mdio_wait_for_idle(priv);
98+
}
99+
100+
int fpga_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
101+
{
102+
struct fpga_mdio_priv *priv = bus->priv;
103+
int ret = -EIO;
104+
105+
if (!priv || !priv->reg_base) {
106+
pddf_dbg(
107+
MULTIFPGA,
108+
KERN_ERR
109+
"[%s]: Bus private data (reg_base) not properly set for bus %s\n",
110+
__FUNCTION__, dev_name(&bus->dev));
111+
return -EINVAL;
112+
}
113+
114+
// Extract the device and reg addresses from reg_num
115+
int dev_addr = (reg_num >> MDIO_DEVAD_SHIFT) & 0x1F;
116+
int c45_reg_addr = reg_num & 0xFFFF;
117+
118+
mutex_lock(&priv->lock);
119+
120+
// Clause 45 Handling
121+
122+
// Step 1: Write the register address
123+
ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr,
124+
FPGA_MDIO_OP_ADDRESS_VAL, c45_reg_addr,
125+
FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS);
126+
if (ret < 0)
127+
goto out;
128+
129+
// Step 2: Read the data from the register
130+
ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr,
131+
FPGA_MDIO_OP_READ_VAL, 0,
132+
FPGA_MDIO_CTRL_READ_CMD_BIT_POS);
133+
if (ret < 0)
134+
goto out;
135+
136+
// Poll for Read Data Valid and read data
137+
unsigned long timeout = jiffies + msecs_to_jiffies(100);
138+
do {
139+
uint32_t read_data_reg = ioread32(
140+
priv->reg_base + FPGA_MDIO_READ_DATA_REG_OFFSET);
141+
if (read_data_reg & FPGA_MDIO_READ_DATA_VALID_BIT) {
142+
ret = (read_data_reg >> FPGA_MDIO_CTRL_DATA_SHIFT) &
143+
FPGA_MDIO_READ_DATA_MASK;
144+
goto out;
145+
}
146+
usleep_range(10, 20);
147+
} while (time_before(jiffies, timeout));
148+
149+
pddf_dbg(
150+
MULTIFPGA,
151+
KERN_ERR
152+
"[%s]: C45 READ data not valid within timeout for bus %s, PHY 0x%x, MMD 0x%x\n",
153+
__FUNCTION__, dev_name(&bus->dev), phy_addr, dev_addr);
154+
ret = -ETIMEDOUT;
155+
156+
out:
157+
mutex_unlock(&priv->lock);
158+
return ret;
159+
}
160+
EXPORT_SYMBOL(fpga_mdio_read);
161+
162+
int fpga_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
163+
uint16_t val)
164+
{
165+
struct fpga_mdio_priv *priv = bus->priv;
166+
uint32_t cmd_val;
167+
int ret;
168+
169+
if (!priv || !priv->reg_base) {
170+
pddf_dbg(
171+
MULTIFPGA,
172+
KERN_ERR
173+
"[%s]: Bus private data (reg_base) not properly set for bus %s\n",
174+
__FUNCTION__, dev_name(&bus->dev));
175+
return -EINVAL;
176+
}
177+
178+
// Extract the device and reg addresses from reg_num
179+
int dev_addr = (reg_num >> MDIO_DEVAD_SHIFT) & 0x1F;
180+
int c45_reg_addr = reg_num & 0xFFFF;
181+
182+
mutex_lock(&priv->lock);
183+
184+
// Clause 45 Handling
185+
186+
// Step 1: Write the register address
187+
ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr,
188+
FPGA_MDIO_OP_ADDRESS_VAL, c45_reg_addr,
189+
FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS);
190+
if (ret < 0)
191+
goto out;
192+
193+
// Step 2: Write the data to the register
194+
ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr,
195+
FPGA_MDIO_OP_WRITE_VAL, val,
196+
FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS);
197+
if (ret < 0)
198+
goto out;
199+
200+
out:
201+
mutex_unlock(&priv->lock);
202+
return 0;
203+
}
204+
EXPORT_SYMBOL(fpga_mdio_write);
205+
206+
static struct mdio_fpga_ops fpga_algo_ops_instance = {
207+
.read = fpga_mdio_read,
208+
.write = fpga_mdio_write,
209+
};
210+
211+
static int __init pddf_custom_mdio_algo_init(void)
212+
{
213+
pddf_dbg(MULTIFPGA, KERN_INFO "[%s]\n", __FUNCTION__);
214+
mdio_fpga_algo_ops = &fpga_algo_ops_instance;
215+
return 0;
216+
}
217+
218+
static void __exit pddf_custom_mdio_algo_exit(void)
219+
{
220+
pddf_dbg(MULTIFPGA, KERN_INFO "[%s]\n", __FUNCTION__);
221+
mdio_fpga_algo_ops = NULL;
222+
return;
223+
}
224+
225+
module_init(pddf_custom_mdio_algo_init);
226+
module_exit(pddf_custom_mdio_algo_exit);
227+
228+
MODULE_DESCRIPTION("Custom algorithm for Nexthop FPGAPCIe MDIO implementation");
229+
MODULE_VERSION("1.0.0");
230+
MODULE_LICENSE("GPL");

platform/pddf/i2c/debian/rules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ PACKAGE_PRE_NAME := sonic-platform-pddf
1919
KVERSION ?= $(shell uname -r)
2020
KERNEL_SRC := /lib/modules/$(KVERSION)
2121
MOD_SRC_DIR:= $(shell pwd)
22-
MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver
22+
MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c multifpgapci/mdio fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver
2323
MODULE_DIR:= modules
2424
UTILS_DIR := utils
2525
SERVICE_DIR := service

platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "pddf_multifpgapci_gpio_defs.h"
2525
#include "pddf_multifpgapci_i2c_defs.h"
26+
#include "pddf_multifpgapci_mdio_defs.h"
2627

2728
#define NAME_SIZE 32
2829

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2025 Nexthop Systems Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*/
14+
15+
#ifndef __PDDF_MULTIFPGAPCI_MDIO_DEFS_H__
16+
#define __PDDF_MULTIFPGAPCI_MDIO_DEFS_H__
17+
18+
#include "linux/types.h"
19+
#include <linux/kobject.h>
20+
#include <linux/pci.h>
21+
#include <linux/phy.h>
22+
#include <linux/sysfs.h>
23+
24+
#include "pddf_client_defs.h"
25+
26+
#define MDIO_MAX_BUS 512
27+
28+
struct mdio_bus_attrs {
29+
PDDF_ATTR attr_ch_base_offset;
30+
PDDF_ATTR attr_ch_size;
31+
PDDF_ATTR attr_num_virt_ch;
32+
PDDF_ATTR attr_new_mdio_bus;
33+
PDDF_ATTR attr_del_mdio_bus;
34+
};
35+
36+
#define NUM_MDIO_BUS_ATTRS (sizeof(struct mdio_bus_attrs) / sizeof(PDDF_ATTR))
37+
38+
struct mdio_bus_sysfs_vals {
39+
uint32_t ch_base_offset;
40+
uint32_t ch_size;
41+
uint32_t num_virt_ch;
42+
};
43+
44+
struct mdio_bus_drvdata {
45+
struct pci_dev *pci_dev;
46+
size_t bar_length;
47+
struct kobject *mdio_kobj;
48+
49+
// temp_sysfs_vals store temporary values provided by sysfs,
50+
// which are eventually copied/saved to MDIO bus platform data.
51+
struct mdio_bus_sysfs_vals temp_sysfs_vals;
52+
53+
// platform data
54+
struct mii_bus *mdio_buses[MDIO_MAX_BUS];
55+
bool mdio_bus_registered[MDIO_MAX_BUS];
56+
void *__iomem ch_base_addr;
57+
int ch_size;
58+
int num_virt_ch;
59+
60+
// sysfs attrs
61+
struct mdio_bus_attrs attrs;
62+
struct attribute *mdio_bus_attrs[NUM_MDIO_BUS_ATTRS + 1];
63+
struct attribute_group mdio_bus_attr_group;
64+
};
65+
66+
struct fpga_mdio_priv {
67+
void __iomem *reg_base; // Base address for this MDIO instance
68+
struct mutex lock; // Mutex for this MDIO instance
69+
int last_read_value;
70+
};
71+
72+
struct mdio_fpga_ops {
73+
int (*read)(struct mii_bus *bus, int phy_id, int regnum);
74+
int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);
75+
};
76+
77+
extern struct mdio_fpga_ops *mdio_fpga_algo_ops;
78+
79+
#endif
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
obj-m := driver/ gpio/ i2c/
1+
obj-m := driver/ gpio/ i2c/ mdio/
22
obj-m += pddf_multifpgapci_module.o
33

44
ccflags-y := -I$(M)/modules/include
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
obj-m := pddf_multifpgapci_mdio_module.o
2+
3+
ccflags-y := -I$(M)/modules/include

0 commit comments

Comments
 (0)