On Mon, Jun 25, 2018 at 3:35 PM, Loic Poulain <loic.poulain@xxxxxxxxxx> wrote: > Most of FTDI's devices have an EEPROM which records FTDI devices > configuration setting (e.g. the VID, PID, I/O config...) and user > data. For example, FT232R and FTX chips have 128-byte and 2048-byte > internal EEPROM respectively. > > This patch adds support for FTDI EEPROM read/write via USB control > transfers and register a new nvm device to the nvmem core. > > This permits to expose the EEPROM as a sysfs file, allowing userspace > to read/modify FTDI configuration and its user data without having to > rely on a specific userspace USB driver. > > Moreover, any upcoming new tentative to add CBUS GPIO support could > integrate CBUS EEPROM configuration reading in order to determine > which of the CBUS pins are available as GPIO. > FWIW, Reviewed-by: Andy Shevchenko <andy.shevchenko@xxxxxxxxx> One nit below, though. > Signed-off-by: Loic Poulain <loic.poulain@xxxxxxxxxx> > --- > v2: Use ifdef instead of IS_ENABLED > error message in case of nvmem registering failure > Fix space/tab in Kconfig > v3: Make nvmem a child of the usb dev instead of the serial port > Add macros defining eeprom sizes > Check read/write size is a nultiple of the eeprom word-size > Remove useless change in Kconfig > v4: Reword commit message > Remove default-yes from Kconfig > Change includes ordering > Use default linux size defines > Use get_unaligned_le16 helper > Prepend EEPROM functions with ftdi_ > Error message in ftdi_eeprom_register() > v5: Fix missing linux/sizes header > > drivers/usb/serial/Kconfig | 10 ++++ > drivers/usb/serial/ftdi_sio.c | 119 ++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/serial/ftdi_sio.h | 28 ++++++++++ > 3 files changed, 157 insertions(+) > > diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig > index 533f127..5747562 100644 > --- a/drivers/usb/serial/Kconfig > +++ b/drivers/usb/serial/Kconfig > @@ -181,6 +181,16 @@ config USB_SERIAL_FTDI_SIO > To compile this driver as a module, choose M here: the > module will be called ftdi_sio. > > +config USB_SERIAL_FTDI_SIO_NVMEM > + bool "FTDI MTP non-volatile memory support" > + depends on USB_SERIAL_FTDI_SIO > + select NVMEM > + help > + Say yes here to add support for the MTP non-volatile memory > + present on FTDI. Most of FTDI's devices have an EEPROM which > + records FTDI device's configuration setting as well as user > + data. > + > config USB_SERIAL_VISOR > tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver" > help > diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c > index 7ea221d..75a35a8 100644 > --- a/drivers/usb/serial/ftdi_sio.c > +++ b/drivers/usb/serial/ftdi_sio.c > @@ -37,9 +37,13 @@ > #include <linux/spinlock.h> > #include <linux/mutex.h> > #include <linux/uaccess.h> > +#include <linux/sizes.h> > +#include <linux/nvmem-provider.h> > #include <linux/usb.h> > #include <linux/serial.h> > #include <linux/usb/serial.h> > +#include <asm/unaligned.h> > + I would put it rather as follows +#include <linux/nvmem-provider.h> ... // something else is still in order here? +#include <linux/sizes.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/serial.h> #include <linux/usb/serial.h> + +#include <asm/unaligned.h> + // also note another blank line as divider. > #include "ftdi_sio.h" > #include "ftdi_sio_ids.h" > > @@ -73,6 +77,8 @@ struct ftdi_private { > unsigned int latency; /* latency setting in use */ > unsigned short max_packet_size; > struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */ > + > + struct nvmem_device *eeprom; > }; > > /* struct ftdi_sio_quirk is used by devices requiring special attention. */ > @@ -1529,6 +1535,112 @@ static int get_lsr_info(struct usb_serial_port *port, > return 0; > } > > +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM > + > +static int ftdi_write_eeprom(void *priv, unsigned int off, void *val, > + size_t bytes) > +{ > + struct usb_serial_port *port = priv; > + struct usb_device *udev = port->serial->dev; > + unsigned char *buf = val; Btw, not sure why you need this now... > + > + if (bytes % 2) /* 16-bit eeprom */ > + return -EINVAL; > + > + while (bytes) { > + int rv; > + > + rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), > + FTDI_SIO_WRITE_EEPROM_REQUEST, > + FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE, > + get_unaligned_le16(buf), off / 2, NULL, > + 0, WDR_TIMEOUT); > + if (rv < 0) > + return rv; > + > + off += 2; > + buf += 2; > + bytes -= 2; > + } > + > + return 0; > +} > + > +static int ftdi_read_eeprom(void *priv, unsigned int off, void *val, > + size_t bytes) > +{ > + struct usb_serial_port *port = priv; > + struct usb_device *udev = port->serial->dev; > + unsigned char *buf = val; Ditto. > + > + if (bytes % 2) /* 16-bit eeprom */ > + return -EINVAL; > + > + while (bytes) { > + int rv; > + > + rv = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), > + FTDI_SIO_READ_EEPROM_REQUEST, > + FTDI_SIO_READ_EEPROM_REQUEST_TYPE, > + 0, off / 2, buf, 2, WDR_TIMEOUT); > + if (rv < 0) > + return rv; > + > + off += 2; > + buf += 2; > + bytes -= 2; > + } > + > + return 0; > +} > + > +static int ftdi_register_eeprom(struct usb_serial_port *port) > +{ > + struct ftdi_private *priv = usb_get_serial_port_data(port); > + struct usb_device *udev = port->serial->dev; > + struct nvmem_config nvmconf = {}; > + > + switch (priv->chip_type) { > + case FTX: > + nvmconf.size = SZ_2K; > + break; > + case FT232RL: > + nvmconf.size = SZ_128; > + break; > + default: > + return 0; > + } > + > + nvmconf.word_size = 2; > + nvmconf.stride = 2; > + nvmconf.read_only = false; > + nvmconf.priv = port; > + nvmconf.dev = &udev->dev; > + nvmconf.reg_read = ftdi_read_eeprom; > + nvmconf.reg_write = ftdi_write_eeprom; > + nvmconf.owner = THIS_MODULE; > + > + priv->eeprom = nvmem_register(&nvmconf); > + if (IS_ERR(priv->eeprom)) { > + dev_err(&udev->dev, "Unable to register FTDI EEPROM\n"); > + priv->eeprom = NULL; > + return -ENOMEM; > + } > + > + dev_info(&udev->dev, "Registered %d-byte FTDI EEPROM\n", nvmconf.size); > + > + return 0; > +} > + > +static void ftdi_unregister_eeprom(struct usb_serial_port *port) > +{ > + struct ftdi_private *priv = usb_get_serial_port_data(port); > + > + if (priv->eeprom) > + nvmem_unregister(priv->eeprom); > +} > + > +#endif /* CONFIG_USB_SERIAL_FTDI_SIO_NVMEM */ > > /* Determine type of FTDI chip based on USB config and descriptor. */ > static void ftdi_determine_type(struct usb_serial_port *port) > @@ -1814,6 +1926,10 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) > priv->latency = 16; > write_latency_timer(port); > create_sysfs_attrs(port); > +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM > + ftdi_register_eeprom(port); > +#endif > + > return 0; > } > > @@ -1931,6 +2047,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) > { > struct ftdi_private *priv = usb_get_serial_port_data(port); > > +#ifdef CONFIG_USB_SERIAL_FTDI_SIO_NVMEM > + ftdi_unregister_eeprom(port); > +#endif > remove_sysfs_attrs(port); > > kfree(priv); > diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h > index dcd0b6e..9eab961 100644 > --- a/drivers/usb/serial/ftdi_sio.h > +++ b/drivers/usb/serial/ftdi_sio.h > @@ -36,6 +36,8 @@ > #define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ > #define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ > #define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */ > +#define FTDI_SIO_READ_EEPROM 0x90 /* Read eeprom */ > +#define FTDI_SIO_WRITE_EEPROM 0x91 /* Write eeprom */ > > /* Interface indices for FT2232, FT2232H and FT4232H devices */ > #define INTERFACE_A 1 > @@ -400,6 +402,32 @@ enum ftdi_sio_baudrate { > * > */ > > + /* FTDI_SIO_READ_EEPROM */ > +#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 > +#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM > +/* > + * BmRequestType: 1100 0000b > + * bRequest: FTDI_SIO_READ_EEPROM > + * wValue: 0 > + * wIndex: Word Index > + * wLength: 2 > + * Data: return data (a word) > + * > + */ > + > +/* FTDI_SIO_WRITE_EEPROM */ > +#define FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE 0x40 > +#define FTDI_SIO_WRITE_EEPROM_REQUEST FTDI_SIO_WRITE_EEPROM > +/* > + * BmRequestType: 0100 0000b > + * bRequest: FTDI_SIO_WRITE_EEPROM > + * wValue: Data (word) > + * wIndex: Word Index > + * wLength: 0 > + * Data: None > + * > + */ > + > /* FTDI_SIO_GET_MODEM_STATUS */ > /* Retrieve the current value of the modem status register */ > > -- > 2.7.4 > -- With Best Regards, Andy Shevchenko -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html