// SPDX-License-Identifier: GPL-2.0+ /* * Driver for ChipOne icn8505 i2c touchscreen controller * * Copyright (c) 2015-2018 Red Hat Inc. * * Red Hat authors: * Hans de Goede <hdegoede@redhat.com>
*/
/* ICN8505_REG_POWER commands */ #define ICN8505_POWER_ACTIVE 0x00 #define ICN8505_POWER_MONITOR 0x01 #define ICN8505_POWER_HIBERNATE 0x02 /* * The Android driver uses these to turn on/off the charger filter, but the * filter is way too aggressive making e.g. onscreen keyboards unusable.
*/ #define ICN8505_POWER_ENA_CHARGER_MODE 0x55 #define ICN8505_POWER_DIS_CHARGER_MODE 0x66
/* * Note this function uses a number of magic register addresses and values, * there are deliberately no defines for these because the algorithm is taken * from the icn85xx Android driver and I do not want to make up possibly wrong * names for the addresses and/or values.
*/ staticint icn8505_try_fw_upload(struct icn8505_data *icn8505, conststruct firmware *fw)
{ struct device *dev = &icn8505->client->dev;
size_t offset, count; int error;
u8 buf[4];
u32 crc;
/* Put the controller in programming mode */
error = icn8505_write_prog_reg(icn8505, 0xcc3355, 0x5a); if (error) return error;
usleep_range(2000, 5000);
error = icn8505_write_prog_reg(icn8505, 0x040400, 0x01); if (error) return error;
usleep_range(2000, 5000);
error = icn8505_read_prog_data(icn8505, 0x040002, buf, 1); if (error) return error;
if (buf[0] != 0x85) {
dev_err(dev, "Failed to enter programming mode\n"); return -ENODEV;
}
/* Get and check length and CRC */
error = icn8505_read_prog_data(icn8505, 0x40034, buf, 2); if (error) return error;
if (get_unaligned_le16(buf) != fw->size) {
dev_warn(dev, "Length mismatch after uploading fw\n"); return -EIO;
}
error = icn8505_read_prog_data(icn8505, 0x4002c, buf, 4); if (error) return error;
crc = crc32_be(0, fw->data, fw->size); if (get_unaligned_le32(buf) != crc) {
dev_warn(dev, "CRC mismatch after uploading fw\n"); return -EIO;
}
/* Boot controller from SRAM */
error = icn8505_write_prog_reg(icn8505, 0x40400, 0x03); if (error) return error;
usleep_range(2000, 5000); return 0;
}
staticint icn8505_upload_fw(struct icn8505_data *icn8505)
{ struct device *dev = &icn8505->client->dev; conststruct firmware *fw; int i, error;
/* * Always load the firmware, even if we don't need it at boot, we * we may need it at resume. Having loaded it once will make the * firmware class code cache it at suspend/resume.
*/
error = firmware_request_platform(&fw, icn8505->firmware_name, dev); if (error) {
dev_err(dev, "Firmware request error %d\n", error); return error;
}
/* Check if the controller is not already up and running */ if (icn8505_read_reg_silent(icn8505, 0x000a) == 0x85) goto success;
for (i = 1; i <= MAX_FW_UPLOAD_TRIES; i++) {
error = icn8505_try_fw_upload(icn8505, fw); if (!error) goto success;
dev_err(dev, "Failed to upload firmware: %d (attempt %d/%d)\n",
error, i, MAX_FW_UPLOAD_TRIES);
usleep_range(2000, 5000);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.