This patch provides SPI interface driver for SMS chip-set based devices. The patch includes common SMS SPI code, and adapter driver for PXA310 Signed-off-by: Uri Shkolnik <uris@xxxxxxxxxxxx>
This patch provides SPI interface driver for SMS chip-set based devices. The patch includes common SMS SPI code, and adapter driver for PXA310 Signed-off-by: Uri Shkolnik <uris@xxxxxxxxxxxx> diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsdbg_prn.h v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsdbg_prn.h --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsdbg_prn.h 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsdbg_prn.h 2008-11-18 18:14:34.000000000 +0200 @@ -0,0 +1,56 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ + +#ifndef _SMS_DBG_H_ +#define _SMS_DBG_H_ + +#include <linux/kernel.h> +#include <linux/module.h> + +/************************************************************************/ +/* Debug Zones definitions. */ +/************************************************************************/ +#undef PERROR +# define PERROR(fmt, args...) \ + printk(KERN_ERR "spibus error: line %d- %s(): " fmt, __LINE__,\ + __func__, ## args) +#undef PWARNING +# define PWARNING(fmt, args...) \ + printk(KERN_WARNING "spibus warning: line %d- %s(): " fmt, __LINE__, \ + __func__, ## args) + +/* the debug macro - conditional compilation from the makefile */ +#undef PDEBUG /* undef it, just in case */ +#ifdef SPIBUS_DEBUG +# define PDEBUG(fmt, args...) \ + printk(KERN_DEBUG "spibus: line %d- %s(): " fmt, __LINE__, \ + __func__, ## args) +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +/* The following defines are used for printing and +are mandatory for compilation. */ +#define TXT(str) str +#define PRN_DBG(str) PDEBUG str +#define PRN_ERR(str) PERROR str + +#endif /*_SMS_DBG_H_*/ diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspicommon.c v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspicommon.c --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspicommon.c 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspicommon.c 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,326 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +#include "smsspicommon.h" +#include "smsdbg_prn.h" + +static int smsspi_common_find_msg(struct spi_dev *dev, rx_buffer_st * buf, + int offset, int len, int *unused_bytes) +{ + int i, missing_bytes; + int recieved_bytes, padded_msg_len; + int align_fix; + char *ptr = (char *)buf->ptr + offset; + + PRN_DBG((TXT("entering with %d bytes.\n"), len)); + missing_bytes = 0; + for (i = 0; i < len; i++, ptr++) { + switch (dev->rxState) { + case RxsWait_a5: + dev->rxState = + ((*ptr & 0xff) == 0xa5) ? RxsWait_5a : RxsWait_a5; + dev->rxPacket.msg_offset = + (unsigned long)ptr - (unsigned long)buf->ptr + 4; + break; + case RxsWait_5a: + dev->rxState = + ((*ptr & 0xff) == 0x5a) ? RxsWait_e7 : RxsWait_a5; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsWait_e7: + dev->rxState = + ((*ptr & 0xff) == 0xe7) ? RxsWait_7e : RxsWait_a5; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsWait_7e: + dev->rxState = + ((*ptr & 0xff) == 0x7e) ? RxsTypeH : RxsWait_a5; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsTypeH: + dev->rxPacket.msg_buf = buf; + dev->rxPacket.msg_offset = + (unsigned long)ptr - (unsigned long)buf->ptr; + dev->rxState = RxsTypeL; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsTypeL: + dev->rxState = RxsGetSrcId; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetSrcId: + dev->rxState = RxsGetDstId; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetDstId: + dev->rxState = RxsGetLenL; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetLenL: + dev->rxState = RxsGetLenH; + dev->rxPacket.msg_len = (*ptr & 0xff); + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetLenH: + dev->rxState = RxsFlagsL; + dev->rxPacket.msg_len += (*ptr & 0xff) << 8; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsFlagsL: + dev->rxState = RxsFlagsH; + dev->rxPacket.msg_flags = (*ptr & 0xff); + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsFlagsH: + dev->rxState = RxsData; + dev->rxPacket.msg_flags += (*ptr & 0xff) << 8; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsData: + recieved_bytes = + len + offset - dev->rxPacket.msg_offset; + padded_msg_len = + ((dev->rxPacket.msg_len + 4 + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS; + if (recieved_bytes < padded_msg_len) { + *unused_bytes = 0; + return padded_msg_len - recieved_bytes; + } + dev->rxState = RxsWait_a5; + if (dev->cb.msg_found_cb) { + align_fix = 0; + if (dev->rxPacket. + msg_flags & MSG_HDR_FLAG_SPLIT_MSG_HDR) { + align_fix = + (dev->rxPacket. + msg_flags >> 8) & 0x3; + if (align_fix) { + /* The FW alligned the message data + therefore - alligment bytes should be + thrown away. Throw the aligment bytes + by moving the header ahead over the + alligment bytes. */ + ptr = + (char *)dev->rxPacket. + msg_buf->ptr + + dev->rxPacket.msg_offset; + + /* Restore header to original + state before alignment changes + */ + u16 length = + (ptr[5] << 8) | ptr[4]; + length -= align_fix; + ptr[5] = length >> 8; + ptr[4] = length & 0xff; + /* Zero alignment flags */ + ptr[7] &= 0xfc; + + for (i = MSG_HDR_LEN - 1; + i >= 0; i--) { + ptr[i + align_fix] = + ptr[i]; + } + dev->rxPacket.msg_offset += + align_fix; + } + } + + PRN_DBG((TXT + ("Msg found and sent to callback func.\n"))); + dev->cb.msg_found_cb(dev->context, + dev->rxPacket.msg_buf, + dev->rxPacket.msg_offset, + dev->rxPacket.msg_len - + align_fix); + *unused_bytes = + len + offset - dev->rxPacket.msg_offset - + dev->rxPacket.msg_len; + if (*unused_bytes == 0) + *unused_bytes = -1; + return 0; + } else { + PRN_DBG((TXT + ("Msg found but no callback. therefore - thrown away.\n"))); + } + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + } + } + + if (dev->rxState == RxsWait_a5) { + *unused_bytes = 0; + return 0; + } else { + /* Workaround to corner case: if the last byte of the buffer + is "a5" (first byte of the preamble), the host thinks it should + send another 256 bytes. In case the a5 is the firmware + underflow byte, this will cause an infinite loop, so we check + for this case explicity. */ + if ((dev->rxState == RxsWait_5a) && (*(ptr - 2) == 0xa5)) { + dev->rxState = RxsWait_a5; + *unused_bytes = 0; + return 0; + } + + if (dev->rxPacket.msg_offset >= (SPI_PACKET_SIZE + 4)) + /* adding 4 for the preamble. */ + { /*The packet will be copied to a new buffer + and rescaned by the state machine */ + *unused_bytes = dev->rxState - RxsWait_a5; + dev->rxState = RxsWait_a5; + dev->cb.free_rx_buf(dev->context, buf); + return 0; + } else { + /* report missing bytes and continue + with message scan. */ + *unused_bytes = 0; + return SPI_PACKET_SIZE; + } + } +} + +void smsspi_common_transfer_msg(struct spi_dev *dev, struct spi_msg *txmsg, + int padding_allowed) +{ + int len, bytes_to_transfer; + unsigned long tx_phy_addr; + int missing_bytes, tx_bytes; + int offset, unused_bytes; + int align_block; + char *txbuf; + rx_buffer_st * buf, *tmp_buf; + + len = 0; + if (!dev->cb.transfer_data_cb) { + PRN_ERR((TXT + ("function called while module is not initialized.\n"))); + return; + } + if (txmsg == 0) { + bytes_to_transfer = SPI_PACKET_SIZE; + txbuf = 0; + tx_phy_addr = 0; + tx_bytes = 0; + } else { + tx_bytes = txmsg->len; + if (padding_allowed) + bytes_to_transfer = + (((tx_bytes + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS); + else + bytes_to_transfer = (((tx_bytes + 3) >> 2) << 2); + txbuf = txmsg->buf; + tx_phy_addr = txmsg->buf_phy_addr; + } + offset = 0; + unused_bytes = 0; + buf = + dev->cb.allocate_rx_buf(dev->context, + RX_PACKET_SIZE + SPI_PACKET_SIZE); + if (!buf) { + PRN_ERR((TXT("Failed to allocate RX buffer.\n"))); + return; + } + while (bytes_to_transfer || unused_bytes) { + if ((unused_bytes <= 0) && (bytes_to_transfer > 0)) { + len = min(bytes_to_transfer, RX_PACKET_SIZE); + PRN_DBG((TXT("transfering block of %d bytes\n"), len)); + dev->cb.transfer_data_cb(dev->phy_context, txbuf, + tx_phy_addr, + (char *)buf->ptr + offset, + buf->phy_addr + offset, len); + } + missing_bytes = + smsspi_common_find_msg(dev, buf, offset, len, + &unused_bytes); + if (bytes_to_transfer) + bytes_to_transfer -= len; + + if (tx_bytes) + tx_bytes -= len; + + if (missing_bytes) + offset += len; + + if (unused_bytes) { + tmp_buf = + dev->cb.allocate_rx_buf(dev->context, + RX_PACKET_SIZE); + if (!tmp_buf) { + PRN_ERR((TXT + ("Failed to allocate RX buffer.\n"))); + return; + } + if (unused_bytes > 0) { + /* Copy the remaining bytes to the end of + alingment block (256 bytes) so next read + will be alligned. */ + align_block = + (((unused_bytes + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS); + memset(tmp_buf->ptr, 0, + align_block - unused_bytes); + memcpy((char *)tmp_buf->ptr + + (align_block - unused_bytes), + (char *)buf->ptr + offset + len - + unused_bytes, unused_bytes); + len = align_block; + } + offset = 0; + buf = tmp_buf; + } + if (tx_bytes <= 0) { + txbuf = 0; + tx_bytes = 0; + } + if (bytes_to_transfer < missing_bytes) { + bytes_to_transfer = + (((missing_bytes + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS); + PRN_DBG((TXT + ("a message was found, adding bytes to transfer, txmsg %d, total %d\n") + , tx_bytes, bytes_to_transfer)); + } + } + dev->cb.free_rx_buf(dev->context, buf); +} + +int smsspicommon_init(struct spi_dev *dev, void *context, void *phy_context, + spi_dev_cb_st *cb) +{ + PRN_DBG((TXT("entering.\n"))); + if (cb->transfer_data_cb == 0 || + cb->msg_found_cb == 0 || + cb->allocate_rx_buf == 0 || cb->free_rx_buf == 0) { + PRN_ERR((TXT("Invalid input parameters of init routine.\n"))); + return -1; + } + dev->context = context; + dev->phy_context = phy_context; + memcpy(&dev->cb, cb, sizeof(spi_dev_cb_st)); + dev->rxState = RxsWait_a5; + PRN_DBG((TXT("exiting.\n"))); + return 0; +} diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspicommon.c~ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspicommon.c~ --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspicommon.c~ 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspicommon.c~ 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,326 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +#include "smsspicommon.h" +#include "smsdbg_prn.h" + +static int smsspi_common_find_msg(struct spi_dev *dev, rx_buffer_st * buf, + int offset, int len, int *unused_bytes) +{ + int i, missing_bytes; + int recieved_bytes, padded_msg_len; + int align_fix; + char *ptr = (char *)buf->ptr + offset; + + PRN_DBG((TXT("entering with %d bytes.\n"), len)); + missing_bytes = 0; + for (i = 0; i < len; i++, ptr++) { + switch (dev->rxState) { + case RxsWait_a5: + dev->rxState = + ((*ptr & 0xff) == 0xa5) ? RxsWait_5a : RxsWait_a5; + dev->rxPacket.msg_offset = + (unsigned long)ptr - (unsigned long)buf->ptr + 4; + break; + case RxsWait_5a: + dev->rxState = + ((*ptr & 0xff) == 0x5a) ? RxsWait_e7 : RxsWait_a5; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsWait_e7: + dev->rxState = + ((*ptr & 0xff) == 0xe7) ? RxsWait_7e : RxsWait_a5; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsWait_7e: + dev->rxState = + ((*ptr & 0xff) == 0x7e) ? RxsTypeH : RxsWait_a5; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsTypeH: + dev->rxPacket.msg_buf = buf; + dev->rxPacket.msg_offset = + (unsigned long)ptr - (unsigned long)buf->ptr; + dev->rxState = RxsTypeL; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsTypeL: + dev->rxState = RxsGetSrcId; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetSrcId: + dev->rxState = RxsGetDstId; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetDstId: + dev->rxState = RxsGetLenL; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetLenL: + dev->rxState = RxsGetLenH; + dev->rxPacket.msg_len = (*ptr & 0xff); + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsGetLenH: + dev->rxState = RxsFlagsL; + dev->rxPacket.msg_len += (*ptr & 0xff) << 8; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsFlagsL: + dev->rxState = RxsFlagsH; + dev->rxPacket.msg_flags = (*ptr & 0xff); + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsFlagsH: + dev->rxState = RxsData; + dev->rxPacket.msg_flags += (*ptr & 0xff) << 8; + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + case RxsData: + recieved_bytes = + len + offset - dev->rxPacket.msg_offset; + padded_msg_len = + ((dev->rxPacket.msg_len + 4 + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS; + if (recieved_bytes < padded_msg_len) { + *unused_bytes = 0; + return padded_msg_len - recieved_bytes; + } + dev->rxState = RxsWait_a5; + if (dev->cb.msg_found_cb) { + align_fix = 0; + if (dev->rxPacket. + msg_flags & MSG_HDR_FLAG_SPLIT_MSG_HDR) { + align_fix = + (dev->rxPacket. + msg_flags >> 8) & 0x3; + if (align_fix) { + /* The FW alligned the message data + therefore - alligment bytes should be + thrown away. Throw the aligment bytes + by moving the header ahead over the + alligment bytes. */ + ptr = + (char *)dev->rxPacket. + msg_buf->ptr + + dev->rxPacket.msg_offset; + + /* Restore header to original + state before alignment changes + */ + u16 length = + (ptr[5] << 8) | ptr[4]; + length -= align_fix; + ptr[5] = length >> 8; + ptr[4] = length & 0xff; + /* Zero alignment flags */ + ptr[7] &= 0xfc; + + for (i = MSG_HDR_LEN - 1; + i >= 0; i--) { + ptr[i + align_fix] = + ptr[i]; + } + dev->rxPacket.msg_offset += + align_fix; + } + } + + PRN_DBG((TXT + ("Msg found and sent to callback func.\n"))); + dev->cb.msg_found_cb(dev->context, + dev->rxPacket.msg_buf, + dev->rxPacket.msg_offset, + dev->rxPacket.msg_len - + align_fix); + *unused_bytes = + len + offset - dev->rxPacket.msg_offset - + dev->rxPacket.msg_len; + if (*unused_bytes == 0) + *unused_bytes = -1; + return 0; + } else { + PRN_DBG((TXT + ("Msg found but no callback. therefore - thrown away.\n"))); + } + PRN_DBG((TXT("state %d.\n"), dev->rxState)); + break; + } + } + + if (dev->rxState == RxsWait_a5) { + *unused_bytes = 0; + return 0; + } else { + /* Workaround to corner case: if the last byte of the buffer + is "a5" (first byte of the preamble), the host thinks it should + send another 256 bytes. In case the a5 is the firmware + underflow byte, this will cause an infinite loop, so we check + for this case explicity. */ + if ((dev->rxState == RxsWait_5a) && (*(ptr - 2) == 0xa5)) { + dev->rxState = RxsWait_a5; + *unused_bytes = 0; + return 0; + } + + if (dev->rxPacket.msg_offset >= (SPI_PACKET_SIZE + 4)) + /* adding 4 for the preamble. */ + { /*The packet will be copied to a new buffer + and rescaned by the state machine */ + *unused_bytes = dev->rxState - RxsWait_a5; + dev->rxState = RxsWait_a5; + dev->cb.free_rx_buf(dev->context, buf); + return 0; + } else { + /* report missing bytes and continue + with message scan. */ + *unused_bytes = 0; + return SPI_PACKET_SIZE; + } + } +} + +void smsspi_common_transfer_msg(struct spi_dev *dev, struct spi_msg *txmsg, + int padding_allowed) +{ + int len, bytes_to_transfer; + unsigned long tx_phy_addr; + int missing_bytes, tx_bytes; + int offset, unused_bytes; + int align_block; + char *txbuf; + rx_buffer_st * buf, *tmp_buf; + + len = 0; + if (!dev->cb.transfer_data_cb) { + PRN_ERR((TXT + ("function called while module is not initialized.\n"))); + return; + } + if (txmsg == 0) { + bytes_to_transfer = SPI_PACKET_SIZE; + txbuf = 0; + tx_phy_addr = 0; + tx_bytes = 0; + } else { + tx_bytes = txmsg->len; + if (padding_allowed) + bytes_to_transfer = + (((tx_bytes + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS); + else + bytes_to_transfer = (((tx_bytes + 3) >> 2) << 2); + txbuf = txmsg->buf; + tx_phy_addr = txmsg->buf_phy_addr; + } + offset = 0; + unused_bytes = 0; + buf = + dev->cb.allocate_rx_buf(dev->context, + RX_PACKET_SIZE + SPI_PACKET_SIZE); + if (!buf) { + PRN_ERR((TXT("Failed to allocate RX buffer.\n"))); + return; + } + while (bytes_to_transfer || unused_bytes) { + if ((unused_bytes <= 0) && (bytes_to_transfer > 0)) { + len = min(bytes_to_transfer, RX_PACKET_SIZE); + PRN_DBG((TXT("transfering block of %d bytes\n"), len)); + dev->cb.transfer_data_cb(dev->phy_context, txbuf, + tx_phy_addr, + (char *)buf->ptr + offset, + buf->phy_addr + offset, len); + } + missing_bytes = + smsspi_common_find_msg(dev, buf, offset, len, + &unused_bytes); + if (bytes_to_transfer) + bytes_to_transfer -= len; + + if (tx_bytes) + tx_bytes -= len; + + if (missing_bytes) + offset += len; + + if (unused_bytes) { + tmp_buf = + dev->cb.allocate_rx_buf(dev->context, + RX_PACKET_SIZE); + if (!tmp_buf) { + PRN_ERR((TXT + ("Failed to allocate RX buffer.\n"))); + return; + } + if (unused_bytes > 0) { + /* Copy the remaining bytes to the end of + alingment block (256 bytes) so next read + will be alligned. */ + align_block = + (((unused_bytes + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS); + memset(tmp_buf->ptr, 0, + align_block - unused_bytes); + memcpy((char *)tmp_buf->ptr + + (align_block - unused_bytes), + (char *)buf->ptr + offset + len - + unused_bytes, unused_bytes); + len = align_block; + } + offset = 0; + buf = tmp_buf; + } + if (tx_bytes <= 0) { + txbuf = 0; + tx_bytes = 0; + } + if (bytes_to_transfer < missing_bytes) { + bytes_to_transfer = + (((missing_bytes + SPI_PACKET_SIZE - + 1) >> SPI_PACKET_SIZE_BITS) << + SPI_PACKET_SIZE_BITS); + PRN_DBG((TXT + ("a message was found, adding bytes to transfer, txmsg %d, total %d\n"), + tx_bytes, bytes_to_transfer)); + } + } + dev->cb.free_rx_buf(dev->context, buf); +} + +int smsspicommon_init(struct spi_dev *dev, void *context, void *phy_context, + spi_dev_cb_st *cb) +{ + PRN_DBG((TXT("entering.\n"))); + if (cb->transfer_data_cb == 0 || + cb->msg_found_cb == 0 || + cb->allocate_rx_buf == 0 || cb->free_rx_buf == 0) { + PRN_ERR((TXT("Invalid input parameters of init routine.\n"))); + return -1; + } + dev->context = context; + dev->phy_context = phy_context; + memcpy(&dev->cb, cb, sizeof(spi_dev_cb_st)); + dev->rxState = RxsWait_a5; + PRN_DBG((TXT("exiting.\n"))); + return 0; +} diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspicommon.h v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspicommon.h --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspicommon.h 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspicommon.h 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,96 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +#ifndef _SMS_SPI_COMMON_H_ +#define _SMS_SPI_COMMON_H_ + +#define RX_PACKET_SIZE 0x1000 +#define SPI_PACKET_SIZE_BITS 8 +#define SPI_PACKET_SIZE (1<<SPI_PACKET_SIZE_BITS) +#define SPI_MAX_CTRL_MSG_SIZE 0x100 + +#define MSG_HDR_FLAG_SPLIT_MSG_HDR 0x0004 +#define MSG_HDR_LEN 8 + +enum _spi_rx_state { + RxsWait_a5 = 0, + RxsWait_5a, + RxsWait_e7, + RxsWait_7e, + RxsTypeH, + RxsTypeL, + RxsGetSrcId, + RxsGetDstId, + RxsGetLenL, + RxsGetLenH, + RxsFlagsL, + RxsFlagsH, + RxsData +} spi_rx_state; + +struct { + void *ptr; + unsigned long phy_addr; +} rx_buffer_st; + +struct _rx_packet_request { + rx_buffer_st *msg_buf; + int msg_offset; + int msg_len; + int msg_flags; +} rx_packet_request; + +struct { + void (*transfer_data_cb) (void *context, unsigned char *, unsigned long, + unsigned char *, unsigned long, int); + void (*msg_found_cb) (void *, void *, int, int); + rx_buffer_st *(*allocate_rx_buf) (void *, int); + void (*free_rx_buf) (void *, rx_buffer_st *); +} spi_dev_cb_st; + +struct spi_dev { + void *context; + void *phy_context; + spi_dev_cb_st cb; + char *rxbuf; + spi_rx_state rxState; + rx_packet_request rxPacket; + char *internal_tx_buf; +} spi_dev_st; + +struct spi_msg { + char *buf; + unsigned long buf_phy_addr; + int len; +} spi_msg_st; + +void smsspi_common_transfer_msg(struct spi_dev *dev, struct spi_msg *txmsg, + int padding_allowed); +int smsspicommon_init(struct spi_dev *dev, void *contex, void *phy_context, + spi_dev_cb_st *cb); + +#if defined HEXDUMP_DEBUG && defined SPIBUS_DEBUG +/*! dump a human readable print of a binary buffer */ +void smsspi_khexdump(char *buf, int len); +#else +#define smsspi_khexdump(buf, len) +#endif + +#endif /*_SMS_SPI_COMMON_H_*/ diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspilog.c v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspilog.c --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspilog.c 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspilog.c 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,446 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +/*! + \file spibusdrv.c + + \brief spi bus driver module + + This file contains implementation of the spi bus driver. +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> + +#include "smscoreapi.h" +#include "smsdbg_prn.h" +#include "smsspicommon.h" +#include "smsspiphy.h" + +#define SMS_INTR_PIN 16 /* 0 for nova sip, 26 for vega */ +#define TX_BUFFER_SIZE 0x200 +#define RX_BUFFER_SIZE (0x1000 + SPI_PACKET_SIZE + 0x100) +#define NUM_RX_BUFFERS 72 + +struct { + struct spi_dev dev; + void *phy_dev; + + struct completion write_operation; + struct list_head tx_queue; + int allocatedPackets; + int padding_allowed; + char *rxbuf; + dma_addr_t rxbuf_phy_addr; + + struct smscore_device_t *coredev; + struct list_head txqueue; + char *txbuf; + dma_addr_t txbuf_phy_addr; +} spi_device_st; + +struct _smsspi_txmsg { + struct list_head node; /*! internal management */ + void *buffer; + size_t size; + int alignment; + int add_preamble; + struct completion completion; + void (*prewrite) (void *); + void (*postwrite) (void *); +} smsspi_txmsg_t; + +spi_device_st *spi_dev; + +static void spi_worker_thread(void *arg); +static DECLARE_WORK(spi_work_queue, (void *)spi_worker_thread); +static u8 smsspi_preamble[] = { 0xa5, 0x5a, 0xe7, 0x7e }; +static u8 smsspi_startup[] = { 0, 0, 0xde, 0xc1, 0xa5, 0x51, 0xf1, 0xed }; +static u32 default_type = SMS_NOVA_A0; +static u32 intr_pin = SMS_INTR_PIN; + +module_param(default_type, int, 0644); +MODULE_PARM_DESC(default_type, "default board type."); + +module_param(intr_pin, int, 0644); +MODULE_PARM_DESC(intr_pin, "interrupt pin number."); + +/******************************************/ +static void spi_worker_thread(void *arg) +{ + spi_device_st *spi_device = spi_dev; + smsspi_txmsg_t *msg = NULL; + spi_msg_st txmsg; + + PDEBUG("worker start\n"); + do { + /* do we have a msg to write ? */ + if (!msg && !list_empty(&spi_device->txqueue)) + msg = + (smsspi_txmsg_t *) list_entry(spi_device->txqueue. + next, smsspi_txmsg_t, + node); + + if (msg) { + if (msg->add_preamble) { + txmsg.len = + min(msg->size + sizeof(smsspi_preamble), + (size_t) TX_BUFFER_SIZE); + txmsg.buf = spi_device->txbuf; + txmsg.buf_phy_addr = spi_device->txbuf_phy_addr; + memcpy(txmsg.buf, smsspi_preamble, + sizeof(smsspi_preamble)); + memcpy(&txmsg.buf[sizeof(smsspi_preamble)], + msg->buffer, + txmsg.len - sizeof(smsspi_preamble)); + msg->add_preamble = 0; + msg->buffer += + txmsg.len - sizeof(smsspi_preamble); + msg->size -= + txmsg.len - sizeof(smsspi_preamble); + /* zero out the rest of aligned buffer */ + memset(&txmsg.buf[txmsg.len], 0, + TX_BUFFER_SIZE - txmsg.len); + smsspi_common_transfer_msg(&spi_device->dev, + &txmsg, 1); + } else { + txmsg.len = + min(msg->size, (size_t) TX_BUFFER_SIZE); + txmsg.buf = spi_device->txbuf; + txmsg.buf_phy_addr = spi_device->txbuf_phy_addr; + memcpy(txmsg.buf, msg->buffer, txmsg.len); + + msg->buffer += txmsg.len; + msg->size -= txmsg.len; + /* zero out the rest of aligned buffer */ + memset(&txmsg.buf[txmsg.len], 0, + TX_BUFFER_SIZE - txmsg.len); + smsspi_common_transfer_msg(&spi_device->dev, + &txmsg, 0); + } + + } else { + smsspi_common_transfer_msg(&spi_device->dev, NULL, 1); + } + + /* if there was write, have we finished ? */ + if (msg && !msg->size) { + /* call postwrite call back */ + if (msg->postwrite) + msg->postwrite(spi_device); + + list_del(&msg->node); + complete(&msg->completion); + msg = NULL; + } + /* if there was read, did we read anything ? */ + + } while (!list_empty(&spi_device->txqueue) || msg); + + PDEBUG("worker end\n"); + +} + +static void msg_found(void *context, void *buf, int offset, int len) +{ + spi_device_st *spi_device = (spi_device_st *) context; + struct smscore_buffer_t *cb = + (struct smscore_buffer_t + *)(container_of(buf, struct smscore_buffer_t, p)); + + PDEBUG("entering\n"); + cb->offset = offset; + cb->size = len; + /* PERROR ("buffer %p is sent back to core databuf=%p, + offset=%d.\n", cb, cb->p, cb->offset); */ + smscore_onresponse(spi_device->coredev, cb); + + PDEBUG("exiting\n"); + +} + +static void smsspi_int_handler(void *context) +{ + spi_device_st *spi_device = (spi_device_st *) context; + PDEBUG("interrupt\n"); + PREPARE_WORK(&spi_work_queue, (void *)spi_worker_thread); + spi_device->padding_allowed = 1; + schedule_work(&spi_work_queue); +} + +static int smsspi_queue_message_and_wait(spi_device_st *spi_device, + smsspi_txmsg_t *msg) +{ + init_completion(&msg->completion); + list_add_tail(&msg->node, &spi_device->txqueue); + schedule_work(&spi_work_queue); + wait_for_completion(&msg->completion); + + return 0; +} + +static int smsspi_preload(void *context) +{ + smsspi_txmsg_t msg; + spi_device_st *spi_device = (spi_device_st *) context; + + prepareForFWDnl(spi_device->phy_dev); + PDEBUG("Sending SPI init sequence\n"); + msg.buffer = smsspi_startup; + msg.size = sizeof(smsspi_startup); + msg.alignment = 4; + msg.add_preamble = 0; + msg.prewrite = NULL; /* smsspiphy_reduce_clock; */ + msg.postwrite = NULL; + + return smsspi_queue_message_and_wait(context, &msg); +} + +static int smsspi_postload(void *context) +{ + struct _Msg { + struct SmsMsgHdr_ST hdr; + u32 data[3]; + } Msg = { + { + MSG_SMS_SPI_INT_LINE_SET_REQ, 0, HIF_TASK, + sizeof(struct _Msg), 0}, { + 0, intr_pin, 0} + }; + spi_device_st *spi_device = (spi_device_st *) context; + smsspi_txmsg_t msg; + + PDEBUG("Sending SPI Set Interrupt command sequence\n"); + fwDnlComplete(spi_device->phy_dev, 0); + msg.buffer = &Msg; + msg.size = sizeof(Msg); + msg.alignment = SPI_PACKET_SIZE; + msg.add_preamble = 1; + msg.prewrite = NULL; + msg.postwrite = NULL; /* smsspiphy_restore_clock; */ + + return smsspi_queue_message_and_wait(context, &msg); +} + +static int smsspi_write(void *context, void *txbuf, size_t len) +{ + smsspi_txmsg_t msg; + + msg.buffer = txbuf; + msg.size = len; + msg.prewrite = NULL; + msg.postwrite = NULL; + + if (len > 0x1000) { + /* The FW is the only long message. Do not add preamble, + and do not padd it */ + msg.alignment = 4; + msg.add_preamble = 0; + msg.prewrite = smschipreset; + } else { + msg.alignment = SPI_PACKET_SIZE; + msg.add_preamble = 1; + } + PDEBUG("Writing message to SPI.\n"); + PDEBUG("msg hdr: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x.\n", + ((u8 *) txbuf)[0], ((u8 *) txbuf)[1], ((u8 *) txbuf)[2], + ((u8 *) txbuf)[3], ((u8 *) txbuf)[4], ((u8 *) txbuf)[5], + ((u8 *) txbuf)[6], ((u8 *) txbuf)[7]); + return smsspi_queue_message_and_wait(context, &msg); +} + +rx_buffer_st *allocate_rx_buf(void *context, int size) +{ + struct smscore_buffer_t *buf; + spi_device_st *spi_device = (spi_device_st *) context; + if (size > RX_BUFFER_SIZE) { + PERROR("Requested size is bigger than max buffer size.\n"); + return NULL; + } + buf = smscore_getbuffer(spi_device->coredev); + PDEBUG("Recieved Rx buf %p physical 0x%x (contained in %p)\n", buf->p, + buf->phys, buf); + + /* note: this is not mistake! the rx_buffer_st is identical to part of + smscore_buffer_t and we return the address of the start of the + identical part */ + return (rx_buffer_st *) &buf->p; +} + +static void free_rx_buf(void *context, rx_buffer_st *buf) +{ + spi_device_st *spi_device = (spi_device_st *) context; + struct smscore_buffer_t *cb = + (struct smscore_buffer_t + *)(container_of(((void *)buf), struct smscore_buffer_t, p)); + PDEBUG("buffer %p is released.\n", cb); + smscore_putbuffer(spi_device->coredev, cb); +} + +/*! Release device STUB + +\param[in] dev: device control block +\return void +*/ +static void smsspi_release(struct device *dev) +{ + PDEBUG("nothing to do\n"); + /* Nothing to release */ +} + +static struct platform_device smsspi_device = { + .name = "smsspi", + .id = 1, + .dev = { + .release = smsspi_release, + }, +}; + +int smsspi_register(void) +{ + struct smsdevice_params_t params; + int ret; + spi_device_st *spi_device; + spi_dev_cb_st common_cb; + + PDEBUG("entering \n"); + + spi_device = + kmalloc(sizeof(spi_device_st), GFP_KERNEL); + spi_dev = spi_device; + + INIT_LIST_HEAD(&spi_device->txqueue); + + ret = platform_device_register(&smsspi_device); + if (ret < 0) { + PERROR("platform_device_register failed\n"); + return ret; + } + + spi_device->txbuf = + dma_alloc_coherent(NULL, TX_BUFFER_SIZE, + &spi_device->txbuf_phy_addr, + GFP_KERNEL | GFP_DMA); + if (!spi_device->txbuf) { + printk(KERN_INFO "%s dma_alloc_coherent(...) failed\n", + __func__); + ret = -ENOMEM; + goto txbuf_error; + } + + spi_device->phy_dev = + smsspiphy_init(NULL, smsspi_int_handler, &spi_device); + if (spi_device->phy_dev == 0) { + printk(KERN_INFO "%s smsspiphy_init(...) failed\n", __func__); + goto phy_error; + } + + common_cb.allocate_rx_buf = allocate_rx_buf; + common_cb.free_rx_buf = free_rx_buf; + common_cb.msg_found_cb = msg_found; + common_cb.transfer_data_cb = smsspibus_xfer; + + ret = + smsspicommon_init(&spi_device->dev, spi_device, spi_device->phy_dev, + &common_cb); + if (ret) { + printk(KERN_INFO "%s smsspiphy_init(...) failed\n", __func__); + goto common_error; + } + + /* register in smscore */ + memset(¶ms, 0, sizeof(params)); + params.context = spi_device; + params.device = &smsspi_device.dev; + params.buffer_size = RX_BUFFER_SIZE; + params.num_buffers = NUM_RX_BUFFERS; + params.flags = SMS_DEVICE_NOT_READY; + params.sendrequest_handler = smsspi_write; + strcpy(params.devpath, "spi"); + params.device_type = default_type; + + if (0) { + /* device family */ + /* params.setmode_handler = smsspi_setmode; */ + } else { + params.flags = + SMS_DEVICE_FAMILY2 | SMS_DEVICE_NOT_READY | + SMS_ROM_NO_RESPONSE; + params.preload_handler = smsspi_preload; + params.postload_handler = smsspi_postload; + } + + ret = smscore_register_device(¶ms, &spi_device->coredev); + if (ret < 0) { + printk(KERN_INFO "%s smscore_register_device(...) failed\n", + __func__); + goto reg_device_error; + } + + ret = smscore_start_device(spi_device->coredev); + if (ret < 0) { + printk(KERN_INFO "%s smscore_start_device(...) failed\n", + __func__); + goto start_device_error; + } + + PDEBUG("exiting\n"); + return 0; + +start_device_error: + smscore_unregister_device(spi_device->coredev); + +reg_device_error: + +common_error: + smsspiphy_deinit(spi_device->phy_dev); + +phy_error: + dma_free_coherent(NULL, TX_BUFFER_SIZE, spi_device->txbuf, + spi_device->txbuf_phy_addr); + +txbuf_error: + platform_device_unregister(&smsspi_device); + + PDEBUG("exiting error %d\n", ret); + + return ret; +} + +void smsspi_unregister(void) +{ + spi_device_st *spi_device = spi_dev; + PDEBUG("entering\n"); + + /* stop interrupts */ + smsspiphy_deinit(spi_device->phy_dev); + smscore_unregister_device(spi_device->coredev); + + dma_free_coherent(NULL, TX_BUFFER_SIZE, spi_device->txbuf, + spi_device->txbuf_phy_addr); + + platform_device_unregister(&smsspi_device); + PDEBUG("exiting\n"); +} diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspilog.c~ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspilog.c~ --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspilog.c~ 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspilog.c~ 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,446 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +/*! + \file spibusdrv.c + + \brief spi bus driver module + + This file contains implementation of the spi bus driver. +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> + +#include "smscoreapi.h" +#include "smsdbg_prn.h" +#include "smsspicommon.h" +#include "smsspiphy.h" + +#define SMS_INTR_PIN 16 /* 0 for nova sip, 26 for vega */ +#define TX_BUFFER_SIZE 0x200 +#define RX_BUFFER_SIZE (0x1000 + SPI_PACKET_SIZE + 0x100) +#define NUM_RX_BUFFERS 72 + +struct { + struct spi_dev dev; + void *phy_dev; + + struct completion write_operation; + struct list_head tx_queue; + int allocatedPackets; + int padding_allowed; + char *rxbuf; + dma_addr_t rxbuf_phy_addr; + + struct smscore_device_t *coredev; + struct list_head txqueue; + char *txbuf; + dma_addr_t txbuf_phy_addr; +} spi_device_st; + +struct _smsspi_txmsg { + struct list_head node; /*! internal management */ + void *buffer; + size_t size; + int alignment; + int add_preamble; + struct completion completion; + void (*prewrite) (void *); + void (*postwrite) (void *); +} smsspi_txmsg_t; + +spi_device_st *spi_dev; + +static void spi_worker_thread(void *arg); +static DECLARE_WORK(spi_work_queue, (void *)spi_worker_thread); +static u8 smsspi_preamble[] = { 0xa5, 0x5a, 0xe7, 0x7e }; +static u8 smsspi_startup[] = { 0, 0, 0xde, 0xc1, 0xa5, 0x51, 0xf1, 0xed }; +static u32 default_type = SMS_NOVA_A0; +static u32 intr_pin = SMS_INTR_PIN; + +module_param(default_type, int, 0644); +MODULE_PARM_DESC(default_type, "default board type."); + +module_param(intr_pin, int, 0644); +MODULE_PARM_DESC(intr_pin, "interrupt pin number."); + +/******************************************/ +static void spi_worker_thread(void *arg) +{ + spi_device_st *spi_device = spi_dev; + smsspi_txmsg_t *msg = NULL; + spi_msg_st txmsg; + + PDEBUG("worker start\n"); + do { + /* do we have a msg to write ? */ + if (!msg && !list_empty(&spi_device->txqueue)) + msg = + (smsspi_txmsg_t *) list_entry(spi_device->txqueue. + next, smsspi_txmsg_t, + node); + + if (msg) { + if (msg->add_preamble) { + txmsg.len = + min(msg->size + sizeof(smsspi_preamble), + (size_t) TX_BUFFER_SIZE); + txmsg.buf = spi_device->txbuf; + txmsg.buf_phy_addr = spi_device->txbuf_phy_addr; + memcpy(txmsg.buf, smsspi_preamble, + sizeof(smsspi_preamble)); + memcpy(&txmsg.buf[sizeof(smsspi_preamble)], + msg->buffer, + txmsg.len - sizeof(smsspi_preamble)); + msg->add_preamble = 0; + msg->buffer += + txmsg.len - sizeof(smsspi_preamble); + msg->size -= + txmsg.len - sizeof(smsspi_preamble); + /* zero out the rest of aligned buffer */ + memset(&txmsg.buf[txmsg.len], 0, + TX_BUFFER_SIZE - txmsg.len); + smsspi_common_transfer_msg(&spi_device->dev, + &txmsg, 1); + } else { + txmsg.len = + min(msg->size, (size_t) TX_BUFFER_SIZE); + txmsg.buf = spi_device->txbuf; + txmsg.buf_phy_addr = spi_device->txbuf_phy_addr; + memcpy(txmsg.buf, msg->buffer, txmsg.len); + + msg->buffer += txmsg.len; + msg->size -= txmsg.len; + /* zero out the rest of aligned buffer */ + memset(&txmsg.buf[txmsg.len], 0, + TX_BUFFER_SIZE - txmsg.len); + smsspi_common_transfer_msg(&spi_device->dev, + &txmsg, 0); + } + + } else { + smsspi_common_transfer_msg(&spi_device->dev, NULL, 1); + } + + /* if there was write, have we finished ? */ + if (msg && !msg->size) { + /* call postwrite call back */ + if (msg->postwrite) + msg->postwrite(spi_device); + + list_del(&msg->node); + complete(&msg->completion); + msg = NULL; + } + /* if there was read, did we read anything ? */ + + } while (!list_empty(&spi_device->txqueue) || msg); + + PDEBUG("worker end\n"); + +} + +static void msg_found(void *context, void *buf, int offset, int len) +{ + spi_device_st *spi_device = (spi_device_st *) context; + struct smscore_buffer_t *cb = + (struct smscore_buffer_t + *)(container_of(buf, struct smscore_buffer_t, p)); + + PDEBUG("entering\n"); + cb->offset = offset; + cb->size = len; + /* PERROR ("buffer %p is sent back to core databuf=%p, + offset=%d.\n", cb, cb->p, cb->offset); */ + smscore_onresponse(spi_device->coredev, cb); + + PDEBUG("exiting\n"); + +} + +static void smsspi_int_handler(void *context) +{ + spi_device_st *spi_device = (spi_device_st *) context; + PDEBUG("interrupt\n"); + PREPARE_WORK(&spi_work_queue, (void *)spi_worker_thread); + spi_device->padding_allowed = 1; + schedule_work(&spi_work_queue); +} + +static int smsspi_queue_message_and_wait(spi_device_st *spi_device, + smsspi_txmsg_t *msg) +{ + init_completion(&msg->completion); + list_add_tail(&msg->node, &spi_device->txqueue); + schedule_work(&spi_work_queue); + wait_for_completion(&msg->completion); + + return 0; +} + +static int smsspi_preload(void *context) +{ + smsspi_txmsg_t msg; + spi_device_st *spi_device = (spi_device_st *) context; + + prepareForFWDnl(spi_device->phy_dev); + PDEBUG("Sending SPI init sequence\n"); + msg.buffer = smsspi_startup; + msg.size = sizeof(smsspi_startup); + msg.alignment = 4; + msg.add_preamble = 0; + msg.prewrite = NULL; /* smsspiphy_reduce_clock; */ + msg.postwrite = NULL; + + return smsspi_queue_message_and_wait(context, &msg); +} + +static int smsspi_postload(void *context) +{ + struct _Msg { + struct SmsMsgHdr_ST hdr; + u32 data[3]; + } Msg = { + { + MSG_SMS_SPI_INT_LINE_SET_REQ, 0, HIF_TASK, + sizeof(struct _Msg), 0}, { + 0, intr_pin, 0} + }; + spi_device_st *spi_device = (spi_device_st *) context; + smsspi_txmsg_t msg; + + PDEBUG("Sending SPI Set Interrupt command sequence\n"); + fwDnlComplete(spi_device->phy_dev, 0); + msg.buffer = &Msg; + msg.size = sizeof(Msg); + msg.alignment = SPI_PACKET_SIZE; + msg.add_preamble = 1; + msg.prewrite = NULL; + msg.postwrite = NULL; /* smsspiphy_restore_clock; */ + + return smsspi_queue_message_and_wait(context, &msg); +} + +static int smsspi_write(void *context, void *txbuf, size_t len) +{ + smsspi_txmsg_t msg; + + msg.buffer = txbuf; + msg.size = len; + msg.prewrite = NULL; + msg.postwrite = NULL; + + if (len > 0x1000) { + /* The FW is the only long message. Do not add preamble, + and do not padd it */ + msg.alignment = 4; + msg.add_preamble = 0; + msg.prewrite = smschipreset; + } else { + msg.alignment = SPI_PACKET_SIZE; + msg.add_preamble = 1; + } + PDEBUG("Writing message to SPI.\n"); + PDEBUG("msg hdr: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x.\n", + ((u8 *) txbuf)[0], ((u8 *) txbuf)[1], ((u8 *) txbuf)[2], + ((u8 *) txbuf)[3], ((u8 *) txbuf)[4], ((u8 *) txbuf)[5], + ((u8 *) txbuf)[6], ((u8 *) txbuf)[7]); + return smsspi_queue_message_and_wait(context, &msg); +} + +rx_buffer_st *allocate_rx_buf(void *context, int size) +{ + struct smscore_buffer_t *buf; + spi_device_st *spi_device = (spi_device_st *) context; + if (size > RX_BUFFER_SIZE) { + PERROR("Requested size is bigger than max buffer size.\n"); + return NULL; + } + buf = smscore_getbuffer(spi_device->coredev); + PDEBUG("Recieved Rx buf %p physical 0x%x (contained in %p)\n", buf->p, + buf->phys, buf); + + /* note: this is not mistake! the rx_buffer_st is identical to part of + smscore_buffer_t and we return the address of the start of the + identical part */ + return (rx_buffer_st *) &buf->p; +} + +static void free_rx_buf(void *context, rx_buffer_st *buf) +{ + spi_device_st *spi_device = (spi_device_st *) context; + struct smscore_buffer_t *cb = + (struct smscore_buffer_t + *)(container_of(((void *)buf), struct smscore_buffer_t, p)); + PDEBUG("buffer %p is released.\n", cb); + smscore_putbuffer(spi_device->coredev, cb); +} + +/*! Release device STUB + +\param[in] dev: device control block +\return void +*/ +static void smsspi_release(struct device *dev) +{ + PDEBUG("nothing to do\n"); + /* Nothing to release */ +} + +static struct platform_device smsspi_device = { + .name = "smsspi", + .id = 1, + .dev = { + .release = smsspi_release, + }, +}; + +int smsspi_register(void) +{ + struct smsdevice_params_t params; + int ret; + spi_device_st *spi_device; + spi_dev_cb_st common_cb; + + PDEBUG("entering \n"); + + spi_device = + (spi_device_st *) kmalloc(sizeof(spi_device_st), GFP_KERNEL); + spi_dev = spi_device; + + INIT_LIST_HEAD(&spi_device->txqueue); + + ret = platform_device_register(&smsspi_device); + if (ret < 0) { + PERROR("platform_device_register failed\n"); + return ret; + } + + spi_device->txbuf = + dma_alloc_coherent(NULL, TX_BUFFER_SIZE, + &spi_device->txbuf_phy_addr, + GFP_KERNEL | GFP_DMA); + if (!spi_device->txbuf) { + printk(KERN_INFO "%s dma_alloc_coherent(...) failed\n", + __func__); + ret = -ENOMEM; + goto txbuf_error; + } + + spi_device->phy_dev = + smsspiphy_init(NULL, smsspi_int_handler, &spi_device); + if (spi_device->phy_dev == 0) { + printk(KERN_INFO "%s smsspiphy_init(...) failed\n", __func__); + goto phy_error; + } + + common_cb.allocate_rx_buf = allocate_rx_buf; + common_cb.free_rx_buf = free_rx_buf; + common_cb.msg_found_cb = msg_found; + common_cb.transfer_data_cb = smsspibus_xfer; + + ret = + smsspicommon_init(&spi_device->dev, spi_device, spi_device->phy_dev, + &common_cb); + if (ret) { + printk(KERN_INFO "%s smsspiphy_init(...) failed\n", __func__); + goto common_error; + } + + /* register in smscore */ + memset(¶ms, 0, sizeof(params)); + params.context = spi_device; + params.device = &smsspi_device.dev; + params.buffer_size = RX_BUFFER_SIZE; + params.num_buffers = NUM_RX_BUFFERS; + params.flags = SMS_DEVICE_NOT_READY; + params.sendrequest_handler = smsspi_write; + strcpy(params.devpath, "spi"); + params.device_type = default_type; + + if (0) { + /* device family */ + /* params.setmode_handler = smsspi_setmode; */ + } else { + params.flags = + SMS_DEVICE_FAMILY2 | SMS_DEVICE_NOT_READY | + SMS_ROM_NO_RESPONSE; + params.preload_handler = smsspi_preload; + params.postload_handler = smsspi_postload; + } + + ret = smscore_register_device(¶ms, &spi_device->coredev); + if (ret < 0) { + printk(KERN_INFO "%s smscore_register_device(...) failed\n", + __func__); + goto reg_device_error; + } + + ret = smscore_start_device(spi_device->coredev); + if (ret < 0) { + printk(KERN_INFO "%s smscore_start_device(...) failed\n", + __func__); + goto start_device_error; + } + + PDEBUG("exiting\n"); + return 0; + +start_device_error: + smscore_unregister_device(spi_device->coredev); + +reg_device_error: + +common_error: + smsspiphy_deinit(spi_device->phy_dev); + +phy_error: + dma_free_coherent(NULL, TX_BUFFER_SIZE, spi_device->txbuf, + spi_device->txbuf_phy_addr); + +txbuf_error: + platform_device_unregister(&smsspi_device); + + PDEBUG("exiting error %d\n", ret); + + return ret; +} + +void smsspi_unregister(void) +{ + spi_device_st *spi_device = spi_dev; + PDEBUG("entering\n"); + + /* stop interrupts */ + smsspiphy_deinit(spi_device->phy_dev); + smscore_unregister_device(spi_device->coredev); + + dma_free_coherent(NULL, TX_BUFFER_SIZE, spi_device->txbuf, + spi_device->txbuf_phy_addr); + + platform_device_unregister(&smsspi_device); + PDEBUG("exiting\n"); +} diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspiphy.h v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspiphy.h --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspiphy.h 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspiphy.h 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,36 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ + +#ifndef __SMS_SPI_PHY_H__ +#define __SMS_SPI_PHY_H__ + +void smsspibus_xfer(void *context, unsigned char *txbuf, + unsigned long txbuf_phy_addr, unsigned char *rxbuf, + unsigned long rxbuf_phy_addr, int len); +void *smsspiphy_init(void *context, void (*smsspi_interruptHandler) (void *), + void *intr_context); +void smsspiphy_deinit(void *context); +void smschipreset(void *context); +void WriteFWtoStellar(void *pSpiPhy, unsigned char *pFW, unsigned long Len); +void prepareForFWDnl(void *pSpiPhy); +void fwDnlComplete(void *context, int App); + +#endif /* __SMS_SPI_PHY_H__ */ diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspiphy_pxa.c v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspiphy_pxa.c --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspiphy_pxa.c 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspiphy_pxa.c 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,435 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +#define PXA_310_LV + +#include <linux/kernel.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#ifdef PXA_310_LV +#include <asm/arch/ssp.h> +#include <asm/arch/mfp.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pxa3xx_gpio.h> +#endif +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/dma-mapping.h> +#include <asm/dma.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include "smsdbg_prn.h" + +#ifdef PXA_310_LV + +#define SSP_PORT 4 +#define SSP_CKEN CKEN_SSP4 +#define SMS_IRQ_GPIO MFP_PIN_GPIO93 + +#if (SSP_PORT == 1) +#define SDCMR_RX DRCMRRXSSDR +#define SDCMR_TX DRCMRTXSSDR +#else +#if (SSP_PORT == 2) +#define SDCMR_RX DRCMR15 +#define SDCMR_TX DRCMR16 +#else +#if (SSP_PORT == 3) +#define SDCMR_RX DRCMR66 +#define SDCMR_TX DRCMR67 +#else +#if (SSP_PORT == 4) +#define SDCMR_RX DRCMRRXSADR +#define SDCMR_TX DRCMRTXSADR +#endif +#endif +#endif +#endif +#else /*PXA_310_LV */ +#define SSP_PORT 1 +#define SDCMR_RX DRCMRRXSSDR +#define SDCMR_TX DRCMRTXSSDR + +#endif /*PXA_310_LV */ + +/* Macros defining physical layer behaviour*/ +#ifdef PXA_310_LV +#define CLOCK_FACTOR 1 +#else /*PXA_310_LV */ +#define CLOCK_FACTOR 2 +#endif /*PXA_310_LV */ + +/* Macros for coding reuse */ + +/*! macro to align the divider to the proper offset in the register bits */ +#define CLOCK_DIVIDER(i)((i-1)<<8) /* 1-4096 */ + +/*! DMA related macros */ +#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) +#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) + +#define SSP_TIMEOUT_SCALE (769) +#define SSP_TIMEOUT(x) ((x*10000)/SSP_TIMEOUT_SCALE) + +#define SPI_PACKET_SIZE 256 + +/* physical layer variables */ +/*! global bus data */ +struct { + struct ssp_dev sspdev; /*!< ssp port configuration */ + struct completion transfer_in_process; + void (*interruptHandler) (void *); + void *intr_context; + struct device *dev; /*!< device model stuff */ + int rx_dma_channel; + int tx_dma_channel; + int rx_buf_len; + int tx_buf_len; +} spiphy_dev_st; + +/*! +invert the endianness of a single 32it integer + +\param[in] u: word to invert + +\return the inverted word +*/ +static inline u32 invert_bo(u32 u) +{ + return ((u & 0xff) << 24) | ((u & 0xff00) << 8) | ((u & 0xff0000) >> 8) + | ((u & 0xff000000) >> 24); +} + +/*! +invert the endianness of a data buffer + +\param[in] buf: buffer to invert +\param[in] len: buffer length + +\return the inverted word +*/ + +static int invert_endianness(char *buf, int len) +{ + int i; + u32 *ptr = (u32 *) buf; + + len = (len + 3) / 4; + for (i = 0; i < len; i++, ptr++) + *ptr = invert_bo(*ptr); + + return 4 * ((len + 3) & (~3)); +} + +/*! Map DMA buffers when request starts + +\return error status +*/ +static unsigned long dma_map_buf(spiphy_dev_st *spiphy_dev, char *buf, int len, + int direction) +{ + unsigned long phyaddr; + /* map dma buffers */ + if (!buf) { + PERROR(" NULL buffers to map\n"); + return 0; + } + /* map buffer */ + phyaddr = dma_map_single(spiphy_dev->dev, buf, len, direction); + if (dma_mapping_error(phyaddr)) { + PERROR("exiting with error\n"); + return 0; + } + return phyaddr; +} + +static irqreturn_t spibus_interrupt(int irq, void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + PDEBUG("recieved interrupt from device dev=%p.\n", context); + if (spiphy_dev->interruptHandler) + spiphy_dev->interruptHandler(spiphy_dev->intr_context); + return IRQ_HANDLED; + +} + +/*! DMA controller callback - called only on BUS error condition + +\param[in] channel: DMA channel with error +\param[in] data: Unused +\param[in] regs: Unused +\return void +*/ +static void spibus_dma_handler(int channel, void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + u32 irq_status = DCSR(channel) & DMA_INT_MASK; + + PDEBUG("recieved interrupt from dma channel %d irq status %x.\n", + channel, irq_status); + if (irq_status & DCSR_BUSERR) { + PERROR("bus error!!! resetting channel %d\n", channel); + + DCSR(spiphy_dev->rx_dma_channel) = RESET_DMA_CHANNEL; + DCSR(spiphy_dev->tx_dma_channel) = RESET_DMA_CHANNEL; + } + DCSR(spiphy_dev->rx_dma_channel) = RESET_DMA_CHANNEL; + complete(&spiphy_dev->transfer_in_process); +} + +void smsspibus_xfer(void *context, unsigned char *txbuf, + unsigned long txbuf_phy_addr, unsigned char *rxbuf, + unsigned long rxbuf_phy_addr, int len) +{ + /* DMA burst is 8 bytes, therefore we need tmp buffer that size. */ + unsigned long tmp[2]; + unsigned long txdma; + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + + /* program the controller */ + if (txbuf) + invert_endianness(txbuf, len); + + tmp[0] = -1; + tmp[1] = -1; + + /* map RX buffer */ + + if (!txbuf) + txdma = + dma_map_buf(spiphy_dev, (char *)tmp, sizeof(tmp), + DMA_TO_DEVICE); + else + txdma = txbuf_phy_addr; + + init_completion(&spiphy_dev->transfer_in_process); + /* configure DMA Controller */ + DCSR(spiphy_dev->rx_dma_channel) = RESET_DMA_CHANNEL; + DSADR(spiphy_dev->rx_dma_channel) = __PREG(SSDR_P(SSP_PORT)); + DTADR(spiphy_dev->rx_dma_channel) = rxbuf_phy_addr; + DCMD(spiphy_dev->rx_dma_channel) = DCMD_INCTRGADDR | DCMD_FLOWSRC + | DCMD_WIDTH4 | DCMD_ENDIRQEN | DCMD_BURST8 | len; + PDEBUG("rx channel=%d, src=0x%x, dst=0x%x, cmd=0x%x\n", + spiphy_dev->rx_dma_channel, __PREG(SSDR_P(SSP_PORT)), + (unsigned int)rxbuf_phy_addr, DCMD(spiphy_dev->rx_dma_channel)); + spiphy_dev->rx_buf_len = len; + + DCSR(spiphy_dev->tx_dma_channel) = RESET_DMA_CHANNEL; + DTADR(spiphy_dev->tx_dma_channel) = __PREG(SSDR_P(SSP_PORT)); + DSADR(spiphy_dev->tx_dma_channel) = txdma; + if (txbuf) { + DCMD(spiphy_dev->tx_dma_channel) = + DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_WIDTH4 + /* | DCMD_ENDIRQEN */ | DCMD_BURST8 | len; + spiphy_dev->tx_buf_len = len; + } else { + DCMD(spiphy_dev->tx_dma_channel) = DCMD_FLOWTRG + | DCMD_WIDTH4 /* | DCMD_ENDIRQEN */ | DCMD_BURST8 | len; + spiphy_dev->tx_buf_len = 4; + } + + PDEBUG("tx channel=%d, src=0x%x, dst=0x%x, cmd=0x%x\n", + spiphy_dev->tx_dma_channel, (unsigned int)txdma, + __PREG(SSDR_P(SSP_PORT)), DCMD(spiphy_dev->tx_dma_channel)); + /* DALGN - DMA ALIGNMENT REG. */ + if (rxbuf_phy_addr & 0x7) + DALGN |= (1 << spiphy_dev->rx_dma_channel); + else + DALGN &= ~(1 << spiphy_dev->rx_dma_channel); + if (txdma & 0x7) + DALGN |= (1 << spiphy_dev->tx_dma_channel); + else + DALGN &= ~(1 << spiphy_dev->tx_dma_channel); + + /* Start DMA controller */ + DCSR(spiphy_dev->rx_dma_channel) |= DCSR_RUN; + DCSR(spiphy_dev->tx_dma_channel) |= DCSR_RUN; + PDEBUG("DMA running. wait for completion.\n"); + wait_for_completion(&spiphy_dev->transfer_in_process); + PDEBUG("DMA complete.\n"); + invert_endianness(rxbuf, len); +} + +void smschipreset(void *context) +{ + +} + +void *smsspiphy_init(void *context, void (*smsspi_interruptHandler) (void *), + void *intr_context) +{ + int ret; + spiphy_dev_st *spiphy_dev; + u32 mode = 0, flags = 0, psp_flags = 0, speed = 0; + PDEBUG("entering\n"); + + spiphy_dev = kmalloc(sizeof(spiphy_dev_st), GFP_KERNEL); + + ret = ssp_init(&spiphy_dev->sspdev, SSP_PORT, 0); + if (ret) { + PERROR("ssp_init failed. error %d", ret); + goto error_sspinit; + } +#ifdef PXA_310_LV + pxa3xx_mfp_set_afds(SMS_IRQ_GPIO, MFP_AF0, MFP_DS03X); + pxa3xx_gpio_set_rising_edge_detect(SMS_IRQ_GPIO, 1); + pxa3xx_gpio_set_direction(SMS_IRQ_GPIO, GPIO_DIR_IN); +#else /*PXA_310_LV */ + /* receive input interrupts from the SMS 1000 on J32 pin 11 */ + pxa_gpio_mode(22 | GPIO_IN); +#endif /*PXA_310_LV */ + speed = CLOCK_DIVIDER(CLOCK_FACTOR); /* clock divisor for this mode */ + /* 32bit words in the fifo */ + mode = SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS; + /* SSCR1 = flags */ + flags = SSCR1_RxTresh(1) | SSCR1_TxTresh(1) | SSCR1_TSRE | + SSCR1_RSRE | SSCR1_RIE | SSCR1_TRAIL; /* | SSCR1_TIE */ + + ssp_config(&spiphy_dev->sspdev, mode, flags, psp_flags, speed); + ssp_disable(&(spiphy_dev->sspdev)); +#ifdef PXA_310_LV + + pxa3xx_mfp_set_afds(MFP_PIN_GPIO95, MFP_AF1, MFP_DS03X); + pxa3xx_mfp_set_afds(MFP_PIN_GPIO96, MFP_AF1, MFP_DS03X); + pxa3xx_mfp_set_afds(MFP_PIN_GPIO97, MFP_AF1, MFP_DS03X); + pxa3xx_mfp_set_afds(MFP_PIN_GPIO98, MFP_AF1, MFP_DS03X); +#else /*PXA_310_LV */ + pxa_gpio_mode(GPIO23_SCLK_MD); + pxa_gpio_mode(GPIO24_SFRM_MD); + pxa_gpio_mode(GPIO25_STXD_MD); + pxa_gpio_mode(GPIO26_SRXD_MD); +#endif /*PXA_310_LV */ + /* setup the dma */ + spiphy_dev->rx_dma_channel = + pxa_request_dma("spibusdrv_rx", DMA_PRIO_HIGH, spibus_dma_handler, + spiphy_dev); + if (spiphy_dev->rx_dma_channel < 0) { + ret = -EBUSY; + PERROR("Could not get RX DMA channel.\n"); + goto error_rxdma; + } + spiphy_dev->tx_dma_channel = + pxa_request_dma("spibusdrv_tx", DMA_PRIO_HIGH, spibus_dma_handler, + spiphy_dev); + if (spiphy_dev->tx_dma_channel < 0) { + ret = -EBUSY; + PERROR("Could not get TX DMA channel.\n"); + goto error_txdma; + } + + SDCMR_RX = DRCMR_MAPVLD | spiphy_dev->rx_dma_channel; + SDCMR_TX = DRCMR_MAPVLD | spiphy_dev->tx_dma_channel; + + PDEBUG("dma rx channel: %d, dma tx channel: %d\n", + spiphy_dev->rx_dma_channel, spiphy_dev->tx_dma_channel); + /* enable the clock */ + + spiphy_dev->interruptHandler = smsspi_interruptHandler; + spiphy_dev->intr_context = intr_context; +#ifdef PXA_310_LV + set_irq_type(IRQ_GPIO(MFP2GPIO(SMS_IRQ_GPIO)), IRQT_FALLING); + ret = + request_irq(IRQ_GPIO(MFP2GPIO(SMS_IRQ_GPIO)), spibus_interrupt, + SA_INTERRUPT, "SMSSPI", spiphy_dev); +#else /*PXA_310_LV */ + set_irq_type(IRQ_GPIO(22), IRQT_FALLING); + ret = + request_irq(IRQ_GPIO(22), spibus_interrupt, SA_INTERRUPT, "SMSSPI", + &(g_spidata.sspdev)); +#endif /*PXA_310_LV */ + if (ret) { + PERROR("Could not get interrupt for SMS device. status =%d\n", + ret); + goto error_irq; + } + + ssp_enable(&(spiphy_dev->sspdev)); + PDEBUG("exiting\n"); + return spiphy_dev; +error_irq: + if (spiphy_dev->tx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->tx_dma_channel); + +error_txdma: + if (spiphy_dev->rx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->rx_dma_channel); + +error_rxdma: + ssp_exit(&spiphy_dev->sspdev); +error_sspinit: + PDEBUG("exiting on error\n"); + return 0; +} + +int smsspiphy_deinit(void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + PDEBUG("entering\n"); + + /* disable the spi port */ + ssp_flush(&spiphy_dev->sspdev); + ssp_disable(&spiphy_dev->sspdev); + + /* release DMA resources */ + if (spiphy_dev->rx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->rx_dma_channel); + + if (spiphy_dev->tx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->tx_dma_channel); + + /* release Memory resources */ +#ifdef PXA_310_LV + free_irq(IRQ_GPIO(MFP2GPIO(SMS_IRQ_GPIO)), spiphy_dev); +#else /*PXA_310_LV */ + free_irq(IRQ_GPIO(22), &spiphy_dev->sspdev); +#endif /*PXA_310_LV */ + ssp_exit(&spiphy_dev->sspdev); + PDEBUG("exiting\n"); + return 0; +} + +void smsspiphy_set_config(spiphy_dev_st *spiphy_dev, int clock_divider) +{ + u32 mode, flags, speed, psp_flags = 0; + ssp_disable(&spiphy_dev->sspdev); + /* clock divisor for this mode. */ + speed = CLOCK_DIVIDER(clock_divider); + /* 32bit words in the fifo */ + mode = SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS; + flags = SSCR1_RxTresh(1) | SSCR1_TxTresh(1) | SSCR1_TSRE | + SSCR1_RSRE | SSCR1_RIE | SSCR1_TRAIL; /* | SSCR1_TIE */ + ssp_config(&spiphy_dev->sspdev, mode, flags, psp_flags, speed); + ssp_enable(&spiphy_dev->sspdev); +} + +void prepareForFWDnl(void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + smsspiphy_set_config(spiphy_dev, 2); + msleep(100); +} + +void fwDnlComplete(void *context, int App) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + smsspiphy_set_config(spiphy_dev, 1); + msleep(100); +} diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspiphy_pxa.c~ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspiphy_pxa.c~ --- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsspiphy_pxa.c~ 1970-01-01 02:00:00.000000000 +0200 +++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsspiphy_pxa.c~ 2008-11-18 18:13:37.000000000 +0200 @@ -0,0 +1,434 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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 2 of the License, or +(at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. + +****************************************************************/ +#define PXA_310_LV + +#include <linux/kernel.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#ifdef PXA_310_LV +#include <asm/arch/ssp.h> +#include <asm/arch/mfp.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pxa3xx_gpio.h> +#endif +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/dma-mapping.h> +#include <asm/dma.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include "smsdbg_prn.h" + +#ifdef PXA_310_LV + +#define SSP_PORT 4 +#define SSP_CKEN CKEN_SSP4 +#define SMS_IRQ_GPIO MFP_PIN_GPIO93 + +#if (SSP_PORT == 1) +#define SDCMR_RX DRCMRRXSSDR +#define SDCMR_TX DRCMRTXSSDR +#else +#if (SSP_PORT == 2) +#define SDCMR_RX DRCMR15 +#define SDCMR_TX DRCMR16 +#else +#if (SSP_PORT == 3) +#define SDCMR_RX DRCMR66 +#define SDCMR_TX DRCMR67 +#else +#if (SSP_PORT == 4) +#define SDCMR_RX DRCMRRXSADR +#define SDCMR_TX DRCMRTXSADR +#endif +#endif +#endif +#endif +#else /*PXA_310_LV */ +#define SSP_PORT 1 +#define SDCMR_RX DRCMRRXSSDR +#define SDCMR_TX DRCMRTXSSDR + +#endif /*PXA_310_LV */ + +/* Macros defining physical layer behaviour*/ +#ifdef PXA_310_LV +#define CLOCK_FACTOR 1 +#else /*PXA_310_LV */ +#define CLOCK_FACTOR 2 +#endif /*PXA_310_LV */ + +/* Macros for coding reuse */ + +/*! macro to align the divider to the proper offset in the register bits */ +#define CLOCK_DIVIDER(i)((i-1)<<8) /* 1-4096 */ + +/*! DMA related macros */ +#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) +#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) + +#define SSP_TIMEOUT_SCALE (769) +#define SSP_TIMEOUT(x) ((x*10000)/SSP_TIMEOUT_SCALE) + +#define SPI_PACKET_SIZE 256 + +/* physical layer variables */ +/*! global bus data */ +struct { + struct ssp_dev sspdev; /*!< ssp port configuration */ + struct completion transfer_in_process; + void (*interruptHandler) (void *); + void *intr_context; + struct device *dev; /*!< device model stuff */ + int rx_dma_channel; + int tx_dma_channel; + int rx_buf_len; + int tx_buf_len; +} spiphy_dev_st; + +/*! +invert the endianness of a single 32it integer + +\param[in] u: word to invert + +\return the inverted word +*/ +static inline u32 invert_bo(u32 u) +{ + return ((u & 0xff) << 24) | ((u & 0xff00) << 8) | ((u & 0xff0000) >> 8) + | ((u & 0xff000000) >> 24); +} + +/*! +invert the endianness of a data buffer + +\param[in] buf: buffer to invert +\param[in] len: buffer length + +\return the inverted word +*/ + +static int invert_endianness(char *buf, int len) +{ + int i; + u32 *ptr = (u32 *) buf; + + len = (len + 3) / 4; + for (i = 0; i < len; i++, ptr++) + *ptr = invert_bo(*ptr); + + return 4 * ((len + 3) & (~3)); +} + +/*! Map DMA buffers when request starts + +\return error status +*/ +static unsigned long dma_map_buf(spiphy_dev_st *spiphy_dev, char *buf, int len, + int direction) +{ + unsigned long phyaddr; + /* map dma buffers */ + if (!buf) { + PERROR(" NULL buffers to map\n"); + return 0; + } + /* map buffer */ + phyaddr = dma_map_single(spiphy_dev->dev, buf, len, direction); + if (dma_mapping_error(phyaddr)) { + PERROR("exiting with error\n"); + return 0; + } + return phyaddr; +} + +static irqreturn_t spibus_interrupt(int irq, void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + PDEBUG("recieved interrupt from device dev=%p.\n", context); + if (spiphy_dev->interruptHandler) + spiphy_dev->interruptHandler(spiphy_dev->intr_context); + return IRQ_HANDLED; + +} + +/*! DMA controller callback - called only on BUS error condition + +\param[in] channel: DMA channel with error +\param[in] data: Unused +\param[in] regs: Unused +\return void +*/ +static void spibus_dma_handler(int channel, void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + u32 irq_status = DCSR(channel) & DMA_INT_MASK; + + PDEBUG("recieved interrupt from dma channel %d irq status %x.\n", + channel, irq_status); + if (irq_status & DCSR_BUSERR) { + PERROR("bus error!!! resetting channel %d\n", channel); + + DCSR(spiphy_dev->rx_dma_channel) = RESET_DMA_CHANNEL; + DCSR(spiphy_dev->tx_dma_channel) = RESET_DMA_CHANNEL; + } + DCSR(spiphy_dev->rx_dma_channel) = RESET_DMA_CHANNEL; + complete(&spiphy_dev->transfer_in_process); +} + +void smsspibus_xfer(void *context, unsigned char *txbuf, + unsigned long txbuf_phy_addr, unsigned char *rxbuf, + unsigned long rxbuf_phy_addr, int len) +{ + /* DMA burst is 8 bytes, therefore we need tmp buffer that size. */ + unsigned long tmp[2]; + unsigned long txdma; + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + + /* program the controller */ + if (txbuf) + invert_endianness(txbuf, len); + + tmp[0] = -1; + tmp[1] = -1; + + /* map RX buffer */ + + if (!txbuf) + txdma = + dma_map_buf(spiphy_dev, (char *)tmp, sizeof(tmp), + DMA_TO_DEVICE); + else + txdma = txbuf_phy_addr; + + init_completion(&spiphy_dev->transfer_in_process); + /* configure DMA Controller */ + DCSR(spiphy_dev->rx_dma_channel) = RESET_DMA_CHANNEL; + DSADR(spiphy_dev->rx_dma_channel) = __PREG(SSDR_P(SSP_PORT)); + DTADR(spiphy_dev->rx_dma_channel) = rxbuf_phy_addr; + DCMD(spiphy_dev->rx_dma_channel) = DCMD_INCTRGADDR | DCMD_FLOWSRC + | DCMD_WIDTH4 | DCMD_ENDIRQEN | DCMD_BURST8 | len; + PDEBUG("rx channel=%d, src=0x%x, dst=0x%x, cmd=0x%x\n", + spiphy_dev->rx_dma_channel, __PREG(SSDR_P(SSP_PORT)), + (unsigned int)rxbuf_phy_addr, DCMD(spiphy_dev->rx_dma_channel)); + spiphy_dev->rx_buf_len = len; + + DCSR(spiphy_dev->tx_dma_channel) = RESET_DMA_CHANNEL; + DTADR(spiphy_dev->tx_dma_channel) = __PREG(SSDR_P(SSP_PORT)); + DSADR(spiphy_dev->tx_dma_channel) = txdma; + if (txbuf) { + DCMD(spiphy_dev->tx_dma_channel) = + DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_WIDTH4 + /* | DCMD_ENDIRQEN */ | DCMD_BURST8 | len; + spiphy_dev->tx_buf_len = len; + } else { + DCMD(spiphy_dev->tx_dma_channel) = DCMD_FLOWTRG + | DCMD_WIDTH4 /* | DCMD_ENDIRQEN */ | DCMD_BURST8 | len; + spiphy_dev->tx_buf_len = 4; + } + + PDEBUG("tx channel=%d, src=0x%x, dst=0x%x, cmd=0x%x\n", + spiphy_dev->tx_dma_channel, (unsigned int)txdma, + __PREG(SSDR_P(SSP_PORT)), DCMD(spiphy_dev->tx_dma_channel)); + /* DALGN - DMA ALIGNMENT REG. */ + if (rxbuf_phy_addr & 0x7) + DALGN |= (1 << spiphy_dev->rx_dma_channel); + else + DALGN &= ~(1 << spiphy_dev->rx_dma_channel); + if (txdma & 0x7) + DALGN |= (1 << spiphy_dev->tx_dma_channel); + else + DALGN &= ~(1 << spiphy_dev->tx_dma_channel); + + /* Start DMA controller */ + DCSR(spiphy_dev->rx_dma_channel) |= DCSR_RUN; + DCSR(spiphy_dev->tx_dma_channel) |= DCSR_RUN; + PDEBUG("DMA running. wait for completion.\n"); + wait_for_completion(&spiphy_dev->transfer_in_process); + PDEBUG("DMA complete.\n"); + invert_endianness(rxbuf, len); +} + +void smschipreset(void *context) +{ + +} + +void *smsspiphy_init(void *context, void (*smsspi_interruptHandler) (void *), + void *intr_context) +{ + int ret; + spiphy_dev_st *spiphy_dev; + u32 mode = 0, flags = 0, psp_flags = 0, speed = 0; + PDEBUG("entering\n"); + + spiphy_dev = kmalloc(sizeof(spiphy_dev_st), GFP_KERNEL); + + ret = ssp_init(&spiphy_dev->sspdev, SSP_PORT, 0); + if (ret) { + PERROR("ssp_init failed. error %d", ret); + goto error_sspinit; + } +#ifdef PXA_310_LV + pxa3xx_mfp_set_afds(SMS_IRQ_GPIO, MFP_AF0, MFP_DS03X); + pxa3xx_gpio_set_rising_edge_detect(SMS_IRQ_GPIO, 1); + pxa3xx_gpio_set_direction(SMS_IRQ_GPIO, GPIO_DIR_IN); +#else /*PXA_310_LV */ + /* receive input interrupts from the SMS 1000 on J32 pin 11 */ + pxa_gpio_mode(22 | GPIO_IN); +#endif /*PXA_310_LV */ + speed = CLOCK_DIVIDER(CLOCK_FACTOR); /* clock divisor for this mode */ + /* 32bit words in the fifo */ + mode = SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS; + /* SSCR1 = flags */ + flags = SSCR1_RxTresh(1) | SSCR1_TxTresh(1) | SSCR1_TSRE | + SSCR1_RSRE | SSCR1_RIE | SSCR1_TRAIL; /* | SSCR1_TIE */ + + ssp_config(&spiphy_dev->sspdev, mode, flags, psp_flags, speed); + ssp_disable(&(spiphy_dev->sspdev)); +#ifdef PXA_310_LV + + pxa3xx_mfp_set_afds(MFP_PIN_GPIO95, MFP_AF1, MFP_DS03X); + pxa3xx_mfp_set_afds(MFP_PIN_GPIO96, MFP_AF1, MFP_DS03X); + pxa3xx_mfp_set_afds(MFP_PIN_GPIO97, MFP_AF1, MFP_DS03X); + pxa3xx_mfp_set_afds(MFP_PIN_GPIO98, MFP_AF1, MFP_DS03X); +#else /*PXA_310_LV */ + pxa_gpio_mode(GPIO23_SCLK_MD); + pxa_gpio_mode(GPIO24_SFRM_MD); + pxa_gpio_mode(GPIO25_STXD_MD); + pxa_gpio_mode(GPIO26_SRXD_MD); +#endif /*PXA_310_LV */ + /* setup the dma */ + spiphy_dev->rx_dma_channel = + pxa_request_dma("spibusdrv_rx", DMA_PRIO_HIGH, spibus_dma_handler, + spiphy_dev); + if (spiphy_dev->rx_dma_channel < 0) { + ret = -EBUSY; + PERROR("Could not get RX DMA channel.\n"); + goto error_rxdma; + } + spiphy_dev->tx_dma_channel = + pxa_request_dma("spibusdrv_tx", DMA_PRIO_HIGH, spibus_dma_handler, + spiphy_dev); + if (spiphy_dev->tx_dma_channel < 0) { + ret = -EBUSY; + PERROR("Could not get TX DMA channel.\n"); + goto error_txdma; + } + + SDCMR_RX = DRCMR_MAPVLD | spiphy_dev->rx_dma_channel; + SDCMR_TX = DRCMR_MAPVLD | spiphy_dev->tx_dma_channel; + + PDEBUG("dma rx channel: %d, dma tx channel: %d\n", + spiphy_dev->rx_dma_channel, spiphy_dev->tx_dma_channel); + /* enable the clock */ + + spiphy_dev->interruptHandler = smsspi_interruptHandler; + spiphy_dev->intr_context = intr_context; +#ifdef PXA_310_LV + set_irq_type(IRQ_GPIO(MFP2GPIO(SMS_IRQ_GPIO)), IRQT_FALLING); + ret = + request_irq(IRQ_GPIO(MFP2GPIO(SMS_IRQ_GPIO)), spibus_interrupt, + SA_INTERRUPT, "SMSSPI", spiphy_dev); +#else /*PXA_310_LV */ + set_irq_type(IRQ_GPIO(22), IRQT_FALLING); + ret = + request_irq(IRQ_GPIO(22), spibus_interrupt, SA_INTERRUPT, "SMSSPI", + &(g_spidata.sspdev)); +#endif /*PXA_310_LV */ + if (ret) { + PERROR("Could not get interrupt for SMS device. status =%d\n", + ret); + goto error_irq; + } + + ssp_enable(&(spiphy_dev->sspdev)); + PDEBUG("exiting\n"); + return spiphy_dev; +error_irq: + if (spiphy_dev->tx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->tx_dma_channel); + +error_txdma: + if (spiphy_dev->rx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->rx_dma_channel); + +error_rxdma: + ssp_exit(&spiphy_dev->sspdev); +error_sspinit: + PDEBUG("exiting on error\n"); + return 0; +} + +int smsspiphy_deinit(void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + PDEBUG("entering\n"); + + /* disable the spi port */ + ssp_flush(&spiphy_dev->sspdev); + ssp_disable(&spiphy_dev->sspdev); + + /* release DMA resources */ + if (spiphy_dev->rx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->rx_dma_channel); + + if (spiphy_dev->tx_dma_channel >= 0) + pxa_free_dma(spiphy_dev->tx_dma_channel); + + /* release Memory resources */ +#ifdef PXA_310_LV + free_irq(IRQ_GPIO(MFP2GPIO(SMS_IRQ_GPIO)), spiphy_dev); +#else /*PXA_310_LV */ + free_irq(IRQ_GPIO(22), &spiphy_dev->sspdev); +#endif /*PXA_310_LV */ + ssp_exit(&spiphy_dev->sspdev); + PDEBUG("exiting\n"); + return 0; +} + +void smsspiphy_set_config(spiphy_dev_st *spiphy_dev, int clock_divider) +{ + u32 mode, flags, speed, psp_flags = 0; + ssp_disable(&spiphy_dev->sspdev); + /* clock divisor for this mode. */ + speed = CLOCK_DIVIDER(clock_divider); + /* 32bit words in the fifo */ + mode = SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS; + flags = SSCR1_RxTresh(1) | SSCR1_TxTresh(1) | SSCR1_TSRE | SSCR1_RSRE | SSCR1_RIE | SSCR1_TRAIL; /* | SSCR1_TIE */ + ssp_config(&spiphy_dev->sspdev, mode, flags, psp_flags, speed); + ssp_enable(&spiphy_dev->sspdev); +} + +void prepareForFWDnl(void *context) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + smsspiphy_set_config(spiphy_dev, 2); + msleep(100); +} + +void fwDnlComplete(void *context, int App) +{ + spiphy_dev_st *spiphy_dev = (spiphy_dev_st *) context; + smsspiphy_set_config(spiphy_dev, 1); + msleep(100); +}
_______________________________________________ linux-dvb mailing list linux-dvb@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb