diff options
Diffstat (limited to 'drivers/mfd/rave-sp.c')
-rw-r--r-- | drivers/mfd/rave-sp.c | 118 |
1 files changed, 110 insertions, 8 deletions
diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c index 5c858e784a89..36dcd98977d6 100644 --- a/drivers/mfd/rave-sp.c +++ b/drivers/mfd/rave-sp.c @@ -45,7 +45,9 @@ #define RAVE_SP_DLE 0x10 #define RAVE_SP_MAX_DATA_SIZE 64 -#define RAVE_SP_CHECKSUM_SIZE 2 /* Worst case scenario on RDU2 */ +#define RAVE_SP_CHECKSUM_8B2C 1 +#define RAVE_SP_CHECKSUM_CCITT 2 +#define RAVE_SP_CHECKSUM_SIZE RAVE_SP_CHECKSUM_CCITT /* * We don't store STX, ETX and unescaped bytes, so Rx is only * DATA + CSUM @@ -160,6 +162,8 @@ struct rave_sp_variant { * @variant: Device variant specific information * @event_notifier_list: Input event notification chain * + * @part_number_firmware: Firmware version + * @part_number_bootloader: Bootloader version */ struct rave_sp { struct serdev_device *serdev; @@ -171,8 +175,40 @@ struct rave_sp { const struct rave_sp_variant *variant; struct blocking_notifier_head event_notifier_list; + + const char *part_number_firmware; + const char *part_number_bootloader; }; +struct rave_sp_version { + u8 hardware; + __le16 major; + u8 minor; + u8 letter[2]; +} __packed; + +struct rave_sp_status { + struct rave_sp_version bootloader_version; + struct rave_sp_version firmware_version; + u16 rdu_eeprom_flag; + u16 dds_eeprom_flag; + u8 pic_flag; + u8 orientation; + u32 etc; + s16 temp[2]; + u8 backlight_current[3]; + u8 dip_switch; + u8 host_interrupt; + u16 voltage_28; + u8 i2c_device_status; + u8 power_status; + u8 general_status; + u8 deprecated1; + u8 power_led_status; + u8 deprecated2; + u8 periph_power_shutoff; +} __packed; + static bool rave_sp_id_is_event(u8 code) { return (code & 0xF0) == RAVE_SP_EVNT_BASE; @@ -275,8 +311,8 @@ static int rave_sp_write(struct rave_sp *sp, const u8 *data, u8 data_size) length = dest - frame; - print_hex_dump(KERN_DEBUG, "rave-sp tx: ", DUMP_PREFIX_NONE, - 16, 1, frame, length, false); + print_hex_dump_debug("rave-sp tx: ", DUMP_PREFIX_NONE, + 16, 1, frame, length, false); return serdev_device_write(sp->serdev, frame, length, HZ); } @@ -415,10 +451,15 @@ static void rave_sp_receive_frame(struct rave_sp *sp, const size_t payload_length = length - checksum_length; const u8 *crc_reported = &data[payload_length]; struct device *dev = &sp->serdev->dev; - u8 crc_calculated[checksum_length]; + u8 crc_calculated[RAVE_SP_CHECKSUM_SIZE]; + + if (unlikely(checksum_length > sizeof(crc_calculated))) { + dev_warn(dev, "Checksum too long, dropping\n"); + return; + } - print_hex_dump(KERN_DEBUG, "rave-sp rx: ", DUMP_PREFIX_NONE, - 16, 1, data, length, false); + print_hex_dump_debug("rave-sp rx: ", DUMP_PREFIX_NONE, + 16, 1, data, length, false); if (unlikely(length <= checksum_length)) { dev_warn(dev, "Dropping short frame\n"); @@ -512,8 +553,6 @@ static int rave_sp_receive_buf(struct serdev_device *serdev, /* FALLTHROUGH */ case RAVE_SP_EXPECT_ESCAPED_DATA: - deframer->data[deframer->length++] = byte; - if (deframer->length == sizeof(deframer->data)) { dev_warn(dev, "Bad frame: Too long\n"); /* @@ -528,6 +567,8 @@ static int rave_sp_receive_buf(struct serdev_device *serdev, goto reset_framer; } + deframer->data[deframer->length++] = byte; + /* * We've extracted out special byte, now we * can go back to regular data collecting @@ -609,6 +650,52 @@ static int rave_sp_default_cmd_translate(enum rave_sp_command command) } } +static const char *devm_rave_sp_version(struct device *dev, + struct rave_sp_version *version) +{ + /* + * NOTE: The format string below uses %02d to display u16 + * intentionally for the sake of backwards compatibility with + * legacy software. + */ + return devm_kasprintf(dev, GFP_KERNEL, "%02d%02d%02d.%c%c\n", + version->hardware, + le16_to_cpu(version->major), + version->minor, + version->letter[0], + version->letter[1]); +} + +static int rave_sp_get_status(struct rave_sp *sp) +{ + struct device *dev = &sp->serdev->dev; + u8 cmd[] = { + [0] = RAVE_SP_CMD_STATUS, + [1] = 0 + }; + struct rave_sp_status status; + const char *version; + int ret; + + ret = rave_sp_exec(sp, cmd, sizeof(cmd), &status, sizeof(status)); + if (ret) + return ret; + + version = devm_rave_sp_version(dev, &status.firmware_version); + if (!version) + return -ENOMEM; + + sp->part_number_firmware = version; + + version = devm_rave_sp_version(dev, &status.bootloader_version); + if (!version) + return -ENOMEM; + + sp->part_number_bootloader = version; + + return 0; +} + static const struct rave_sp_checksum rave_sp_checksum_8b2c = { .length = 1, .subroutine = csum_8b2c, @@ -657,6 +744,7 @@ static const struct serdev_device_ops rave_sp_serdev_device_ops = { static int rave_sp_probe(struct serdev_device *serdev) { struct device *dev = &serdev->dev; + const char *unknown = "unknown\n"; struct rave_sp *sp; u32 baud; int ret; @@ -689,6 +777,20 @@ static int rave_sp_probe(struct serdev_device *serdev) serdev_device_set_baudrate(serdev, baud); + ret = rave_sp_get_status(sp); + if (ret) { + dev_warn(dev, "Failed to get firmware status: %d\n", ret); + sp->part_number_firmware = unknown; + sp->part_number_bootloader = unknown; + } + + /* + * Those strings already have a \n embedded, so there's no + * need to have one in format string. + */ + dev_info(dev, "Firmware version: %s", sp->part_number_firmware); + dev_info(dev, "Bootloader version: %s", sp->part_number_bootloader); + return devm_of_platform_populate(dev); } |