in drivers/misc/sfp/amzn-sfp.c [88:304]
static ssize_t amzn_sfp_rw(struct bin_attribute *ba, char *buf, loff_t ofs,
size_t len, u16 flags)
{
struct amzn_sfp_softc *sc = ba->private;
struct i2c_client *client = sc->client;
char iobuf[AMZN_SFP_HALF_SIZE + 1];
struct i2c_msg msg[2];
unsigned long ts;
int error, nmsgs;
u16 addr;
u8 reg;
/* Make sure the offset and length are valid. */
if (ofs < 0 || ofs >= ba->size)
return -ESPIPE;
if (len == 0)
return -EINVAL;
if (ofs + len > ba->size)
return -ENOSPC;
addr = client->addr;
rt_mutex_lock(&sc->lock);
switch (sc->sfp_type) {
case AMZN_SFP_TYPE_SFP_PLUS:
/*
* Handle I2C address auto-increment for DOM access.
* Never cross-over between different I2C addresses!
*/
addr += ofs / AMZN_SFP_FULL_SIZE;
reg = ofs % AMZN_SFP_FULL_SIZE;
if (len + reg > AMZN_SFP_FULL_SIZE)
len = AMZN_SFP_FULL_SIZE - reg;
break;
case AMZN_SFP_TYPE_QSFP_PLUS:
case AMZN_SFP_TYPE_QSFP28:
case AMZN_SFP_TYPE_QSFP_DD:
/*
* Handle paging of the upper half. Never cross-over
* into different pages or from lower into upper half.
* The lower half starts at offset 0. Page 0 of the
* upper half starts at offset 128. Upper half page 1
* starts at offset 256. Upper half page X starts at
* offset 128 + X * 128.
*/
if (ofs < AMZN_SFP_HALF_SIZE) {
/* Lower half: don't cross-over into upper half. */
reg = ofs;
if (len + reg > AMZN_SFP_HALF_SIZE)
len = AMZN_SFP_HALF_SIZE - reg;
break;
}
/* Upper half */
/* Prepare the buffer for writing page select. */
iobuf[0] = AMZN_QSFP_PAGE_SELECT;
iobuf[1] = (ofs / AMZN_SFP_HALF_SIZE) - 1;
/* Calculate the offset on the page. */
reg = (ofs % AMZN_SFP_HALF_SIZE) + AMZN_SFP_HALF_SIZE;
/* Stay within the page size (which starts at 128). */
if (len + reg > AMZN_SFP_FULL_SIZE)
len = AMZN_SFP_FULL_SIZE - reg;
/*
* Ok, so now we have to be careful. We can't just
* blindly write the page select register. DACs in
* particular barf on that (NAK the data write).
* This even applies to writing a value 0 to select
* page 0. As such, we should only write to the
* page select register when necessary.
* So, what we need to do is read the current value
* of the page select register and only select the
* page if the current page is different from the
* desired page. But this would add overhead for
* each and every upper page access.
* To prevent the extra overhead, we cache the page
* in memory and give it a 1 second retention. As
* such, we only read the page select register once
* a second to determine the active upper page.
* The retention is needed because modules are hot-
* pluggable. We don't have the knowledge in this
* driver to know if the module we're talking to
* hasn't been replaced.
*/
ts = sc->cur_page_ts + amzn_sfp_page_retention * HZ;
if (sc->cur_page == -1 ||
!time_in_range(jiffies, sc->cur_page_ts, ts)) {
/*
* Read the page select register and update our
* notion of the current page. Read the value
* in iobuf[2] to preserve the desired page in
* iobuf[1].
*/
nmsgs = 0;
msg[nmsgs].addr = addr;
msg[nmsgs].flags = 0;
msg[nmsgs].len = 1;
msg[nmsgs].buf = &iobuf[0];
nmsgs++;
msg[nmsgs].addr = addr;
msg[nmsgs].flags = I2C_M_RD;
msg[nmsgs].len = 1;
msg[nmsgs].buf = &iobuf[2];
nmsgs++;
error = i2c_transfer(client->adapter, msg, nmsgs);
if (error < 0) {
/* Don't trust our state. */
sc->cur_page = -1;
rt_mutex_unlock(&sc->lock);
return error;
}
if (iobuf[2] != sc->cur_page) {
if (sc->cur_page != -1)
dev_notice(&sc->client->dev,
"resetting current page to %u"
" (was %u)\n", iobuf[2],
sc->cur_page);
sc->cur_page = iobuf[2];
}
sc->cur_page_ts = jiffies;
}
/*
* Don't write the page select register if the desired
* page is the same as the current page.
*/
if (iobuf[1] == sc->cur_page)
break;
/*
* Write the page select register. We have a lock on
* the device, so we know the page can not be changed
* between now and when we access the page.
*/
nmsgs = 0;
msg[nmsgs].addr = addr;
msg[nmsgs].flags = 0;
msg[nmsgs].len = 2;
msg[nmsgs].buf = iobuf;
nmsgs++;
error = i2c_transfer(client->adapter, msg, nmsgs);
if (error < 0) {
/* Don't trust our state. */
sc->cur_page = -1;
rt_mutex_unlock(&sc->lock);
return error;
}
sc->cur_page = iobuf[1];
sc->cur_page_ts = jiffies;
break;
default:
/*
* Unknown SFP type, we simply give access to the full
* EEPROM, without any knowledge about. You get what
* you get...
*/
reg = ofs;
break;
}
/*
* The SPI-I2C controller has a limited buffer
* size (96 bytes) and the driver simply returns EOPNOTSUPP when
* a larger transfer is requested. Bad driver!
*/
if (len > 64)
len = 64;
nmsgs = 0;
if (flags == I2C_M_RD) {
msg[nmsgs].addr = addr;
msg[nmsgs].flags = 0;
msg[nmsgs].len = 1;
msg[nmsgs].buf = ®
nmsgs++;
msg[nmsgs].addr = addr;
msg[nmsgs].flags = flags;
msg[nmsgs].len = len;
msg[nmsgs].buf = buf;
nmsgs++;
} else {
iobuf[0] = reg;
memcpy(iobuf + 1, buf, len);
msg[nmsgs].addr = addr;
msg[nmsgs].flags = 0;
msg[nmsgs].len = len + 1;
msg[nmsgs].buf = iobuf;
nmsgs++;
}
/* Writing to page select byte and immediate read to that same page
* cause certain modules to hang. Wait 4 - 5ms for modules to load
* upper page eeprom
*/
ts = sc->cur_page_ts + msecs_to_jiffies(amzn_sfp_page_load_wait_ms);
if (time_in_range(jiffies, sc->cur_page_ts, ts)) {
ts = amzn_sfp_page_load_wait_ms * 1000;
usleep_range(ts, ts + 1000);
}
error = i2c_transfer(client->adapter, msg, nmsgs);
rt_mutex_unlock(&sc->lock);
if (error < 0)
return error;
if (error != nmsgs)
return -EPIPE;
return (ssize_t)len;
}