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