Skip to content

Commit fc54111

Browse files
matthew-gerlachRuss Weight
authored andcommitted
net: phy: add skeleton of memory based QSFP controller.
Add the beginning of a driver for a memory based QSFP controller. Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
1 parent 1228cc5 commit fc54111

3 files changed

Lines changed: 216 additions & 0 deletions

File tree

drivers/net/phy/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ config SFP
6161
depends on HWMON || HWMON=n
6262
select MDIO_I2C
6363

64+
config QSFP_MEM
65+
tristate "Memory based QSFP support"
66+
depends on HWMON || HWMON=n
67+
help
68+
Adds support for a QSFP controller that shadows the QSFP module's
69+
memory pages in memory.
70+
6471
comment "MII PHY device drivers"
6572

6673
config AMD_PHY

drivers/net/phy/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ obj-$(CONFIG_SFP) += sfp.o
3030
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
3131
obj-y += $(sfp-obj-y) $(sfp-obj-m)
3232

33+
obj-$(CONFIG_QSFP_MEM) += qsfp-mem.o
34+
3335
obj-$(CONFIG_ADIN_PHY) += adin.o
3436
obj-$(CONFIG_AMD_PHY) += amd.o
3537
aquantia-objs += aquantia_main.o

drivers/net/phy/qsfp-mem.c

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/* Intel(R) Memory based QSFP driver.
4+
*
5+
* Copyright (C) 2020 Intel Corporation. All rights reserved.
6+
*/
7+
8+
#include <linux/bitfield.h>
9+
#include <linux/dfl.h>
10+
#include <linux/etherdevice.h>
11+
#include <linux/ethtool.h>
12+
#include <linux/i2c.h>
13+
#include <linux/io-64-nonatomic-lo-hi.h>
14+
#include <linux/module.h>
15+
#include <linux/netdevice.h>
16+
#include <linux/regmap.h>
17+
#include <linux/uaccess.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 STAT_MOD_PRES BIT(0)
28+
#define STAT_INT_MOD BIT(1)
29+
#define STAT_INT_I2C BIT(2)
30+
#define STAT_TX_ERR BIT(3)
31+
#define STAT_RX_ERR BIT(4)
32+
#define STAT_SNK_RDY BIT(5)
33+
#define STAT_SRC_RDY BIT(6)
34+
#define STAT_FSM_PAUSE BIT(7)
35+
#define STAT_CURR_PAGE GENMASK_ULL(15, 8)
36+
#define STAT_CURR_ADDR GENMASK_ULL(23, 16)
37+
38+
#define I2C_BASE_OFF 0x40
39+
#define I2C_TFR_CMD I2C_BASE_OFF
40+
#define I2C_RX_DATA (I2C_BASE_OFF + 0x8)
41+
#define I2C_CTRL (I2C_BASE_OFF + 0x10)
42+
#define I2C_CTRL_EN BIT(0)
43+
#define I2C_CTRL_BSP BIT(1)
44+
#define I2C_CTRL_TCT GENMASK_ULL(3, 2)
45+
#define I2C_CTRL_RXT GENMASK_ULL(5, 4)
46+
47+
#define I2C_ISER (I2C_BASE_OFF + 0x18)
48+
#define I2C_ISER_TXRDY BIT(0)
49+
#define I2C_ISER_RXRDY BIT(1)
50+
#define I2C_ISR (I2C_BASE_OFF + 0x20)
51+
#define I2C_STAT (I2C_BASE_OFF + 0x28)
52+
#define I2C_TC_FIFO_LVL (I2C_BASE_OFF + 0x30)
53+
#define I2C_RX_FIFO_LVL (I2C_BASE_OFF + 0x38)
54+
#define I2C_SCL_LOW (I2C_BASE_OFF + 0x40)
55+
#define I2C_SCL_HIGH (I2C_BASE_OFF + 0x48)
56+
#define I2C_SDA_HOLD (I2C_BASE_OFF + 0x50)
57+
58+
#define QSFP_SHADOW_CSRS_BASE_OFF 0x100
59+
60+
#define POLL_INTERVAL_US 1
61+
#define POLL_TIMEOUT_US 100000
62+
63+
#define RESET_DELAY_US 10
64+
65+
#define INPUT_CLK_FREQ_HZ 100000000
66+
#define BUS_CLK_FREQ_HZ I2C_MAX_STANDARD_MODE_FREQ
67+
68+
struct qsfp {
69+
void __iomem *base;
70+
struct regmap *regmap;
71+
};
72+
73+
static int qsfp_init_mod(struct device *dev, struct qsfp *qsfp)
74+
{
75+
u64 reg_val;
76+
int ret;
77+
78+
reg_val = readq(qsfp->base + STAT_OFF);
79+
if (!(reg_val & STAT_MOD_PRES)) {
80+
dev_err(dev, "No QSFP module present.");
81+
return -ENODEV;
82+
}
83+
84+
reg_val = readq(qsfp->base + CONF_OFF);
85+
if (reg_val & CONF_POLL_EN) {
86+
reg_val &= ~CONF_POLL_EN;
87+
writeq(reg_val, qsfp->base + CONF_OFF);
88+
89+
ret = readq_poll_timeout((qsfp->base + STAT_OFF), reg_val,
90+
(reg_val & STAT_FSM_PAUSE),
91+
POLL_INTERVAL_US, POLL_TIMEOUT_US);
92+
if (ret)
93+
dev_warn(dev, "Timed out waiting for fsm pause.");
94+
}
95+
96+
writeq(CONF_RST_MOD | CONF_RST_CON, qsfp->base + CONF_OFF);
97+
98+
udelay(RESET_DELAY_US);
99+
100+
writeq(0, qsfp->base + CONF_OFF);
101+
102+
udelay(RESET_DELAY_US);
103+
104+
writeq(CONF_MOD_SEL, qsfp->base + CONF_OFF);
105+
106+
return 0;
107+
}
108+
109+
static void qsfp_init_i2c(struct device *dev, struct qsfp *qsfp)
110+
{
111+
u64 divisor = INPUT_CLK_FREQ_HZ / I2C_MAX_STANDARD_MODE_FREQ;
112+
u64 clk_mhz = INPUT_CLK_FREQ_HZ / 1000000;
113+
u64 reg_val = FIELD_PREP(I2C_CTRL_TCT, 0x2) | FIELD_PREP(I2C_CTRL_RXT, 0x2);
114+
u64 t_high, t_low;
115+
116+
if (BUS_CLK_FREQ_HZ <= I2C_MAX_STANDARD_MODE_FREQ) {
117+
t_high = divisor * 1 / 2;
118+
t_low = divisor * 1 / 2;
119+
} else {
120+
reg_val |= I2C_CTRL_BSP;
121+
t_high = divisor * 1 / 3;
122+
t_low = divisor * 2 / 3;
123+
}
124+
125+
writeq(reg_val, qsfp->base + I2C_CTRL);
126+
127+
reg_val = readq(qsfp->base + I2C_CTRL);
128+
129+
reg_val |= I2C_CTRL_EN;
130+
131+
writeq(reg_val, qsfp->base + I2C_CTRL);
132+
133+
writeq(t_high, qsfp->base + I2C_SCL_HIGH);
134+
writeq(t_low, qsfp->base + I2C_SCL_LOW);
135+
/* SDA Hold Time, 300ns */
136+
writeq(3 * clk_mhz / 10, qsfp->base + I2C_SDA_HOLD);
137+
138+
writeq(I2C_ISER_TXRDY | I2C_CTRL_RXT, qsfp->base + I2C_ISER);
139+
}
140+
141+
static const struct regmap_config mmio_cfg = {
142+
.reg_bits = 64,
143+
.reg_stride = 8,
144+
.val_bits = 64,
145+
.fast_io = true,
146+
.max_register = 0x100 + (128 * 6),
147+
};
148+
149+
static int qsfp_probe(struct dfl_device *dfl_dev)
150+
{
151+
struct device *dev = &dfl_dev->dev;
152+
struct qsfp *qsfp;
153+
u64 reg_val;
154+
int ret;
155+
156+
qsfp = devm_kzalloc(dev, sizeof(*qsfp), GFP_KERNEL);
157+
if (!qsfp)
158+
return -ENOMEM;
159+
160+
dev_set_drvdata(dev, qsfp);
161+
162+
qsfp->base = devm_ioremap_resource(dev, &dfl_dev->mmio_res);
163+
if (!qsfp->base)
164+
return -ENOMEM;
165+
166+
ret = qsfp_init_mod(dev, qsfp);
167+
if (ret)
168+
return ret;
169+
170+
qsfp_init_i2c(dev, qsfp);
171+
172+
reg_val = readq(qsfp->base + CONF_OFF);
173+
reg_val |= CONF_POLL_EN;
174+
writeq(reg_val, qsfp->base + CONF_OFF);
175+
176+
qsfp->regmap = devm_regmap_init_mmio(dev, qsfp->base, &mmio_cfg);
177+
if (IS_ERR(qsfp->regmap))
178+
dev_err(dev, "Failed to create qsfp regmap\n");
179+
180+
return PTR_ERR_OR_ZERO(qsfp->regmap);
181+
}
182+
183+
static void qsfp_remove(struct dfl_device *dfl_dev)
184+
{
185+
}
186+
187+
#define FME_FEATURE_ID_QSFP 0x13
188+
189+
static const struct dfl_device_id qsfp_ids[] = {
190+
{ FME_ID, FME_FEATURE_ID_QSFP },
191+
{ }
192+
};
193+
194+
static struct dfl_driver qsfp_driver = {
195+
.drv = {
196+
.name = "qsfp-mem",
197+
},
198+
.id_table = qsfp_ids,
199+
.probe = qsfp_probe,
200+
.remove = qsfp_remove,
201+
};
202+
203+
module_dfl_driver(qsfp_driver);
204+
MODULE_DEVICE_TABLE(dfl, qsfp_ids);
205+
MODULE_DESCRIPTION("Intel(R) Memory based QSFP driver");
206+
MODULE_AUTHOR("Intel Corporation");
207+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)