| /* | 
 |  * Intel Wireless UWB Link 1480 | 
 |  * PHY parameters upload | 
 |  * | 
 |  * Copyright (C) 2005-2006 Intel Corporation | 
 |  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU General Public License version | 
 |  * 2 as published by the Free Software Foundation. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
 |  * 02110-1301, USA. | 
 |  * | 
 |  * | 
 |  * Code for uploading the PHY parameters to the PHY through the UWB | 
 |  * Radio Control interface. | 
 |  * | 
 |  * We just send the data through the MPI interface using HWA-like | 
 |  * commands and then reset the PHY to make sure it is ok. | 
 |  */ | 
 | #include <linux/delay.h> | 
 | #include <linux/device.h> | 
 | #include <linux/firmware.h> | 
 | #include <linux/usb/wusb.h> | 
 | #include "i1480-dfu.h" | 
 |  | 
 |  | 
 | /** | 
 |  * Write a value array to an address of the MPI interface | 
 |  * | 
 |  * @i1480:	Device descriptor | 
 |  * @data:	Data array to write | 
 |  * @size:	Size of the data array | 
 |  * @returns:	0 if ok, < 0 errno code on error. | 
 |  * | 
 |  * The data array is organized into pairs: | 
 |  * | 
 |  * ADDRESS VALUE | 
 |  * | 
 |  * ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has | 
 |  * to be a multiple of three. | 
 |  */ | 
 | static | 
 | int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size) | 
 | { | 
 | 	int result; | 
 | 	struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf; | 
 | 	struct i1480_evt_confirm *reply = i1480->evt_buf; | 
 |  | 
 | 	BUG_ON(size > 480); | 
 | 	result = -ENOMEM; | 
 | 	cmd->rccb.bCommandType = i1480_CET_VS1; | 
 | 	cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE); | 
 | 	cmd->size = cpu_to_le16(size); | 
 | 	memcpy(cmd->data, data, size); | 
 | 	reply->rceb.bEventType = i1480_CET_VS1; | 
 | 	reply->rceb.wEvent = i1480_CMD_MPI_WRITE; | 
 | 	result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply)); | 
 | 	if (result < 0) | 
 | 		goto out; | 
 | 	if (reply->bResultCode != UWB_RC_RES_SUCCESS) { | 
 | 		dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n", | 
 | 			reply->bResultCode); | 
 | 		result = -EIO; | 
 | 	} | 
 | out: | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | /** | 
 |  * Read a value array to from an address of the MPI interface | 
 |  * | 
 |  * @i1480:	Device descriptor | 
 |  * @data:	where to place the read array | 
 |  * @srcaddr:	Where to read from | 
 |  * @size:	Size of the data read array | 
 |  * @returns:	0 if ok, < 0 errno code on error. | 
 |  * | 
 |  * The command data array is organized into pairs ADDR0 ADDR1..., and | 
 |  * the returned data in ADDR0 VALUE0 ADDR1 VALUE1... | 
 |  * | 
 |  * We generate the command array to be a sequential read and then | 
 |  * rearrange the result. | 
 |  * | 
 |  * We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply. | 
 |  * | 
 |  * As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount | 
 |  * of values we can read is (512 - sizeof(*reply)) / 3 | 
 |  */ | 
 | static | 
 | int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size) | 
 | { | 
 | 	int result; | 
 | 	struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf; | 
 | 	struct i1480_evt_mpi_read *reply = i1480->evt_buf; | 
 | 	unsigned cnt; | 
 |  | 
 | 	memset(i1480->cmd_buf, 0x69, 512); | 
 | 	memset(i1480->evt_buf, 0x69, 512); | 
 |  | 
 | 	BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3); | 
 | 	result = -ENOMEM; | 
 | 	cmd->rccb.bCommandType = i1480_CET_VS1; | 
 | 	cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ); | 
 | 	cmd->size = cpu_to_le16(3*size); | 
 | 	for (cnt = 0; cnt < size; cnt++) { | 
 | 		cmd->data[cnt].page = (srcaddr + cnt) >> 8; | 
 | 		cmd->data[cnt].offset = (srcaddr + cnt) & 0xff; | 
 | 	} | 
 | 	reply->rceb.bEventType = i1480_CET_VS1; | 
 | 	reply->rceb.wEvent = i1480_CMD_MPI_READ; | 
 | 	result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size, | 
 | 			sizeof(*reply) + 3*size); | 
 | 	if (result < 0) | 
 | 		goto out; | 
 | 	if (reply->bResultCode != UWB_RC_RES_SUCCESS) { | 
 | 		dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n", | 
 | 			reply->bResultCode); | 
 | 		result = -EIO; | 
 | 	} | 
 | 	for (cnt = 0; cnt < size; cnt++) { | 
 | 		if (reply->data[cnt].page != (srcaddr + cnt) >> 8) | 
 | 			dev_err(i1480->dev, "MPI-READ: page inconsistency at " | 
 | 				"index %u: expected 0x%02x, got 0x%02x\n", cnt, | 
 | 				(srcaddr + cnt) >> 8, reply->data[cnt].page); | 
 | 		if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff)) | 
 | 			dev_err(i1480->dev, "MPI-READ: offset inconsistency at " | 
 | 				"index %u: expected 0x%02x, got 0x%02x\n", cnt, | 
 | 				(srcaddr + cnt) & 0x00ff, | 
 | 				reply->data[cnt].offset); | 
 | 		data[cnt] = reply->data[cnt].value; | 
 | 	} | 
 | 	result = 0; | 
 | out: | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | /** | 
 |  * Upload a PHY firmware, wait for it to start | 
 |  * | 
 |  * @i1480:     Device instance | 
 |  * @fw_name: Name of the file that contains the firmware | 
 |  * | 
 |  * We assume the MAC fw is up and running. This means we can use the | 
 |  * MPI interface to write the PHY firmware. Once done, we issue an | 
 |  * MBOA Reset, which will force the MAC to reset and reinitialize the | 
 |  * PHY. If that works, we are ready to go. | 
 |  * | 
 |  * Max packet size for the MPI write is 512, so the max buffer is 480 | 
 |  * (which gives us 160 byte triads of MSB, LSB and VAL for the data). | 
 |  */ | 
 | int i1480_phy_fw_upload(struct i1480 *i1480) | 
 | { | 
 | 	int result; | 
 | 	const struct firmware *fw; | 
 | 	const char *data_itr, *data_top; | 
 | 	const size_t MAX_BLK_SIZE = 480;	/* 160 triads */ | 
 | 	size_t data_size; | 
 | 	u8 phy_stat; | 
 |  | 
 | 	result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev); | 
 | 	if (result < 0) | 
 | 		goto out; | 
 | 	/* Loop writing data in chunks as big as possible until done. */ | 
 | 	for (data_itr = fw->data, data_top = data_itr + fw->size; | 
 | 	     data_itr < data_top; data_itr += MAX_BLK_SIZE) { | 
 | 		data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr)); | 
 | 		result = i1480_mpi_write(i1480, data_itr, data_size); | 
 | 		if (result < 0) | 
 | 			goto error_mpi_write; | 
 | 	} | 
 | 	/* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */ | 
 | 	result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1); | 
 | 	if (result < 0) { | 
 | 		dev_err(i1480->dev, "PHY: can't get status: %d\n", result); | 
 | 		goto error_mpi_status; | 
 | 	} | 
 | 	if (phy_stat != 0) { | 
 | 		result = -ENODEV; | 
 | 		dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat); | 
 | 		goto error_phy_status; | 
 | 	} | 
 | 	dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name); | 
 | error_phy_status: | 
 | error_mpi_status: | 
 | error_mpi_write: | 
 | 	release_firmware(fw); | 
 | 	if (result < 0) | 
 | 		dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), " | 
 | 			"power cycle device\n", i1480->phy_fw_name, result); | 
 | out: | 
 | 	return result; | 
 | } |