Skip to content

Commit e24c7f1

Browse files
KrishnaSimmadharipreetam-narayan
authored andcommitted
Simmadha/qsfp mem controller driver (#20)
fpga: QSFP controller subsystem driver for Intel FPGAs Qsfp controller driver to include platform and DFL interface driver. DFL driver is used for the host which accesses the QSFP controller subsystem using the PCIe bus and the platform driver is used where the subsystem is accessed through the AXI/AXI-Lite bus. Signed-off-by: Krishna Kumar S R [email protected]
1 parent 1fec749 commit e24c7f1

File tree

9 files changed

+451
-210
lines changed

9 files changed

+451
-210
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
* Intel QSFP Memory controller subsystem
2+
3+
Required properties:
4+
- compatible: Should be "intel,qsfp-mem"
5+
- reg: Address and length of the register set for the device. It contains
6+
the information of registers in the same order as described by reg-names
7+
- reg-names: Should contain the reg names
8+
9+
Example:
10+
11+
qsfp_eth0: qsfp-eth0 {
12+
compatible = "intel,qsfp-mem";
13+
reg-names = "qsfp-mem-controller";
14+
reg = <0x00000001 0x80112000 0x00001000>,
15+
status = "okay";
16+
};

arch/arm64/boot/dts/intel/socfpga_acadp_ptp_soln.dts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// SPDX-License-Identifier: GPL-2.0
1+
// SPDX-License-Identifier: GPL
22
/*
3-
* Copyright (C) 2022,2023, Intel Corporation
3+
* Copyright (C) 2022,2023 Intel Corporation
44
*/
55
#include "socfpga_agilex.dtsi"
66

drivers/net/phy/Kconfig

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,28 @@ config SFP
7777
depends on HWMON || HWMON=n
7878
select MDIO_I2C
7979

80-
config QSFP
81-
tristate "QSFP cage support"
82-
depends on I2C && PHYLINK
80+
config QSFP_MEM_CORE
81+
tristate "QSFP Controller subsystem core code" if COMPILE_TEST
8382
depends on HWMON || HWMON=n
84-
select MDIO_I2C
8583
help
86-
Currently supports SFF-8636 Rev2.9 QSFP connectors or cables.
84+
The core code for the QSFP Controller subsystem for Intel FPGAs used
85+
by other interafce module drivers for access to the QSFP controller
86+
subsystem registers
87+
88+
config QSFP_MEM
89+
tristate "Memory based platform driver for QSFP Controller subsystem in Intel FPGAs"
90+
select QSFP_MEM_CORE
91+
help
92+
Adds support for a QSFP controller subsystem that shadows the QSFP module's
93+
memory pages in memory
94+
95+
config QSFP_MEM_DFL
96+
tristate "DFL bus driver for QSFP Controller subsystem in Intel FPGAs"
97+
depends on FPGA_DFL
98+
select QSFP_MEM_CORE
99+
help
100+
This is a Device Feature List (DFL) bus driver for the
101+
QSFP Controller subsystem in Intel FPGAs.
87102

88103
comment "MII PHY device drivers"
89104

drivers/net/phy/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ obj-$(CONFIG_SFP) += sfp.o
3333
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
3434
obj-y += $(sfp-obj-y) $(sfp-obj-m)
3535

36-
obj-$(CONFIG_QSFP) += qsfp.o qsfp-mem.o
36+
obj-$(CONFIG_QSFP) += qsfp.o
3737
qsfp-obj-$(CONFIG_QSFP) += qsfp_bus.o
3838
obj-y += $(qsfp-obj-y) $(qsfp-obj-m)
3939

40+
obj-$(CONFIG_QSFP_MEM_CORE) += qsfp-mem-core.o
41+
obj-$(CONFIG_QSFP_MEM_DFL) += qsfp-mem-dfl.o
42+
obj-$(CONFIG_QSFP_MEM) += qsfp-mem-platform.o
43+
4044
obj-$(CONFIG_ADIN_PHY) += adin.o
4145
obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
4246
obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o

drivers/net/phy/qsfp-mem-core.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// SPDX-License-Identifier: GPL
2+
3+
/* Intel(R) Memory based QSFP driver.
4+
*
5+
* Copyright (C) 2020,2022 Intel Corporation. All rights reserved.
6+
*/
7+
8+
#include <linux/bitfield.h>
9+
#include <linux/etherdevice.h>
10+
#include <linux/ethtool.h>
11+
#include <linux/i2c.h>
12+
#include <linux/io-64-nonatomic-lo-hi.h>
13+
#include <linux/module.h>
14+
#include <linux/netdevice.h>
15+
#include <linux/regmap.h>
16+
#include <linux/uaccess.h>
17+
#include <linux/phy/qsfp-mem.h>
18+
19+
#define CONF_OFF 0x20
20+
#define CONF_RST_MOD BIT(0)
21+
#define CONF_RST_CON BIT(1)
22+
#define CONF_MOD_SEL BIT(2)
23+
#define CONF_LOW_POW BIT(3)
24+
#define CONF_POLL_EN BIT(4)
25+
26+
#define STAT_OFF 0x28
27+
#define MODPRSL BIT(0)
28+
#define DELAY_REG 0x38
29+
#define DELAY_VALUE 0xffffff
30+
31+
#define I2C_CTRL 0x48
32+
#define I2C_CTRL_EN BIT(0)
33+
#define I2C_CTRL_BSP BIT(1)
34+
#define I2C_CTRL_FIFO GENMASK(3, 2)
35+
#define I2C_CTRL_FIFO_NOT_FULL 3
36+
37+
#define I2C_ISER 0x4c
38+
#define I2C_ISER_TXRDY BIT(0)
39+
#define I2C_ISER_RXRDY BIT(1)
40+
#define I2C_SCL_LOW 0x60
41+
#define COUNT_PERIOD_LOW 0x82
42+
#define I2C_SCL_HIGH 0x64
43+
#define COUNT_PERIOD_HIGH 0x3c
44+
#define I2C_SDA_HOLD 0x68
45+
#define COUNT_PERIOD_HOLD 0x28
46+
47+
#define QSFP_SHADOW_CSRS_BASE_OFF 0x100
48+
#define QSFP_SHADOW_CSRS_BASE_END 0x3f8
49+
50+
#define DELAY_US 1000
51+
52+
#define QSFP_CHECK_TIME 500
53+
54+
static const struct regmap_range qsfp_mem_regmap_range[] = {
55+
regmap_reg_range(CONF_OFF, STAT_OFF),
56+
regmap_reg_range(QSFP_SHADOW_CSRS_BASE_OFF, QSFP_SHADOW_CSRS_BASE_END),
57+
};
58+
59+
static const struct regmap_access_table qsfp_mem_access_table = {
60+
.yes_ranges = qsfp_mem_regmap_range,
61+
.n_yes_ranges = ARRAY_SIZE(qsfp_mem_regmap_range),
62+
};
63+
64+
static void qsfp_init_i2c(struct qsfp *qsfp)
65+
{
66+
writel(I2C_ISER_TXRDY | I2C_ISER_RXRDY, qsfp->base + I2C_ISER);
67+
writel(COUNT_PERIOD_LOW, qsfp->base + I2C_SCL_LOW);
68+
writel(COUNT_PERIOD_HIGH, qsfp->base + I2C_SCL_HIGH);
69+
writel(COUNT_PERIOD_HOLD, qsfp->base + I2C_SDA_HOLD);
70+
71+
writel(FIELD_PREP(I2C_CTRL_FIFO, I2C_CTRL_FIFO_NOT_FULL) |
72+
I2C_CTRL_EN | I2C_CTRL_BSP, qsfp->base + I2C_CTRL);
73+
}
74+
75+
static const struct regmap_config mmio_cfg = {
76+
.reg_bits = 64,
77+
.reg_stride = 8,
78+
.val_bits = 64,
79+
.fast_io = true,
80+
.rd_table = &qsfp_mem_access_table,
81+
.max_register = QSFP_SHADOW_CSRS_BASE_END,
82+
};
83+
84+
static void qsfp_init(struct qsfp *qsfp)
85+
{
86+
writeq(CONF_RST_MOD | CONF_RST_CON | CONF_MOD_SEL, qsfp->base + CONF_OFF);
87+
udelay(DELAY_US);
88+
writeq(CONF_MOD_SEL, qsfp->base + CONF_OFF);
89+
udelay(DELAY_US);
90+
91+
qsfp_init_i2c(qsfp);
92+
93+
udelay(DELAY_US);
94+
writeq(DELAY_VALUE, qsfp->base + DELAY_REG);
95+
96+
writeq(CONF_POLL_EN | CONF_MOD_SEL, qsfp->base + CONF_OFF);
97+
udelay(DELAY_US);
98+
}
99+
100+
int check_qsfp_plugin(struct qsfp *qsfp)
101+
{
102+
u64 status;
103+
104+
status = readq(qsfp->base + STAT_OFF);
105+
106+
return (!(status & MODPRSL));
107+
}
108+
EXPORT_SYMBOL_GPL(check_qsfp_plugin);
109+
110+
u32 qsfp_connected_show(struct qsfp *qsfp)
111+
{
112+
u32 plugin;
113+
114+
mutex_lock(&qsfp->lock);
115+
plugin = check_qsfp_plugin(qsfp) && (qsfp->init == QSFP_INIT_DONE);
116+
mutex_unlock(&qsfp->lock);
117+
118+
return plugin;
119+
}
120+
EXPORT_SYMBOL_GPL(qsfp_connected_show);
121+
122+
void qsfp_check_hotplug(struct work_struct *work)
123+
{
124+
struct delayed_work *dwork;
125+
struct qsfp *qsfp;
126+
u64 status;
127+
128+
dwork = to_delayed_work(work);
129+
qsfp = container_of(dwork, struct qsfp, dwork);
130+
131+
mutex_lock(&qsfp->lock);
132+
133+
status = readq(qsfp->base + STAT_OFF);
134+
dev_dbg(qsfp->dev, "qsfp status 0x%llx\n", status);
135+
136+
if (check_qsfp_plugin(qsfp) && qsfp->init == QSFP_INIT_RESET) {
137+
dev_info(qsfp->dev, "detected QSFP plugin\n");
138+
qsfp_init(qsfp);
139+
WRITE_ONCE(qsfp->init, QSFP_INIT_DONE);
140+
} else if (!check_qsfp_plugin(qsfp) &&
141+
qsfp->init == QSFP_INIT_DONE) {
142+
dev_info(qsfp->dev, "detected QSFP unplugin\n");
143+
WRITE_ONCE(qsfp->init, QSFP_INIT_RESET);
144+
}
145+
mutex_unlock(&qsfp->lock);
146+
147+
schedule_delayed_work(&qsfp->dwork, msecs_to_jiffies(QSFP_CHECK_TIME));
148+
}
149+
150+
int qsfp_init_work(struct qsfp *qsfp)
151+
{
152+
qsfp->init = QSFP_INIT_RESET;
153+
INIT_DELAYED_WORK(&qsfp->dwork, qsfp_check_hotplug);
154+
qsfp_check_hotplug(&qsfp->dwork.work);
155+
return 0;
156+
}
157+
EXPORT_SYMBOL_GPL(qsfp_init_work);
158+
159+
int qsfp_register_regmap(struct qsfp *qsfp)
160+
{
161+
struct device *dev = qsfp->dev;
162+
163+
qsfp->regmap = devm_regmap_init_mmio(dev, qsfp->base, &mmio_cfg);
164+
if (IS_ERR(qsfp->regmap))
165+
dev_err(dev, "Failed to create qsfp regmap\n");
166+
167+
return PTR_ERR_OR_ZERO(qsfp->regmap);
168+
}
169+
EXPORT_SYMBOL_GPL(qsfp_register_regmap);
170+
171+
void qsfp_remove_device(struct qsfp *qsfp)
172+
{
173+
writeq(CONF_MOD_SEL, qsfp->base + CONF_OFF);
174+
cancel_delayed_work_sync(&qsfp->dwork);
175+
}
176+
EXPORT_SYMBOL_GPL(qsfp_remove_device);
177+
MODULE_LICENSE("GPL");

drivers/net/phy/qsfp-mem-dfl.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/* Intel(R) Memory based QSFP driver For DFL based devices.
4+
*
5+
* Copyright (C) 2022 Intel Corporation. All rights reserved.
6+
*/
7+
#include <linux/dfl.h>
8+
#include <linux/phy/qsfp-mem.h>
9+
#include <linux/module.h>
10+
11+
ssize_t dfl_qsfp_connected_show(struct device *dev, struct device_attribute *attr, char *buf)
12+
{
13+
struct qsfp *qsfp = dev_get_drvdata(dev);
14+
u32 plugin = qsfp_connected_show(qsfp);
15+
16+
return sysfs_emit(buf, "%u\n", plugin);
17+
}
18+
19+
static DEVICE_ATTR_RO(dfl_qsfp_connected);
20+
21+
static struct attribute *qsfp_mem_attrs[] = {
22+
&dev_attr_dfl_qsfp_connected.attr,
23+
NULL,
24+
};
25+
ATTRIBUTE_GROUPS(qsfp_mem);
26+
27+
static int qsfp_dfl_probe(struct dfl_device *dfl_dev)
28+
{
29+
struct device *dev = &dfl_dev->dev;
30+
struct qsfp *qsfp;
31+
int ret = 0;
32+
33+
qsfp = devm_kzalloc(dev, sizeof(*qsfp), GFP_KERNEL);
34+
if (!qsfp)
35+
return -ENOMEM;
36+
37+
qsfp->base = devm_ioremap_resource(dev, &dfl_dev->mmio_res);
38+
if (!qsfp->base)
39+
return -ENOMEM;
40+
41+
qsfp->dev = dev;
42+
mutex_init(&qsfp->lock);
43+
44+
dev_set_drvdata(dev, qsfp);
45+
46+
ret = qsfp_init_work(qsfp);
47+
if (ret != 0) {
48+
dev_err(dev, "Failed to initialize delayed work to read QSFP\n");
49+
return ret;
50+
}
51+
return qsfp_register_regmap(qsfp);
52+
}
53+
54+
static void qsfp_dfl_remove(struct dfl_device *dfl_dev)
55+
{
56+
struct device *dev = &dfl_dev->dev;
57+
struct qsfp *qsfp = dev_get_drvdata(dev);
58+
59+
qsfp_remove_device(qsfp);
60+
mutex_destroy(&qsfp->lock);
61+
}
62+
63+
#define FME_FEATURE_ID_QSFP 0x13
64+
65+
static const struct dfl_device_id qsfp_ids[] = {
66+
{ FME_ID, FME_FEATURE_ID_QSFP },
67+
{ }
68+
};
69+
70+
static struct dfl_driver qsfp_driver = {
71+
.drv = {
72+
.name = "qsfp-mem",
73+
.dev_groups = qsfp_mem_groups,
74+
},
75+
.id_table = qsfp_ids,
76+
.probe = qsfp_dfl_probe,
77+
.remove = qsfp_dfl_remove,
78+
};
79+
80+
module_dfl_driver(qsfp_driver);
81+
MODULE_DEVICE_TABLE(dfl, qsfp_ids);
82+
MODULE_DESCRIPTION("Intel(R) Memory based QSFP DFL driver");
83+
MODULE_AUTHOR("Intel Corporation");
84+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)