blob: 48654ed5e54031d7eee12eb39cb9a1fb5b82b159 [file] [log] [blame]
/*
* Wacom Penabled Driver for I2C
*
* Copyright (c) 2011-2014 Tatsunosuke Tobita, Wacom.
* <tobita.tatsunosuke@wacom.co.jp>
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version of 2 of the License,
* or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "wacom.h"
#include "w9019_flash.h"
bool wacom_i2c_set_feature(struct wacom_i2c *wac_i2c, u8 report_id, unsigned int buf_size, u8 *data,
u16 cmdreg, u16 datareg)
{
int i, ret = -1;
int total = SFEATURE_SIZE + buf_size;
u8 *sFeature = NULL;
bool bRet = false;
sFeature = kzalloc(sizeof(u8) * total, GFP_KERNEL);
if (!sFeature) {
printk(KERN_DEBUG"%s cannot preserve memory \n", __func__);
goto out;
}
memset(sFeature, 0, sizeof(u8) * total);
sFeature[0] = (u8)(cmdreg & 0x00ff);
sFeature[1] = (u8)((cmdreg & 0xff00) >> 8);
sFeature[2] = (RTYPE_FEATURE << 4) | report_id;
sFeature[3] = CMD_SET_FEATURE;
sFeature[4] = (u8)(datareg & 0x00ff);
sFeature[5] = (u8)((datareg & 0xff00) >> 8);
if ( (buf_size + 2) > 255) {
sFeature[6] = (u8)((buf_size + 2) & 0x00ff);
sFeature[7] = (u8)(( (buf_size + 2) & 0xff00) >> 8);
} else {
sFeature[6] = (u8)(buf_size + 2);
sFeature[7] = (u8)(0x00);
}
for (i = 0; i < buf_size; i++)
sFeature[i + SFEATURE_SIZE] = *(data + i);
// ret = wacom_i2c_master_send(client, sFeature, total, WACOM_FLASH_W9014);
ret = wacom_i2c_send(wac_i2c, sFeature, total, WACOM_I2C_MODE_BOOT);
if (ret != total) {
printk(KERN_DEBUG "Sending Set_Feature failed sent bytes: %d \n", ret);
goto err;
}
usleep_range(60, 61);
bRet = true;
err:
kfree(sFeature);
sFeature = NULL;
out:
return bRet;
}
bool wacom_i2c_get_feature(struct wacom_i2c *wac_i2c, u8 report_id, unsigned int buf_size, u8 *data,
u16 cmdreg, u16 datareg, int delay)
{
int ret = -1;
u8 *recv = NULL;
bool bRet = false;
u8 gFeature[] = {
(u8)(cmdreg & 0x00ff),
(u8)((cmdreg & 0xff00) >> 8),
(RTYPE_FEATURE << 4) | report_id,
CMD_GET_FEATURE,
(u8)(datareg & 0x00ff),
(u8)((datareg & 0xff00) >> 8)
};
/*"+ 2", adding 2 more spaces for organizeing again later in the passed data, "data"*/
recv = kzalloc(sizeof(u8) * (buf_size + 0), GFP_KERNEL);
if (!recv) {
printk(KERN_DEBUG"%s cannot preserve memory \n", __func__);
goto out;
}
memset(recv, 0, sizeof(u8) * (buf_size + 0)); /*Append 2 bytes for length low and high of the byte*/
// ret = wacom_i2c_master_send(client, gFeature, GFEATURE_SIZE, WACOM_FLASH_W9014);
ret = wacom_i2c_send(wac_i2c, gFeature, GFEATURE_SIZE, WACOM_I2C_MODE_BOOT);
if (ret != GFEATURE_SIZE) {
printk(KERN_NOTICE"%s Sending Get_Feature failed; sent bytes: %d \n", __func__, ret);
goto err;
}
udelay(delay);
// ret = wacom_i2c_master_recv(client, recv, (buf_size), WACOM_FLASH_W9014);
ret = wacom_i2c_recv(wac_i2c, recv, buf_size,WACOM_I2C_MODE_BOOT);
if (ret != buf_size) {
printk(KERN_NOTICE"%s Receiving data failed; recieved bytes: %d \n", __func__, ret);
goto err;
}
/*Coppy data pointer, subtracting the first two bytes of the length*/
memcpy(data, (recv + 0), buf_size);
bRet = true;
err:
kfree(recv);
recv = NULL;
out:
return bRet;
}
static int wacom_flash_cmd(struct wacom_i2c *wac_i2c)
{
u8 command[10];
int len = 0;
int ret = -1;
command[len++] = 0x0d;
command[len++] = FLASH_START0;
command[len++] = FLASH_START1;
command[len++] = FLASH_START2;
command[len++] = FLASH_START3;
command[len++] = FLASH_START4;
command[len++] = FLASH_START5;
command[len++] = 0x0d;
// ret = i2c_master_send(wac_i2c->client, command, len);
ret = wacom_i2c_send(wac_i2c, command, len, WACOM_I2C_MODE_BOOT);
if(ret < 0){
printk("Sending flash command failed\n");
return -EXIT_FAIL;
}
msleep(300);
return 0;
}
int flash_query_w9014(struct wacom_i2c *wac_i2c)
{
bool bRet = false;
u8 command[CMD_SIZE];
u8 response[RSP_SIZE];
int ECH, len = 0;
command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */
command[len++] = BOOT_QUERY; /* Report:Boot Query command */
command[len++] = ECH = 7; /* Report:echo */
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("%s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, RSP_SIZE, response, COMM_REG, DATA_REG, (10 * 1000));
if (!bRet) {
printk("%s failed to get feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if ( (response[3] != QUERY_CMD) ||
(response[4] != ECH) ) {
printk("%s res3:%x res4:%x \n", __func__, response[3], response[4]);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if (response[5] != QUERY_RSP) {
printk("%s res5:%x \n", __func__, response[5]);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
printk("QUERY SUCCEEDED \n");
return 0;
}
static bool flash_blver_w9014(struct wacom_i2c *wac_i2c, int *blver)
{
bool bRet = false;
u8 command[CMD_SIZE];
u8 response[RSP_SIZE];
int ECH, len = 0;
command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */
command[len++] = BOOT_BLVER; /* Report:Boot Version command */
command[len++] = ECH = 7; /* Report:echo */
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("%s failed to set feature1\n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, RSP_SIZE, response, COMM_REG, DATA_REG, (10 * 1000));
if (!bRet) {
printk("%s 2 failed to set feature\n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if ( (response[3] != BOOT_CMD) ||
(response[4] != ECH) ) {
printk("%s res3:%x res4:%x \n", __func__, response[3], response[4]);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if (response[5] != QUERY_RSP) {
printk("%s res5:%x \n", __func__, response[5]);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
*blver = (int)response[5];
return true;
}
static bool flash_mputype_w9014(struct wacom_i2c *wac_i2c, int* pMpuType)
{
bool bRet = false;
u8 command[CMD_SIZE];
u8 response[RSP_SIZE];
int ECH, len = 0;
command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */
command[len++] = BOOT_MPU; /* Report:Boot Query command */
command[len++] = ECH = 7; /* Report:echo */
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("%s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, RSP_SIZE, response, COMM_REG, DATA_REG, (10 * 1000));
if (!bRet) {
printk("%s failed to get feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if ( (response[3] != MPU_CMD) ||
(response[4] != ECH) ) {
printk("%s res3:%x res4:%x \n", __func__, response[3], response[4]);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
*pMpuType = (int)response[5];
return true;
}
static bool flash_end_w9014(struct wacom_i2c *wac_i2c)
{
bool bRet = false;
u8 command[CMD_SIZE];
int ECH, len = 0;
command[len++] = BOOT_CMD_REPORT_ID;
command[len++] = BOOT_EXIT;
command[len++] = ECH = 7;
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("%s failed to set feature 1\n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
return true;
}
#ifdef PARTIAL_ERASE
static bool erase_datamem(struct wacom_i2c *wac_i2c)
{
bool bRet = false;
u8 command[CMD_SIZE];
u8 response[BOOT_RSP_SIZE];
unsigned char sum = 0;
unsigned char cmd_chksum;
int ECH, j;
int len = 0;
command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */
command[len++] = BOOT_ERASE_DATAMEM; /* Report:erase datamem command */
command[len++] = ECH = BOOT_ERASE_DATAMEM; /* Report:echo */
command[len++] = DATAMEM_SECTOR0; /* Report:erased block No. */
/*Preliminarily store the data that cannnot appear here, but in wacom_set_feature()*/
sum = 0;
sum += 0x05;
sum += 0x07;
for (j = 0; j < 4; j++)
sum += command[j];
cmd_chksum = ~sum + 1; /* Report:check sum */
command[len++] = cmd_chksum;
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("epen - %s failed to set feature 1 \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
udelay(50);
do {
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, BOOT_RSP_SIZE, response, COMM_REG, DATA_REG, 50);
if (!bRet) {
printk("%s failed to get feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if ((response[3] != 0x0e || response[4] != ECH) || (response[5] != 0xff && response[5] != 0x00)) {
printk("epen - %s failing resp3: %x resp4: %x resp5: %x \n",
__func__, response[3], response[4], response[5]);
return false;
}
} while (response[3] == 0x0e && response[4] == ECH && response[5] == 0xff);
return true;
}
static bool erase_codemem(struct wacom_i2c *wac_i2c, int *eraseBlock, int num)
{
bool bRet = false;
u8 command[CMD_SIZE];
u8 response[BOOT_RSP_SIZE];
unsigned char sum = 0;
unsigned char cmd_chksum;
int ECH, len = 0;
int i, j;
for (i = 0; i < num; i++) {
len = 0;
command[len++] = BOOT_CMD_REPORT_ID; /* Report:ReportID */
command[len++] = BOOT_ERASE_FLASH; /* Report:erase command */
command[len++] = ECH = i; /* Report:echo */
command[len++] = *eraseBlock; /* Report:erased block No. */
eraseBlock++;
/*Preliminarily store the data that cannnot appear here, but in wacom_set_feature()*/
sum = 0;
sum += 0x05;
sum += 0x07;
for (j = 0; j < 4; j++)
sum += command[j];
cmd_chksum = ~sum + 1; /* Report:check sum */
command[len++] = cmd_chksum;
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("epen - %s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
udelay(50);
do {
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, BOOT_RSP_SIZE, response, COMM_REG, DATA_REG, 50);
if (!bRet) {
printk("epen - %s failed to get feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if ((response[3] != 0x00 || response[4] != ECH) || (response[5] != 0xff && response[5] != 0x00)) {
printk("epen - %s failing resp3: %x resp4: %x resp5: %x \n",
__func__, response[3], response[4], response[5]);
return false;
}
} while (response[3] == 0x00 && response[4] == ECH && response[5] == 0xff);
}
return true;
}
static bool flash_erase_w9019(struct wacom_i2c *wac_i2c, int *eraseBlock, int num)
{
bool ret;
ret = erase_datamem(wac_i2c);
if (!ret) {
printk("epen - %s erasing datamem failed \n", __func__);
return false;
}
ret = erase_codemem(wac_i2c, eraseBlock, num);
if (!ret) {
printk("epen - %s erasing codemem failed \n", __func__);
return false;
}
return true;
}
#else
static bool flash_erase_all(struct wacom_i2c *wac_i2c)
{
bool bRet = false;
u8 command[BOOT_CMD_SIZE];
u8 response[BOOT_RSP_SIZE];
int i, len = 0;
int ECH, sum = 0;
command[len++] = 7;
command[len++] = 16;
command[len++] = ECH = 2;
command[len++] = 3;
/*Preliminarily store the data that cannnot appear here, but in wacom_set_feature()*/
sum += 0x05;
sum += 0x07;
for (i = 0; i < len; i++)
sum += command[i];
command[len++] = ~sum + 1;
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, len, command, COMM_REG, DATA_REG);
if (!bRet) {
printk("%s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
do {
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, BOOT_RSP_SIZE, response, COMM_REG, DATA_REG, 0);
if (!bRet) {
printk("%s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if (!(response[3] & 0x10) || !(response[4] & ECH) ||
(!(response[5] & 0xff) && response[5] & 0x00)) {
printk("%s failing 4 resp1: %x resp2: %x resp3: %x \n",
__func__, response[3], response[4], response[5]);
return false;
}
mdelay(200);
} while(response[3] == 0x10 && response[4] == ECH && response[5] == 0xff);
return true;
}
#endif
static bool flash_write_block_w9014(struct wacom_i2c *wac_i2c, char *flash_data,
unsigned long ulAddress, u8 *pcommand_id, int *ECH)
{
const int MAX_COM_SIZE = (8 + FLASH_BLOCK_SIZE + 2); //8: num of command[0] to command[7]
//FLASH_BLOCK_SIZE: unit to erase the block
//Num of Last 2 checksums
bool bRet = false;
u8 command[300];
unsigned char sum = 0;
int i;
command[0] = BOOT_CMD_REPORT_ID; /* Report:ReportID */
command[1] = BOOT_WRITE_FLASH; /* Report:program command */
command[2] = *ECH = ++(*pcommand_id); /* Report:echo */
command[3] = ulAddress & 0x000000ff;
command[4] = (ulAddress & 0x0000ff00) >> 8;
command[5] = (ulAddress & 0x00ff0000) >> 16;
command[6] = (ulAddress & 0xff000000) >> 24; /* Report:address(4bytes) */
command[7] = 8; /* Report:size(8*8=64) */
/*Preliminarily store the data that cannnot appear here, but in wacom_set_feature()*/
sum = 0;
sum += 0x05;
sum += 0x4c;
for (i = 0; i < 8; i++)
sum += command[i];
command[MAX_COM_SIZE - 2] = ~sum + 1; /* Report:command checksum */
sum = 0;
for (i = 8; i < (FLASH_BLOCK_SIZE + 8); i++){
command[i] = flash_data[ulAddress+(i - 8)];
sum += flash_data[ulAddress+(i - 8)];
}
command[MAX_COM_SIZE - 1] = ~sum+1; /* Report:data checksum */
/*Subtract 8 for the first 8 bytes*/
bRet = wacom_i2c_set_feature(wac_i2c, REPORT_ID_1, (BOOT_CMD_SIZE + 4 - 8), command, COMM_REG, DATA_REG);
if (!bRet) {
printk("%s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
udelay(50);
return true;
}
static bool flash_write_w9014(struct wacom_i2c *wac_i2c, unsigned char *flash_data,
unsigned long start_address, unsigned long *max_address)
{
bool bRet = false;
u8 command_id = 0;
u8 response[BOOT_RSP_SIZE];
int i, j, ECH = 0, ECH_len = 0;
int ECH_ARRAY[3];
unsigned long ulAddress;
j = 0;
for (ulAddress = start_address; ulAddress < *max_address; ulAddress += FLASH_BLOCK_SIZE) {
for (i = 0; i < FLASH_BLOCK_SIZE; i++) {
if (flash_data[ulAddress+i] != 0xFF)
break;
}
if (i == (FLASH_BLOCK_SIZE))
continue;
/* for debug */
//printk(KERN_DEBUG"epen:write data %#x\n", (unsigned int)ulAddress);
bRet = flash_write_block_w9014(wac_i2c, flash_data, ulAddress, &command_id, &ECH);
if(!bRet)
return false;
if (ECH_len == 3)
ECH_len = 0;
ECH_ARRAY[ECH_len++] = ECH;
if (ECH_len == 3) {
for (j = 0; j < 3; j++) {
do {
bRet = wacom_i2c_get_feature(wac_i2c, REPORT_ID_2, BOOT_RSP_SIZE, response, COMM_REG, DATA_REG, 50);
if (!bRet) {
printk("%s failed to set feature \n", __func__);
return -EXIT_FAIL_SEND_QUERY_COMMAND;
}
if ((response[3] != 0x01 || response[4] != ECH_ARRAY[j]) || (response[5] != 0xff && response[5] != 0x00)) {
printk("%s mismatched echo array \n", __func__);
// printk("addr: %x res:%x \n", ulAddress, response[5]);
return false;
}
} while (response[3] == 0x01 && response[4] == ECH_ARRAY[j] && response[5] == 0xff);
}
}
}
return true;
}
int wacom_i2c_flash_w9014(struct wacom_i2c *wac_i2c, unsigned char *fw_data)
{
bool bRet = false;
int result, i;
int eraseBlock[200], eraseBlockNum;
int iBLVer = 0, iMpuType = 0;
unsigned long max_address = 0; /* Max.address of Load data */
unsigned long start_address = 0x2000; /* Start.address of Load data */
/*Obtain boot loader version*/
if (!flash_blver_w9014(wac_i2c, &iBLVer)) {
printk("epen : %s failed to get Boot Loader version \n", __func__);
return -EXIT_FAIL_GET_BOOT_LOADER_VERSION;
}
printk("epen : BL version: %x \n", iBLVer);
/*Obtain MPU type: this can be manually done in user space*/
if (!flash_mputype_w9014(wac_i2c, &iMpuType)) {
printk("epen : %s failed to get MPU type \n", __func__);
return -EXIT_FAIL_GET_MPU_TYPE;
}
// if (iMpuType != MPU_W9014 || iMpuType != MPU_W9019) {
if (iMpuType != MPU_W9019) {
printk("epen : MPU is not suitable : %x \n", iMpuType);
return -EXIT_FAIL_GET_MPU_TYPE;
}
printk("epen : MPU type: %x \n", iMpuType);
/*-----------------------------------*/
/*Flashing operation starts from here*/
/*Set start and end address and block numbers*/
eraseBlockNum = 0;
start_address = W9014_START_ADDR;
max_address = W9014_END_ADDR;
for (i = BLOCK_NUM; i >= 8; i--) {
eraseBlock[eraseBlockNum] = i;
eraseBlockNum++;
}
msleep(300);
/*Erase the old program*/
printk("epen - %s erasing the current firmware \n", __func__);
#ifdef PARTIAL_ERASE
bRet = flash_erase_w9019(wac_i2c, eraseBlock, eraseBlockNum);
if (!bRet) {
printk("epen - %s failed to erase the user program \n", __func__);
result = -EXIT_FAIL_ERASE;
goto fail;
}
#else
bRet = flash_erase_all(wac_i2c);
if (!bRet) {
printk("epen - %s failed to erase the user program \n", __func__);
result = -EXIT_FAIL_ERASE;
goto fail;
}
#endif
/*Write the new program*/
printk(KERN_DEBUG"epen:%s writing new firmware \n", __func__);
bRet = flash_write_w9014(wac_i2c, fw_data, start_address, &max_address);
if (!bRet) {
printk("epen : %s failed to write firmware \n", __func__);
result = -EXIT_FAIL_WRITE_FIRMWARE;
goto fail;
}
/*Return to the user mode*/
printk("epen : %s closing the boot mode \n", __func__);
bRet = flash_end_w9014(wac_i2c);
if (!bRet) {
printk("epen : %s closing boot mode failed \n", __func__);
result = -EXIT_FAIL_WRITING_MARK_NOT_SET;
goto fail;
}
printk("epen : %s write and verify completed \n", __func__);
result = EXIT_OK;
fail:
return result;
}
int wacom_i2c_flash(struct wacom_i2c *wac_i2c)
{
int ret;
if (wac_i2c->fw_data == NULL) {
printk(KERN_ERR "epen:Data is NULL. Exit.\n");
return -1;
}
wacom_compulsory_flash_mode(wac_i2c, true);
wacom_reset_hw(wac_i2c);
msleep(200);
ret = wacom_flash_cmd(wac_i2c);
if (ret < 0) {
printk(KERN_NOTICE"epen:%s cannot send flash command \n", __func__);
}
printk(KERN_NOTICE"epen:%s pass wacom_flash_cmd \n", __func__);
ret = flash_query_w9014(wac_i2c);
if(ret < 0) {
printk(KERN_NOTICE"epen:%s Error: cannot send query \n", __func__);
ret = -EXIT_FAIL;
goto end_wacom_flash;
}
printk(KERN_NOTICE"epen:%s pass flash_query_w9014 \n", __func__);
ret = wacom_i2c_flash_w9014(wac_i2c, wac_i2c->fw_data);
if (ret < 0) {
printk(KERN_NOTICE"epen:%s Error: flash failed \n", __func__);
ret = -EXIT_FAIL;
goto end_wacom_flash;
}
msleep(200);
end_wacom_flash:
wacom_compulsory_flash_mode(wac_i2c, false);
wacom_reset_hw(wac_i2c);
msleep(200);
return ret;
}