|
| 1 | +From d654fd07f90bda8565ebbb18979dfee8d8fff3da Mon Sep 17 00:00:00 2001 |
| 2 | +From: Wataru Ishida < [email protected]> |
| 3 | +Date: Wed, 2 Apr 2025 07:10:10 +0000 |
| 4 | +Subject: [PATCH] optoe: Add CMIS Bank support for transceivers with >8 lanes |
| 5 | + |
| 6 | +This patch adds CMIS Bank support to the 'optoe3' device class in order |
| 7 | +to enable access to CMIS transceivers with more than 8 lanes (e.g., OSFP-XD, CPO OEs). |
| 8 | + |
| 9 | +- Bank support is enabled only for the 'optoe3' dev class. |
| 10 | +- The default bank size is set to 4, and can be modified via the 'bank_size' sysfs entry. |
| 11 | +- For 'optoe3', the 'write_max' value is updated to 2 to comply with CMIS requirements, |
| 12 | + which mandate that both bank and page values be updated in a single WRITE operation. |
| 13 | + |
| 14 | +Signed-off-by: Wataru Ishida < [email protected]> |
| 15 | +--- |
| 16 | + drivers/misc/eeprom/optoe.c | 142 ++++++++++++++++++++++++++++-------- |
| 17 | + 1 file changed, 112 insertions(+), 30 deletions(-) |
| 18 | + |
| 19 | +diff --git a/drivers/misc/eeprom/optoe.c b/drivers/misc/eeprom/optoe.c |
| 20 | +index 22d2c0cd4..f5be0d9b7 100644 |
| 21 | +--- a/drivers/misc/eeprom/optoe.c |
| 22 | ++++ b/drivers/misc/eeprom/optoe.c |
| 23 | +@@ -150,6 +150,12 @@ struct optoe_platform_data { |
| 24 | + |
| 25 | + /* fundamental unit of addressing for EEPROM */ |
| 26 | + #define OPTOE_PAGE_SIZE 128 |
| 27 | ++ |
| 28 | ++#define OPTOE_DEFAULT_BANK_SIZE 4 |
| 29 | ++#define OPTOE_MAX_SUPPORTED_BANK_SIZE 8 |
| 30 | ++#define OPTOE_NON_BANKED_PAGE_SIZE 16 /* page 00h-0Fh are not banked */ |
| 31 | ++#define OPTOE_BANKED_PAGE_SIZE 240 /* page 10h-FFh are banked */ |
| 32 | ++ |
| 33 | + /* |
| 34 | + * Single address devices (eg QSFP) have 256 pages, plus the unpaged |
| 35 | + * low 128 bytes. If the device does not support paging, it is |
| 36 | +@@ -168,6 +174,7 @@ struct optoe_platform_data { |
| 37 | + #define TWO_ADDR_NO_0X51_SIZE (2 * OPTOE_PAGE_SIZE) |
| 38 | + |
| 39 | + /* a few constants to find our way around the EEPROM */ |
| 40 | ++#define OPTOE_BANK_SELECT_REG 0x7E |
| 41 | + #define OPTOE_PAGE_SELECT_REG 0x7F |
| 42 | + #define ONE_ADDR_PAGEABLE_REG 0x02 |
| 43 | + #define QSFP_NOT_PAGEABLE (1<<2) |
| 44 | +@@ -195,6 +202,7 @@ struct optoe_data { |
| 45 | + u8 *writebuf; |
| 46 | + unsigned int write_max; |
| 47 | + unsigned int write_timeout; |
| 48 | ++ unsigned int bank_size; /* 0 means bank is not supported */ |
| 49 | + |
| 50 | + unsigned int num_addresses; |
| 51 | + |
| 52 | +@@ -245,13 +253,19 @@ static const struct i2c_device_id optoe_ids[] = { |
| 53 | + }; |
| 54 | + MODULE_DEVICE_TABLE(i2c, optoe_ids); |
| 55 | + |
| 56 | ++static uint32_t one_addr_eeprom_size_with_bank(uint32_t bank_size) |
| 57 | ++{ |
| 58 | ++ bank_size = bank_size == 0 ? 1 : bank_size; |
| 59 | ++ return (bank_size * OPTOE_BANKED_PAGE_SIZE + OPTOE_NON_BANKED_PAGE_SIZE + 1) * OPTOE_PAGE_SIZE; |
| 60 | ++} |
| 61 | ++ |
| 62 | + /*-------------------------------------------------------------------------*/ |
| 63 | + /* |
| 64 | + * This routine computes the addressing information to be used for |
| 65 | + * a given r/w request. |
| 66 | + * |
| 67 | + * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), |
| 68 | +- * the page, and the offset. |
| 69 | ++ * the bank, the page, and the offset. |
| 70 | + * |
| 71 | + * Handles both single address (eg QSFP) and two address (eg SFP). |
| 72 | + * For SFP, offset 0-255 are on client[0], >255 is on client[1] |
| 73 | +@@ -274,7 +288,7 @@ MODULE_DEVICE_TABLE(i2c, optoe_ids); |
| 74 | + */ |
| 75 | + |
| 76 | + static uint8_t optoe_translate_offset(struct optoe_data *optoe, |
| 77 | +- loff_t *offset, struct i2c_client **client) |
| 78 | ++ loff_t *offset, struct i2c_client **client, uint8_t *bank) |
| 79 | + { |
| 80 | + unsigned int page = 0; |
| 81 | + |
| 82 | +@@ -301,8 +315,9 @@ static uint8_t optoe_translate_offset(struct optoe_data *optoe, |
| 83 | + page = (*offset >> 7)-1; |
| 84 | + /* 0x80 places the offset in the top half, offset is last 7 bits */ |
| 85 | + *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f); |
| 86 | +- |
| 87 | +- return page; /* note also returning client and offset */ |
| 88 | ++ *bank = page < OPTOE_PAGE_SIZE ? 0 : (page - OPTOE_NON_BANKED_PAGE_SIZE) / OPTOE_BANKED_PAGE_SIZE; |
| 89 | ++ page = page - *bank * OPTOE_BANKED_PAGE_SIZE; |
| 90 | ++ return page; /* note also returning client, bank and offset */ |
| 91 | + } |
| 92 | + |
| 93 | + static ssize_t optoe_eeprom_read(struct optoe_data *optoe, |
| 94 | +@@ -511,21 +526,38 @@ static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, |
| 95 | + { |
| 96 | + struct i2c_client *client; |
| 97 | + ssize_t retval = 0; |
| 98 | +- uint8_t page = 0; |
| 99 | ++ uint8_t page = 0, bank = 0; |
| 100 | + loff_t phy_offset = off; |
| 101 | + int ret = 0; |
| 102 | + |
| 103 | +- page = optoe_translate_offset(optoe, &phy_offset, &client); |
| 104 | ++ page = optoe_translate_offset(optoe, &phy_offset, &client, &bank); |
| 105 | + dev_dbg(&client->dev, |
| 106 | +- "%s off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", |
| 107 | +- __func__, off, page, phy_offset, (long int) count, opcode); |
| 108 | ++ "%s off %lld bank:%d page:%d phy_offset:%lld, count:%ld, opcode:%d\n", |
| 109 | ++ __func__, off, bank, page, phy_offset, (long int) count, opcode); |
| 110 | + if (page > 0) { |
| 111 | +- ret = optoe_eeprom_write(optoe, client, &page, |
| 112 | +- OPTOE_PAGE_SELECT_REG, 1); |
| 113 | ++ /* |
| 114 | ++ * CMIS 5.3 8.2.15 Page Mapping |
| 115 | ++ * |
| 116 | ++ * For an arbitrary Page Address change or for just a Bank Index change, |
| 117 | ++ * a host must write both BankSelect and PageSelect in one WRITE access, |
| 118 | ++ * even if the PageSelect value does not change. |
| 119 | ++ * The module does not begin processing the BankSelect value until after the PageSelect register has been written. |
| 120 | ++ * |
| 121 | ++ * For just a Page Index change (mapping another Page in the current Bank), |
| 122 | ++ * or for mapping an arbitrary unbanked Page to Upper Memory, a host may WRITE only the PageSelect Byte. |
| 123 | ++ */ |
| 124 | ++ if (bank > 0) { |
| 125 | ++ char buf[2] = {bank, page}; |
| 126 | ++ ret = optoe_eeprom_write(optoe, client, buf, |
| 127 | ++ OPTOE_BANK_SELECT_REG, 2); |
| 128 | ++ } else { |
| 129 | ++ ret = optoe_eeprom_write(optoe, client, &page, |
| 130 | ++ OPTOE_PAGE_SELECT_REG, 1); |
| 131 | ++ } |
| 132 | + if (ret < 0) { |
| 133 | + dev_dbg(&client->dev, |
| 134 | +- "Write page register for page %d failed ret:%d!\n", |
| 135 | +- page, ret); |
| 136 | ++ "Write page register for bank %d, page %d failed ret:%d!\n", |
| 137 | ++ bank, page, ret); |
| 138 | + return ret; |
| 139 | + } |
| 140 | + } |
| 141 | +@@ -552,14 +584,14 @@ static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, |
| 142 | + } |
| 143 | + |
| 144 | + |
| 145 | +- if (page > 0) { |
| 146 | +- /* return the page register to page 0 (why?) */ |
| 147 | +- page = 0; |
| 148 | +- ret = optoe_eeprom_write(optoe, client, &page, |
| 149 | +- OPTOE_PAGE_SELECT_REG, 1); |
| 150 | ++ if (page > 0 || bank > 0) { |
| 151 | ++ /* return the page register to bank 0, page 0 (why?) */ |
| 152 | ++ char buf[2] = {0, 0}; |
| 153 | ++ ret = optoe_eeprom_write(optoe, client, buf, |
| 154 | ++ OPTOE_BANK_SELECT_REG, 2); |
| 155 | + if (ret < 0) { |
| 156 | + dev_err(&client->dev, |
| 157 | +- "Restore page register to 0 failed:%d!\n", ret); |
| 158 | ++ "Restore bank, page register to (0, 0) failed:%d!\n", ret); |
| 159 | + /* error only if nothing has been transferred */ |
| 160 | + if (retval == 0) |
| 161 | + retval = ret; |
| 162 | +@@ -622,8 +654,10 @@ static ssize_t optoe_page_legal(struct optoe_data *optoe, |
| 163 | + /* if no pages needed, we're good */ |
| 164 | + if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE) |
| 165 | + return len; |
| 166 | ++ |
| 167 | ++ maxlen = one_addr_eeprom_size_with_bank(optoe->bank_size); |
| 168 | + /* if offset exceeds possible pages, we're not good */ |
| 169 | +- if (off >= ONE_ADDR_EEPROM_SIZE) |
| 170 | ++ if (off >= maxlen) |
| 171 | + return OPTOE_EOF; |
| 172 | + /* in between, are pages supported? */ |
| 173 | + status = optoe_eeprom_read(optoe, client, ®val, |
| 174 | +@@ -665,7 +699,7 @@ static ssize_t optoe_page_legal(struct optoe_data *optoe, |
| 175 | + maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off; |
| 176 | + } else { |
| 177 | + /* Pages supported, trim len to the end of pages */ |
| 178 | +- maxlen = ONE_ADDR_EEPROM_SIZE - off; |
| 179 | ++ maxlen = maxlen - off; |
| 180 | + } |
| 181 | + len = (len > maxlen) ? maxlen : len; |
| 182 | + dev_dbg(&client->dev, |
| 183 | +@@ -995,6 +1029,44 @@ static ssize_t set_port_name(struct device *dev, |
| 184 | + return count; |
| 185 | + } |
| 186 | + |
| 187 | ++static ssize_t show_bank_size(struct device *dev, |
| 188 | ++ struct device_attribute *dattr, char *buf) |
| 189 | ++{ |
| 190 | ++ struct i2c_client *client = to_i2c_client(dev); |
| 191 | ++ struct optoe_data *optoe = i2c_get_clientdata(client); |
| 192 | ++ ssize_t count; |
| 193 | ++ |
| 194 | ++ mutex_lock(&optoe->lock); |
| 195 | ++ count = sprintf(buf, "%u\n", optoe->bank_size); |
| 196 | ++ mutex_unlock(&optoe->lock); |
| 197 | ++ |
| 198 | ++ return count; |
| 199 | ++} |
| 200 | ++ |
| 201 | ++static ssize_t set_bank_size(struct device *dev, |
| 202 | ++ struct device_attribute *attr, |
| 203 | ++ const char *buf, size_t count) |
| 204 | ++{ |
| 205 | ++ struct i2c_client *client = to_i2c_client(dev); |
| 206 | ++ struct optoe_data *optoe = i2c_get_clientdata(client); |
| 207 | ++ unsigned int bank_size; |
| 208 | ++ |
| 209 | ++ // setting bank size is only supported for the CMIS device |
| 210 | ++ if (optoe->dev_class != CMIS_ADDR) { |
| 211 | ++ return -EINVAL; |
| 212 | ++ } |
| 213 | ++ |
| 214 | ++ if (kstrtouint(buf, 0, &bank_size) != 0 || |
| 215 | ++ bank_size < 0 || bank_size > OPTOE_MAX_SUPPORTED_BANK_SIZE) |
| 216 | ++ return -EINVAL; |
| 217 | ++ |
| 218 | ++ mutex_lock(&optoe->lock); |
| 219 | ++ optoe->bank_size = bank_size; |
| 220 | ++ mutex_unlock(&optoe->lock); |
| 221 | ++ |
| 222 | ++ return count; |
| 223 | ++} |
| 224 | ++ |
| 225 | + static DEVICE_ATTR(port_name, 0644, show_port_name, set_port_name); |
| 226 | + #endif /* if NOT defined EEPROM_CLASS, the common case */ |
| 227 | + |
| 228 | +@@ -1003,6 +1075,7 @@ static DEVICE_ATTR(write_timeout, 0644, show_dev_write_timeout_size, |
| 229 | + static DEVICE_ATTR(write_max, 0644, show_dev_write_max_size, |
| 230 | + set_dev_write_max_size); |
| 231 | + static DEVICE_ATTR(dev_class, 0644, show_dev_class, set_dev_class); |
| 232 | ++static DEVICE_ATTR(bank_size, 0644, show_bank_size, set_bank_size); |
| 233 | + |
| 234 | + static struct attribute *optoe_attrs[] = { |
| 235 | + #ifndef EEPROM_CLASS |
| 236 | +@@ -1011,6 +1084,7 @@ static struct attribute *optoe_attrs[] = { |
| 237 | + &dev_attr_write_timeout.attr, |
| 238 | + &dev_attr_write_max.attr, |
| 239 | + &dev_attr_dev_class.attr, |
| 240 | ++ &dev_attr_bank_size.attr, |
| 241 | + NULL, |
| 242 | + }; |
| 243 | + |
| 244 | +@@ -1027,6 +1101,17 @@ static int optoe_probe(struct i2c_client *client, |
| 245 | + struct optoe_data *optoe; |
| 246 | + int num_addresses = 0; |
| 247 | + char port_name[MAX_PORT_NAME_LEN]; |
| 248 | ++ /* |
| 249 | ++ * NOTE: AN-2079 |
| 250 | ++ * Finisar recommends that the host implement 1 byte writes |
| 251 | ++ * only since this module only supports 32 byte page boundaries. |
| 252 | ++ * 2 byte writes are acceptable for PE and Vout changes per |
| 253 | ++ * Application Note AN-2071. |
| 254 | ++ * |
| 255 | ++ * use 2 bytes for CMIS devices since CMIS 5.3 requires write both BankSelect and PageSlect in one WRITE acess. |
| 256 | ++ * see the comment in optoe_eeprom_update_client(). |
| 257 | ++ */ |
| 258 | ++ unsigned int write_max; |
| 259 | + |
| 260 | + if (client->addr != 0x50) { |
| 261 | + dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", |
| 262 | +@@ -1097,19 +1182,25 @@ static int optoe_probe(struct i2c_client *client, |
| 263 | + (strcmp(client->name, "sff8436") == 0)) { |
| 264 | + /* one-address (eg QSFP) family */ |
| 265 | + optoe->dev_class = ONE_ADDR; |
| 266 | ++ optoe->bank_size = 0; |
| 267 | + chip.byte_len = ONE_ADDR_EEPROM_SIZE; |
| 268 | + num_addresses = 1; |
| 269 | ++ write_max = 1; |
| 270 | + } else if ((strcmp(client->name, "optoe2") == 0) || |
| 271 | + (strcmp(client->name, "24c04") == 0)) { |
| 272 | + /* SFP family */ |
| 273 | + optoe->dev_class = TWO_ADDR; |
| 274 | ++ optoe->bank_size = 0; |
| 275 | + chip.byte_len = TWO_ADDR_EEPROM_SIZE; |
| 276 | + num_addresses = 2; |
| 277 | ++ write_max = 1; |
| 278 | + } else if (strcmp(client->name, "optoe3") == 0) { |
| 279 | + /* CMIS spec */ |
| 280 | + optoe->dev_class = CMIS_ADDR; |
| 281 | +- chip.byte_len = ONE_ADDR_EEPROM_SIZE; |
| 282 | ++ optoe->bank_size = OPTOE_DEFAULT_BANK_SIZE; |
| 283 | ++ chip.byte_len = one_addr_eeprom_size_with_bank(OPTOE_MAX_SUPPORTED_BANK_SIZE); |
| 284 | + num_addresses = 1; |
| 285 | ++ write_max = 2; |
| 286 | + } else { /* those were the only choices */ |
| 287 | + err = -EINVAL; |
| 288 | + goto exit; |
| 289 | +@@ -1139,15 +1230,6 @@ static int optoe_probe(struct i2c_client *client, |
| 290 | + I2C_FUNC_SMBUS_WRITE_WORD_DATA) || |
| 291 | + i2c_check_functionality(client->adapter, |
| 292 | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { |
| 293 | +- /* |
| 294 | +- * NOTE: AN-2079 |
| 295 | +- * Finisar recommends that the host implement 1 byte writes |
| 296 | +- * only since this module only supports 32 byte page boundaries. |
| 297 | +- * 2 byte writes are acceptable for PE and Vout changes per |
| 298 | +- * Application Note AN-2071. |
| 299 | +- */ |
| 300 | +- unsigned int write_max = 1; |
| 301 | +- |
| 302 | + optoe->bin.write = optoe_bin_write; |
| 303 | + optoe->bin.attr.mode |= 0200; |
| 304 | + |
| 305 | +-- |
| 306 | +2.25.1 |
| 307 | + |
0 commit comments