From 716ca530e1c4515d8683c9d5be3d56b301758b66 Mon Sep 17 00:00:00 2001 From: James <> Date: Wed, 4 Nov 2015 11:49:21 +0000 Subject: trunk-47381 --- .../kernel/lantiq/ltq-adsl-mei/src/lantiq_mei.c | 2840 ++++++++++++++++++++ 1 file changed, 2840 insertions(+) create mode 100644 package/kernel/lantiq/ltq-adsl-mei/src/lantiq_mei.c (limited to 'package/kernel/lantiq/ltq-adsl-mei/src/lantiq_mei.c') diff --git a/package/kernel/lantiq/ltq-adsl-mei/src/lantiq_mei.c b/package/kernel/lantiq/ltq-adsl-mei/src/lantiq_mei.c new file mode 100644 index 0000000..561b300 --- /dev/null +++ b/package/kernel/lantiq/ltq-adsl-mei/src/lantiq_mei.c @@ -0,0 +1,2840 @@ +/****************************************************************************** + + Copyright (c) 2009 + Infineon Technologies AG + Am Campeon 1-12; 81726 Munich, Germany + + For licensing information, see the file 'LICENSE' in the root folder of + this software module. + +******************************************************************************/ + +/*! + \defgroup AMAZON_S_MEI Amazon-S MEI Driver Module + \brief Amazon-S MEI driver module + */ + +/*! + \defgroup Internal Compile Parametere + \ingroup AMAZON_S_MEI + \brief exported functions for other driver use + */ + +/*! + \file amazon_s_mei_bsp.c + \ingroup AMAZON_S_MEI + \brief Amazon-S MEI driver file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lantiq_atm.h" +#include +//#include "ifxmips_atm.h" +#define IFX_MEI_BSP +#include "ifxmips_mei_interface.h" + +/*#define LTQ_RCU_RST IFX_RCU_RST_REQ +#define LTQ_RCU_RST_REQ_ARC_JTAG IFX_RCU_RST_REQ_ARC_JTAG +#define LTQ_RCU_RST_REQ_DFE IFX_RCU_RST_REQ_DFE +#define LTQ_RCU_RST_REQ_AFE IFX_RCU_RST_REQ_AFE +#define IFXMIPS_FUSE_BASE_ADDR IFX_FUSE_BASE_ADDR +#define IFXMIPS_ICU_IM0_IER IFX_ICU_IM0_IER +#define IFXMIPS_ICU_IM2_IER IFX_ICU_IM2_IER +#define LTQ_MEI_INT IFX_MEI_INT +#define LTQ_MEI_DYING_GASP_INT IFX_MEI_DYING_GASP_INT +#define LTQ_MEI_BASE_ADDR IFX_MEI_SPACE_ACCESS +#define IFXMIPS_PMU_PWDCR IFX_PMU_PWDCR +#define IFXMIPS_MPS_CHIPID IFX_MPS_CHIPID + +#define ifxmips_port_reserve_pin ifx_gpio_pin_reserve +#define ifxmips_port_set_dir_in ifx_gpio_dir_in_set +#define ifxmips_port_clear_altsel0 ifx_gpio_altsel0_set +#define ifxmips_port_clear_altsel1 ifx_gpio_altsel1_clear +#define ifxmips_port_set_open_drain ifx_gpio_open_drain_clear +#define ifxmips_port_free_pin ifx_gpio_pin_free +#define ifxmips_mask_and_ack_irq bsp_mask_and_ack_irq +#define IFXMIPS_MPS_CHIPID_VERSION_GET IFX_MCD_CHIPID_VERSION_GET +#define ltq_r32(reg) __raw_readl(reg) +#define ltq_w32(val, reg) __raw_writel(val, reg) +#define ltq_w32_mask(clear, set, reg) ltq_w32((ltq_r32(reg) & ~clear) | set, reg) +*/ + +#define LTQ_RCU_BASE_ADDR 0x1F203000 +#define LTQ_ICU_BASE_ADDR 0x1F880200 +#define LTQ_MEI_BASE_ADDR 0x1E116000 +#define LTQ_PMU_BASE_ADDR 0x1F102000 +#define LTQ_MEI_DYING_GASP_INT (INT_NUM_IM1_IRL0 + 21) +#define LTQ_USB_OC_INT (INT_NUM_IM4_IRL0 + 23) +#define LTQ_MEI_INT (INT_NUM_IM1_IRL0 + 23) + +#define LTQ_RCU_RST_REQ_DFE (1 << 7) +#define LTQ_RCU_RST_REQ_AFE (1 << 11) + +#define LTQ_PMU_BASE (KSEG1 + LTQ_PMU_BASE_ADDR) +#define LTQ_RCU_BASE (KSEG1 + LTQ_RCU_BASE_ADDR) +#define LTQ_ICU_BASE (KSEG1 + LTQ_ICU_BASE_ADDR) + +#define LTQ_PMU_PWDCR ((u32 *)(LTQ_PMU_BASE + 0x001C)) +#define LTQ_PMU_PWDSR ((u32 *)(LTQ_PMU_BASE + 0x0020)) +#define LTQ_RCU_RST ((u32 *)(LTQ_RCU_BASE + 0x0010)) +#define LTQ_RCU_RST_ALL 0x40000000 + +#define LTQ_ICU_IM0_ISR ((u32 *)(LTQ_ICU_BASE + 0x0000)) +#define LTQ_ICU_IM0_IER ((u32 *)(LTQ_ICU_BASE + 0x0008)) +#define LTQ_ICU_IM0_IOSR ((u32 *)(LTQ_ICU_BASE + 0x0010)) +#define LTQ_ICU_IM0_IRSR ((u32 *)(LTQ_ICU_BASE + 0x0018)) +#define LTQ_ICU_IM0_IMR ((u32 *)(LTQ_ICU_BASE + 0x0020)) + + +#define LTQ_ICU_IM1_ISR ((u32 *)(LTQ_ICU_BASE + 0x0028)) +#define LTQ_ICU_IM2_ISR ((u32 *)(LTQ_ICU_BASE + 0x0050)) +#define LTQ_ICU_IM3_ISR ((u32 *)(LTQ_ICU_BASE + 0x0078)) +#define LTQ_ICU_IM4_ISR ((u32 *)(LTQ_ICU_BASE + 0x00A0)) + +#define LTQ_ICU_OFFSET (LTQ_ICU_IM1_ISR - LTQ_ICU_IM0_ISR) +#define LTQ_ICU_IM2_IER (LTQ_ICU_IM0_IER + LTQ_ICU_OFFSET) + +#define IFX_MEI_EMSG(fmt, args...) pr_err("[%s %d]: " fmt,__FUNCTION__, __LINE__, ## args) +#define IFX_MEI_DMSG(fmt, args...) pr_debug("[%s %d]: " fmt,__FUNCTION__, __LINE__, ## args) + +#define LTQ_FUSE_BASE (KSEG1 + 0x1F107354) + +#ifdef CONFIG_LTQ_MEI_FW_LOOPBACK +//#define DFE_MEM_TEST +//#define DFE_PING_TEST +#define DFE_ATM_LOOPBACK + + +#ifdef DFE_ATM_LOOPBACK +#include +#endif + +void dfe_loopback_irq_handler (DSL_DEV_Device_t *pDev); + +#endif //CONFIG_AMAZON_S_MEI_FW_LOOPBACK + +DSL_DEV_Version_t bsp_mei_version = { + major: 5, + minor: 0, + revision:0 +}; +DSL_DEV_HwVersion_t bsp_chip_info; + +#define IFX_MEI_DEVNAME "ifx_mei" +#define BSP_MAX_DEVICES 1 +#define MEI_DIRNAME "ifxmips_mei" + +DSL_DEV_MeiError_t DSL_BSP_FWDownload (DSL_DEV_Device_t *, const char *, unsigned long, long *, long *); +DSL_DEV_MeiError_t DSL_BSP_Showtime (DSL_DEV_Device_t *, DSL_uint32_t, DSL_uint32_t); +DSL_DEV_MeiError_t DSL_BSP_AdslLedInit (DSL_DEV_Device_t *, DSL_DEV_LedId_t, DSL_DEV_LedType_t, DSL_DEV_LedHandler_t); +//DSL_DEV_MeiError_t DSL_BSP_AdslLedSet (DSL_DEV_Device_t *, DSL_DEV_LedId_t, DSL_DEV_LedMode_t); +DSL_DEV_MeiError_t DSL_BSP_MemoryDebugAccess (DSL_DEV_Device_t *, DSL_BSP_MemoryAccessType_t, DSL_uint32_t, DSL_uint32_t*, DSL_uint32_t); +DSL_DEV_MeiError_t DSL_BSP_SendCMV (DSL_DEV_Device_t *, u16 *, int, u16 *); + +int DSL_BSP_KernelIoctls (DSL_DEV_Device_t *, unsigned int, unsigned long); + +static DSL_DEV_MeiError_t IFX_MEI_RunAdslModem (DSL_DEV_Device_t *); +static DSL_DEV_MeiError_t IFX_MEI_CpuModeSet (DSL_DEV_Device_t *, DSL_DEV_CpuMode_t); +static DSL_DEV_MeiError_t IFX_MEI_DownloadBootCode (DSL_DEV_Device_t *); +static DSL_DEV_MeiError_t IFX_MEI_ArcJtagEnable (DSL_DEV_Device_t *, int); +static DSL_DEV_MeiError_t IFX_MEI_AdslMailboxIRQEnable (DSL_DEV_Device_t *, int); + +static int IFX_MEI_GetPage (DSL_DEV_Device_t *, u32, u32, u32, u32 *, u32 *); +static int IFX_MEI_BarUpdate (DSL_DEV_Device_t *, int); + +static ssize_t IFX_MEI_Write (DSL_DRV_file_t *, const char *, size_t, loff_t *); +static long IFX_MEI_UserIoctls (DSL_DRV_file_t *, unsigned int, unsigned long); +static int IFX_MEI_Open (DSL_DRV_inode_t *, DSL_DRV_file_t *); +static int IFX_MEI_Release (DSL_DRV_inode_t *, DSL_DRV_file_t *); + +void AMAZON_SE_MEI_ARC_MUX_Test(void); + +void IFX_MEI_ARC_MUX_Test(void); + +static int adsl_dummy_ledcallback(void); + +int (*ifx_mei_atm_showtime_enter)(struct port_cell_info *, void *) = NULL; +EXPORT_SYMBOL(ifx_mei_atm_showtime_enter); + +int (*ifx_mei_atm_showtime_exit)(void) = NULL; +EXPORT_SYMBOL(ifx_mei_atm_showtime_exit); + +static int (*g_adsl_ledcallback)(void) = adsl_dummy_ledcallback; + +static unsigned int g_tx_link_rate[2] = {0}; + +static void *g_xdata_addr = NULL; + +static u32 *mei_arc_swap_buff = NULL; // holding swap pages + +extern void ltq_mask_and_ack_irq(struct irq_data *d); +static void inline MEI_MASK_AND_ACK_IRQ(int x) +{ + struct irq_data d; + d.hwirq = x; + ltq_mask_and_ack_irq(&d); +} +#define MEI_MAJOR 105 +static int dev_major = MEI_MAJOR; + +static struct file_operations bsp_mei_operations = { + owner:THIS_MODULE, + open:IFX_MEI_Open, + release:IFX_MEI_Release, + write:IFX_MEI_Write, + unlocked_ioctl:IFX_MEI_UserIoctls, +}; + +static DSL_DEV_Device_t dsl_devices[BSP_MAX_DEVICES]; + +static ifx_mei_device_private_t + sDanube_Mei_Private[BSP_MAX_DEVICES]; + +static DSL_BSP_EventCallBack_t dsl_bsp_event_callback[DSL_BSP_CB_LAST + 1]; + +/** + * Write a value to register + * This function writes a value to danube register + * + * \param ul_address The address to write + * \param ul_data The value to write + * \ingroup Internal + */ +static void +IFX_MEI_LongWordWrite (u32 ul_address, u32 ul_data) +{ + IFX_MEI_WRITE_REGISTER_L (ul_data, ul_address); + wmb(); + return; +} + +/** + * Write a value to register + * This function writes a value to danube register + * + * \param pDev the device pointer + * \param ul_address The address to write + * \param ul_data The value to write + * \ingroup Internal + */ +static void +IFX_MEI_LongWordWriteOffset (DSL_DEV_Device_t * pDev, u32 ul_address, + u32 ul_data) +{ + IFX_MEI_WRITE_REGISTER_L (ul_data, pDev->base_address + ul_address); + wmb(); + return; +} + +/** + * Read the danube register + * This function read the value from danube register + * + * \param ul_address The address to write + * \param pul_data Pointer to the data + * \ingroup Internal + */ +static void +IFX_MEI_LongWordRead (u32 ul_address, u32 * pul_data) +{ + *pul_data = IFX_MEI_READ_REGISTER_L (ul_address); + rmb(); + return; +} + +/** + * Read the danube register + * This function read the value from danube register + * + * \param pDev the device pointer + * \param ul_address The address to write + * \param pul_data Pointer to the data + * \ingroup Internal + */ +static void +IFX_MEI_LongWordReadOffset (DSL_DEV_Device_t * pDev, u32 ul_address, + u32 * pul_data) +{ + *pul_data = IFX_MEI_READ_REGISTER_L (pDev->base_address + ul_address); + rmb(); + return; +} + +/** + * Write several DWORD datas to ARC memory via ARC DMA interface + * This function writes several DWORD datas to ARC memory via DMA interface. + * + * \param pDev the device pointer + * \param destaddr The address to write + * \param databuff Pointer to the data buffer + * \param databuffsize Number of DWORDs to write + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_DMAWrite (DSL_DEV_Device_t * pDev, u32 destaddr, + u32 * databuff, u32 databuffsize) +{ + u32 *p = databuff; + u32 temp; + + if (destaddr & 3) + return DSL_DEV_MEI_ERR_FAILURE; + + // Set the write transfer address + IFX_MEI_LongWordWriteOffset (pDev, ME_DX_AD, destaddr); + + // Write the data pushed across DMA + while (databuffsize--) { + temp = *p; + if (destaddr == MEI_TO_ARC_MAILBOX) + MEI_HALF_WORD_SWAP (temp); + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DX_DATA, temp); + p++; + } + + return DSL_DEV_MEI_ERR_SUCCESS; + +} + +/** + * Read several DWORD datas from ARC memory via ARC DMA interface + * This function reads several DWORD datas from ARC memory via DMA interface. + * + * \param pDev the device pointer + * \param srcaddr The address to read + * \param databuff Pointer to the data buffer + * \param databuffsize Number of DWORDs to read + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_DMARead (DSL_DEV_Device_t * pDev, u32 srcaddr, u32 * databuff, + u32 databuffsize) +{ + u32 *p = databuff; + u32 temp; + + if (srcaddr & 3) + return DSL_DEV_MEI_ERR_FAILURE; + + // Set the read transfer address + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DX_AD, srcaddr); + + // Read the data popped across DMA + while (databuffsize--) { + IFX_MEI_LongWordReadOffset (pDev, (u32) ME_DX_DATA, &temp); + if (databuff == (u32 *) DSL_DEV_PRIVATE(pDev)->CMV_RxMsg) // swap half word + MEI_HALF_WORD_SWAP (temp); + *p = temp; + p++; + } + + return DSL_DEV_MEI_ERR_SUCCESS; + +} + +/** + * Switch the ARC control mode + * This function switchs the ARC control mode to JTAG mode or MEI mode + * + * \param pDev the device pointer + * \param mode The mode want to switch: JTAG_MASTER_MODE or MEI_MASTER_MODE. + * \ingroup Internal + */ +static void +IFX_MEI_ControlModeSet (DSL_DEV_Device_t * pDev, int mode) +{ + u32 temp = 0x0; + + IFX_MEI_LongWordReadOffset (pDev, (u32) ME_DBG_MASTER, &temp); + switch (mode) { + case JTAG_MASTER_MODE: + temp &= ~(HOST_MSTR); + break; + case MEI_MASTER_MODE: + temp |= (HOST_MSTR); + break; + default: + IFX_MEI_EMSG ("IFX_MEI_ControlModeSet: unkonwn mode [%d]\n", mode); + return; + } + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DBG_MASTER, temp); +} + +/** + * Disable ARC to MEI interrupt + * + * \param pDev the device pointer + * \ingroup Internal + */ +static void +IFX_MEI_IRQDisable (DSL_DEV_Device_t * pDev) +{ + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ARC2ME_MASK, 0x0); +} + +/** + * Eable ARC to MEI interrupt + * + * \param pDev the device pointer + * \ingroup Internal + */ +static void +IFX_MEI_IRQEnable (DSL_DEV_Device_t * pDev) +{ + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ARC2ME_MASK, MSGAV_EN); +} + +/** + * Poll for transaction complete signal + * This function polls and waits for transaction complete signal. + * + * \param pDev the device pointer + * \ingroup Internal + */ +static void +meiPollForDbgDone (DSL_DEV_Device_t * pDev) +{ + u32 query = 0; + int i = 0; + + while (i < WHILE_DELAY) { + IFX_MEI_LongWordReadOffset (pDev, (u32) ME_ARC2ME_STAT, &query); + query &= (ARC_TO_MEI_DBG_DONE); + if (query) + break; + i++; + if (i == WHILE_DELAY) { + IFX_MEI_EMSG ("PollforDbg fail!\n"); + } + } + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ARC2ME_STAT, ARC_TO_MEI_DBG_DONE); // to clear this interrupt +} + +/** + * ARC Debug Memory Access for a single DWORD reading. + * This function used for direct, address-based access to ARC memory. + * + * \param pDev the device pointer + * \param DEC_mode ARC memory space to used + * \param address Address to read + * \param data Pointer to data + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +_IFX_MEI_DBGLongWordRead (DSL_DEV_Device_t * pDev, u32 DEC_mode, + u32 address, u32 * data) +{ + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DBG_DECODE, DEC_mode); + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DBG_RD_AD, address); + meiPollForDbgDone (pDev); + IFX_MEI_LongWordReadOffset (pDev, (u32) ME_DBG_DATA, data); + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * ARC Debug Memory Access for a single DWORD writing. + * This function used for direct, address-based access to ARC memory. + * + * \param pDev the device pointer + * \param DEC_mode ARC memory space to used + * \param address The address to write + * \param data The data to write + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +_IFX_MEI_DBGLongWordWrite (DSL_DEV_Device_t * pDev, u32 DEC_mode, + u32 address, u32 data) +{ + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DBG_DECODE, DEC_mode); + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DBG_WR_AD, address); + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_DBG_DATA, data); + meiPollForDbgDone (pDev); + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * ARC Debug Memory Access for writing. + * This function used for direct, address-based access to ARC memory. + * + * \param pDev the device pointer + * \param destaddr The address to read + * \param databuffer Pointer to data + * \param databuffsize The number of DWORDs to read + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ + +static DSL_DEV_MeiError_t +IFX_MEI_DebugWrite (DSL_DEV_Device_t * pDev, u32 destaddr, + u32 * databuff, u32 databuffsize) +{ + u32 i; + u32 temp = 0x0; + u32 address = 0x0; + u32 *buffer = 0x0; + + // Open the debug port before DMP memory write + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); + + // For the requested length, write the address and write the data + address = destaddr; + buffer = databuff; + for (i = 0; i < databuffsize; i++) { + temp = *buffer; + _IFX_MEI_DBGLongWordWrite (pDev, ME_DBG_DECODE_DMP1_MASK, address, temp); + address += 4; + buffer++; + } + + // Close the debug port after DMP memory write + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * ARC Debug Memory Access for reading. + * This function used for direct, address-based access to ARC memory. + * + * \param pDev the device pointer + * \param srcaddr The address to read + * \param databuffer Pointer to data + * \param databuffsize The number of DWORDs to read + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_DebugRead (DSL_DEV_Device_t * pDev, u32 srcaddr, u32 * databuff, u32 databuffsize) +{ + u32 i; + u32 temp = 0x0; + u32 address = 0x0; + u32 *buffer = 0x0; + + // Open the debug port before DMP memory read + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); + + // For the requested length, write the address and read the data + address = srcaddr; + buffer = databuff; + for (i = 0; i < databuffsize; i++) { + _IFX_MEI_DBGLongWordRead (pDev, ME_DBG_DECODE_DMP1_MASK, address, &temp); + *buffer = temp; + address += 4; + buffer++; + } + + // Close the debug port after DMP memory read + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * Send a message to ARC MailBox. + * This function sends a message to ARC Mailbox via ARC DMA interface. + * + * \param pDev the device pointer + * \param msgsrcbuffer Pointer to message. + * \param msgsize The number of words to write. + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_MailboxWrite (DSL_DEV_Device_t * pDev, u16 * msgsrcbuffer, + u16 msgsize) +{ + int i; + u32 arc_mailbox_status = 0x0; + u32 temp = 0; + DSL_DEV_MeiError_t meiMailboxError = DSL_DEV_MEI_ERR_SUCCESS; + + // Write to mailbox + meiMailboxError = + IFX_MEI_DMAWrite (pDev, MEI_TO_ARC_MAILBOX, (u32 *) msgsrcbuffer, msgsize / 2); + meiMailboxError = + IFX_MEI_DMAWrite (pDev, MEI_TO_ARC_MAILBOXR, (u32 *) (&temp), 1); + + // Notify arc that mailbox write completed + DSL_DEV_PRIVATE(pDev)->cmv_waiting = 1; + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ME2ARC_INT, MEI_TO_ARC_MSGAV); + + i = 0; + while (i < WHILE_DELAY) { // wait for ARC to clear the bit + IFX_MEI_LongWordReadOffset (pDev, (u32) ME_ME2ARC_INT, &arc_mailbox_status); + if ((arc_mailbox_status & MEI_TO_ARC_MSGAV) != MEI_TO_ARC_MSGAV) + break; + i++; + if (i == WHILE_DELAY) { + IFX_MEI_EMSG (">>> Timeout waiting for ARC to clear MEI_TO_ARC_MSGAV!!!" + " MEI_TO_ARC message size = %d DWORDs <<<\n", msgsize/2); + meiMailboxError = DSL_DEV_MEI_ERR_FAILURE; + } + } + + return meiMailboxError; +} + +/** + * Read a message from ARC MailBox. + * This function reads a message from ARC Mailbox via ARC DMA interface. + * + * \param pDev the device pointer + * \param msgsrcbuffer Pointer to message. + * \param msgsize The number of words to read + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_MailboxRead (DSL_DEV_Device_t * pDev, u16 * msgdestbuffer, + u16 msgsize) +{ + DSL_DEV_MeiError_t meiMailboxError = DSL_DEV_MEI_ERR_SUCCESS; + // Read from mailbox + meiMailboxError = + IFX_MEI_DMARead (pDev, ARC_TO_MEI_MAILBOX, (u32 *) msgdestbuffer, msgsize / 2); + + // Notify arc that mailbox read completed + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ARC2ME_STAT, ARC_TO_MEI_MSGAV); + + return meiMailboxError; +} + +/** + * Download boot pages to ARC. + * This function downloads boot pages to ARC. + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_DownloadBootPages (DSL_DEV_Device_t * pDev) +{ + int boot_loop; + int page_size; + u32 dest_addr; + + /* + ** DMA the boot code page(s) + */ + + for (boot_loop = 1; + boot_loop < + (DSL_DEV_PRIVATE(pDev)->img_hdr-> count); boot_loop++) { + if ((DSL_DEV_PRIVATE(pDev)-> img_hdr->page[boot_loop].p_size) & BOOT_FLAG) { + page_size = IFX_MEI_GetPage (pDev, boot_loop, + GET_PROG, MAXSWAPSIZE, + mei_arc_swap_buff, + &dest_addr); + if (page_size > 0) { + IFX_MEI_DMAWrite (pDev, dest_addr, + mei_arc_swap_buff, + page_size); + } + } + if ((DSL_DEV_PRIVATE(pDev)-> img_hdr->page[boot_loop].d_size) & BOOT_FLAG) { + page_size = IFX_MEI_GetPage (pDev, boot_loop, + GET_DATA, MAXSWAPSIZE, + mei_arc_swap_buff, + &dest_addr); + if (page_size > 0) { + IFX_MEI_DMAWrite (pDev, dest_addr, + mei_arc_swap_buff, + page_size); + } + } + } + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * Initial efuse rar. + **/ +static void +IFX_MEI_FuseInit (DSL_DEV_Device_t * pDev) +{ + u32 data = 0; + IFX_MEI_DMAWrite (pDev, IRAM0_BASE, &data, 1); + IFX_MEI_DMAWrite (pDev, IRAM0_BASE + 4, &data, 1); + IFX_MEI_DMAWrite (pDev, IRAM1_BASE, &data, 1); + IFX_MEI_DMAWrite (pDev, IRAM1_BASE + 4, &data, 1); + IFX_MEI_DMAWrite (pDev, BRAM_BASE, &data, 1); + IFX_MEI_DMAWrite (pDev, BRAM_BASE + 4, &data, 1); + IFX_MEI_DMAWrite (pDev, ADSL_DILV_BASE, &data, 1); + IFX_MEI_DMAWrite (pDev, ADSL_DILV_BASE + 4, &data, 1); +} + +/** + * efuse rar program + **/ +static void +IFX_MEI_FuseProg (DSL_DEV_Device_t * pDev) +{ + u32 reg_data, fuse_value; + int i = 0; + + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, ®_data); + while ((reg_data & 0x10000000) == 0) { + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, ®_data); + i++; + /* 0x4000 translate to about 16 ms@111M, so should be enough */ + if (i == 0x4000) + return; + } + // STEP a: Prepare memory for external accesses + // Write fuse_en bit24 + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, ®_data); + IFX_MEI_LongWordWrite ((u32) LTQ_RCU_RST, reg_data | (1 << 24)); + + IFX_MEI_FuseInit (pDev); + for (i = 0; i < 4; i++) { + IFX_MEI_LongWordRead ((u32) (LTQ_FUSE_BASE) + i * 4, &fuse_value); + switch (fuse_value & 0xF0000) { + case 0x80000: + reg_data = ((fuse_value & RX_DILV_ADDR_BIT_MASK) | + (RX_DILV_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, ADSL_DILV_BASE, ®_data, 1); + break; + case 0x90000: + reg_data = ((fuse_value & RX_DILV_ADDR_BIT_MASK) | + (RX_DILV_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, ADSL_DILV_BASE + 4, ®_data, 1); + break; + case 0xA0000: + reg_data = ((fuse_value & IRAM0_ADDR_BIT_MASK) | + (IRAM0_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, IRAM0_BASE, ®_data, 1); + break; + case 0xB0000: + reg_data = ((fuse_value & IRAM0_ADDR_BIT_MASK) | + (IRAM0_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, IRAM0_BASE + 4, ®_data, 1); + break; + case 0xC0000: + reg_data = ((fuse_value & IRAM1_ADDR_BIT_MASK) | + (IRAM1_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, IRAM1_BASE, ®_data, 1); + break; + case 0xD0000: + reg_data = ((fuse_value & IRAM1_ADDR_BIT_MASK) | + (IRAM1_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, IRAM1_BASE + 4, ®_data, 1); + break; + case 0xE0000: + reg_data = ((fuse_value & BRAM_ADDR_BIT_MASK) | + (BRAM_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, BRAM_BASE, ®_data, 1); + break; + case 0xF0000: + reg_data = ((fuse_value & BRAM_ADDR_BIT_MASK) | + (BRAM_ADDR_BIT_MASK + 0x1)); + IFX_MEI_DMAWrite (pDev, BRAM_BASE + 4, ®_data, 1); + break; + default: // PPE efuse + break; + } + } + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, ®_data); + IFX_MEI_LongWordWrite ((u32) LTQ_RCU_RST, reg_data & ~(1 << 24)); + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, ®_data); +} + +/** + * Enable DFE Clock + * This function enables DFE Clock + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_EnableCLK (DSL_DEV_Device_t * pDev) +{ + u32 arc_debug_data = 0; + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); + //enable ac_clk signal + _IFX_MEI_DBGLongWordRead (pDev, ME_DBG_DECODE_DMP1_MASK, + CRI_CCR0, &arc_debug_data); + arc_debug_data |= ACL_CLK_MODE_ENABLE; + _IFX_MEI_DBGLongWordWrite (pDev, ME_DBG_DECODE_DMP1_MASK, + CRI_CCR0, arc_debug_data); + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * Halt the ARC. + * This function halts the ARC. + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_HaltArc (DSL_DEV_Device_t * pDev) +{ + u32 arc_debug_data = 0x0; + + // Switch arc control from JTAG mode to MEI mode + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); + _IFX_MEI_DBGLongWordRead (pDev, MEI_DEBUG_DEC_AUX_MASK, + ARC_DEBUG, &arc_debug_data); + arc_debug_data |= ARC_DEBUG_HALT; + _IFX_MEI_DBGLongWordWrite (pDev, MEI_DEBUG_DEC_AUX_MASK, + ARC_DEBUG, arc_debug_data); + // Switch arc control from MEI mode to JTAG mode + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + MEI_WAIT (10); + + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * Run the ARC. + * This function runs the ARC. + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_RunArc (DSL_DEV_Device_t * pDev) +{ + u32 arc_debug_data = 0x0; + + // Switch arc control from JTAG mode to MEI mode- write '1' to bit0 + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); + _IFX_MEI_DBGLongWordRead (pDev, MEI_DEBUG_DEC_AUX_MASK, + AUX_STATUS, &arc_debug_data); + + // Write debug data reg with content ANDd with 0xFDFFFFFF (halt bit cleared) + arc_debug_data &= ~ARC_AUX_HALT; + _IFX_MEI_DBGLongWordWrite (pDev, MEI_DEBUG_DEC_AUX_MASK, + AUX_STATUS, arc_debug_data); + + // Switch arc control from MEI mode to JTAG mode- write '0' to bit0 + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + // Enable mask for arc codeswap interrupts + IFX_MEI_IRQEnable (pDev); + + return DSL_DEV_MEI_ERR_SUCCESS; + +} + +/** + * Reset the ARC. + * This function resets the ARC. + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_ResetARC (DSL_DEV_Device_t * pDev) +{ + u32 arc_debug_data = 0; + + IFX_MEI_HaltArc (pDev); + + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, &arc_debug_data); + IFX_MEI_LongWordWrite ((u32) LTQ_RCU_RST, + arc_debug_data | LTQ_RCU_RST_REQ_DFE | LTQ_RCU_RST_REQ_AFE); + + // reset ARC + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_RST_CTRL, MEI_SOFT_RESET); + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_RST_CTRL, 0); + + IFX_MEI_IRQDisable (pDev); + + IFX_MEI_EnableCLK (pDev); + +#if 0 + // reset part of PPE + *(unsigned long *) (BSP_PPE32_SRST) = 0xC30; + *(unsigned long *) (BSP_PPE32_SRST) = 0xFFF; +#endif + + DSL_DEV_PRIVATE(pDev)->modem_ready = 0; + + return DSL_DEV_MEI_ERR_SUCCESS; +} + +DSL_DEV_MeiError_t +DSL_BSP_Showtime (DSL_DEV_Device_t * dev, DSL_uint32_t rate_fast, DSL_uint32_t rate_intl) +{ + struct port_cell_info port_cell = {0}; + + IFX_MEI_EMSG ("Datarate US intl = %d, fast = %d\n", (int)rate_intl, + (int)rate_fast); + + if ( rate_fast ) + g_tx_link_rate[0] = rate_fast / (53 * 8); + if ( rate_intl ) + g_tx_link_rate[1] = rate_intl / (53 * 8); + + if ( g_tx_link_rate[0] == 0 && g_tx_link_rate[1] == 0 ) { + IFX_MEI_EMSG ("Got rate fail.\n"); + } + + if ( ifx_mei_atm_showtime_enter ) + { + port_cell.port_num = 2; + port_cell.tx_link_rate[0] = g_tx_link_rate[0]; + port_cell.tx_link_rate[1] = g_tx_link_rate[1]; + ifx_mei_atm_showtime_enter(&port_cell, g_xdata_addr); + } + else + { + IFX_MEI_EMSG("no hookup from ATM driver to set cell rate\n"); + } + + return DSL_DEV_MEI_ERR_SUCCESS; +}; + +/** + * Reset/halt/run the DFE. + * This function provide operations to reset/halt/run the DFE. + * + * \param pDev the device pointer + * \param mode which operation want to do + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_CpuModeSet (DSL_DEV_Device_t *pDev, + DSL_DEV_CpuMode_t mode) +{ + DSL_DEV_MeiError_t err_ret = DSL_DEV_MEI_ERR_FAILURE; + switch (mode) { + case DSL_CPU_HALT: + err_ret = IFX_MEI_HaltArc (pDev); + break; + case DSL_CPU_RUN: + err_ret = IFX_MEI_RunArc (pDev); + break; + case DSL_CPU_RESET: + err_ret = IFX_MEI_ResetARC (pDev); + break; + default: + break; + } + return err_ret; +} + +/** + * Accress DFE memory. + * This function provide a way to access DFE memory; + * + * \param pDev the device pointer + * \param type read or write + * \param destaddr destination address + * \param databuff pointer to hold data + * \param databuffsize size want to read/write + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +DSL_DEV_MeiError_t +DSL_BSP_MemoryDebugAccess (DSL_DEV_Device_t * pDev, + DSL_BSP_MemoryAccessType_t type, + DSL_uint32_t destaddr, DSL_uint32_t *databuff, + DSL_uint32_t databuffsize) +{ + DSL_DEV_MeiError_t meierr = DSL_DEV_MEI_ERR_SUCCESS; + switch (type) { + case DSL_BSP_MEMORY_READ: + meierr = IFX_MEI_DebugRead (pDev, (u32)destaddr, (u32*)databuff, (u32)databuffsize); + break; + case DSL_BSP_MEMORY_WRITE: + meierr = IFX_MEI_DebugWrite (pDev, (u32)destaddr, (u32*)databuff, (u32)databuffsize); + break; + } + return DSL_DEV_MEI_ERR_SUCCESS; +}; + +/** + * Download boot code to ARC. + * This function downloads boot code to ARC. + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_DownloadBootCode (DSL_DEV_Device_t *pDev) +{ + IFX_MEI_IRQDisable (pDev); + + IFX_MEI_EnableCLK (pDev); + + IFX_MEI_FuseProg (pDev); //program fuse rar + + IFX_MEI_DownloadBootPages (pDev); + + return DSL_DEV_MEI_ERR_SUCCESS; +}; + +/** + * Enable Jtag debugger interface + * This function setups mips gpio to enable jtag debugger + * + * \param pDev the device pointer + * \param enable enable or disable + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_ArcJtagEnable (DSL_DEV_Device_t *dev, int enable) +{ + /* + int meierr=0; + u32 reg_data; + switch (enable) { + case 1: + //reserve gpio 9, 10, 11, 14, 19 for ARC JTAG + ifxmips_port_reserve_pin (0, 9); + ifxmips_port_reserve_pin (0, 10); + ifxmips_port_reserve_pin (0, 11); + ifxmips_port_reserve_pin (0, 14); + ifxmips_port_reserve_pin (1, 3); + + ifxmips_port_set_dir_in(0, 11); + ifxmips_port_clear_altsel0(0, 11); + ifxmips_port_clear_altsel1(0, 11); + ifxmips_port_set_open_drain(0, 11); + //enable ARC JTAG + IFX_MEI_LongWordRead ((u32) LTQ_RCU_RST, ®_data); + IFX_MEI_LongWordWrite ((u32) LTQ_RCU_RST, reg_data | LTQ_RCU_RST_REQ_ARC_JTAG); + break; + case 0: + default: + break; + } +jtag_end: + if (meierr) + return DSL_DEV_MEI_ERR_FAILURE; +*/ + + return DSL_DEV_MEI_ERR_SUCCESS; +}; + +/** + * Enable DFE to MIPS interrupt + * This function enable DFE to MIPS interrupt + * + * \param pDev the device pointer + * \param enable enable or disable + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_AdslMailboxIRQEnable (DSL_DEV_Device_t *pDev, int enable) +{ + DSL_DEV_MeiError_t meierr; + switch (enable) { + case 0: + meierr = DSL_DEV_MEI_ERR_SUCCESS; + IFX_MEI_IRQDisable (pDev); + break; + case 1: + IFX_MEI_IRQEnable (pDev); + meierr = DSL_DEV_MEI_ERR_SUCCESS; + break; + default: + meierr = DSL_DEV_MEI_ERR_FAILURE; + break; + + } + return meierr; +} + +/** + * Get the modem status + * This function return the modem status + * + * \param pDev the device pointer + * \return 1: modem ready 0: not ready + * \ingroup Internal + */ +static int +IFX_MEI_IsModemReady (DSL_DEV_Device_t * pDev) +{ + return DSL_DEV_PRIVATE(pDev)->modem_ready; +} + +DSL_DEV_MeiError_t +DSL_BSP_AdslLedInit (DSL_DEV_Device_t * dev, + DSL_DEV_LedId_t led_number, + DSL_DEV_LedType_t type, + DSL_DEV_LedHandler_t handler) +{ +#if 0 + struct led_config_param param; + if (led_number == DSL_LED_LINK_ID && type == DSL_LED_LINK_TYPE && handler == /*DSL_LED_HD_CPU*/DSL_LED_HD_FW) { + param.operation_mask = CONFIG_OPERATION_UPDATE_SOURCE; + param.led = 0x01; + param.source = 0x01; +// bsp_led_config (¶m); + + } else if (led_number == DSL_LED_DATA_ID && type == DSL_LED_DATA_TYPE && (handler == DSL_LED_HD_FW)) { + param.operation_mask = CONFIG_OPERATION_UPDATE_SOURCE; + param.led = 0x02; + param.source = 0x02; +// bsp_led_config (¶m); + } +#endif + return DSL_DEV_MEI_ERR_SUCCESS; +}; +#if 0 +DSL_DEV_MeiError_t +DSL_BSP_AdslLedSet (DSL_DEV_Device_t * dev, DSL_DEV_LedId_t led_number, DSL_DEV_LedMode_t mode) +{ + printk(KERN_INFO "[%s %d]: mode = %#x, led_number = %d\n", __func__, __LINE__, mode, led_number); + switch (mode) { + case DSL_LED_OFF: + switch (led_number) { + case DSL_LED_LINK_ID: +#ifdef CONFIG_BSP_LED + bsp_led_set_blink (1, 0); + bsp_led_set_data (1, 0); +#endif + break; + case DSL_LED_DATA_ID: +#ifdef CONFIG_BSP_LED + bsp_led_set_blink (0, 0); + bsp_led_set_data (0, 0); +#endif + break; + } + break; + case DSL_LED_FLASH: + switch (led_number) { + case DSL_LED_LINK_ID: +#ifdef CONFIG_BSP_LED + bsp_led_set_blink (1, 1); // data +#endif + break; + case DSL_LED_DATA_ID: +#ifdef CONFIG_BSP_LED + bsp_led_set_blink (0, 1); // data +#endif + break; + } + break; + case DSL_LED_ON: + switch (led_number) { + case DSL_LED_LINK_ID: +#ifdef CONFIG_BSP_LED + bsp_led_set_blink (1, 0); + bsp_led_set_data (1, 1); +#endif + break; + case DSL_LED_DATA_ID: +#ifdef CONFIG_BSP_LED + bsp_led_set_blink (0, 0); + bsp_led_set_data (0, 1); +#endif + break; + } + break; + } + return DSL_DEV_MEI_ERR_SUCCESS; +}; + +#endif + +/** +* Compose a message. +* This function compose a message from opcode, group, address, index, size, and data +* +* \param opcode The message opcode +* \param group The message group number +* \param address The message address. +* \param index The message index. +* \param size The number of words to read/write. +* \param data The pointer to data. +* \param CMVMSG The pointer to message buffer. +* \ingroup Internal +*/ +void +makeCMV (u8 opcode, u8 group, u16 address, u16 index, int size, u16 * data, u16 *CMVMSG) +{ + memset (CMVMSG, 0, MSG_LENGTH * 2); + CMVMSG[0] = (opcode << 4) + (size & 0xf); + CMVMSG[1] = (((index == 0) ? 0 : 1) << 7) + (group & 0x7f); + CMVMSG[2] = address; + CMVMSG[3] = index; + if (opcode == H2D_CMV_WRITE) + memcpy (CMVMSG + 4, data, size * 2); + return; +} + +/** + * Send a message to ARC and read the response + * This function sends a message to arc, waits the response, and reads the responses. + * + * \param pDev the device pointer + * \param request Pointer to the request + * \param reply Wait reply or not. + * \param response Pointer to the response + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +DSL_DEV_MeiError_t +DSL_BSP_SendCMV (DSL_DEV_Device_t * pDev, u16 * request, int reply, u16 * response) // write cmv to arc, if reply needed, wait for reply +{ + DSL_DEV_MeiError_t meierror; +#if defined(BSP_PORT_RTEMS) + int delay_counter = 0; +#endif + + if (MEI_MUTEX_LOCK (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema)) + return -ERESTARTSYS; + + DSL_DEV_PRIVATE(pDev)->cmv_reply = reply; + memset (DSL_DEV_PRIVATE(pDev)->CMV_RxMsg, 0, + sizeof (DSL_DEV_PRIVATE(pDev)-> + CMV_RxMsg)); + DSL_DEV_PRIVATE(pDev)->arcmsgav = 0; + + meierror = IFX_MEI_MailboxWrite (pDev, request, MSG_LENGTH); + + if (meierror != DSL_DEV_MEI_ERR_SUCCESS) { + DSL_DEV_PRIVATE(pDev)->cmv_waiting = 0; + DSL_DEV_PRIVATE(pDev)->arcmsgav = 0; + IFX_MEI_EMSG ("MailboxWrite Fail!\n"); + IFX_MEI_EMSG ("Resetting ARC...\n"); + IFX_MEI_ResetARC(pDev); + MEI_MUTEX_UNLOCK (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema); + return meierror; + } + else { + DSL_DEV_PRIVATE(pDev)->cmv_count++; + } + + if (DSL_DEV_PRIVATE(pDev)->cmv_reply == + NO_REPLY) { + MEI_MUTEX_UNLOCK (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema); + return DSL_DEV_MEI_ERR_SUCCESS; + } + +#if !defined(BSP_PORT_RTEMS) + if (DSL_DEV_PRIVATE(pDev)->arcmsgav == 0) + MEI_WAIT_EVENT_TIMEOUT (DSL_DEV_PRIVATE(pDev)->wait_queue_arcmsgav, CMV_TIMEOUT); +#else + while (DSL_DEV_PRIVATE(pDev)->arcmsgav == 0 && delay_counter < CMV_TIMEOUT / 5) { + MEI_WAIT (5); + delay_counter++; + } +#endif + + DSL_DEV_PRIVATE(pDev)->cmv_waiting = 0; + if (DSL_DEV_PRIVATE(pDev)->arcmsgav == 0) { //CMV_timeout + DSL_DEV_PRIVATE(pDev)->arcmsgav = 0; + IFX_MEI_EMSG ("\%s: DSL_DEV_MEI_ERR_MAILBOX_TIMEOUT\n", + __FUNCTION__); + MEI_MUTEX_UNLOCK (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema); + return DSL_DEV_MEI_ERR_MAILBOX_TIMEOUT; + } + else { + DSL_DEV_PRIVATE(pDev)->arcmsgav = 0; + DSL_DEV_PRIVATE(pDev)-> + reply_count++; + memcpy (response, DSL_DEV_PRIVATE(pDev)->CMV_RxMsg, MSG_LENGTH * 2); + MEI_MUTEX_UNLOCK (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema); + return DSL_DEV_MEI_ERR_SUCCESS; + } + MEI_MUTEX_UNLOCK (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema); + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/** + * Reset the ARC, download boot codes, and run the ARC. + * This function resets the ARC, downloads boot codes to ARC, and runs the ARC. + * + * \param pDev the device pointer + * \return DSL_DEV_MEI_ERR_SUCCESS or DSL_DEV_MEI_ERR_FAILURE + * \ingroup Internal + */ +static DSL_DEV_MeiError_t +IFX_MEI_RunAdslModem (DSL_DEV_Device_t *pDev) +{ + int nSize = 0, idx = 0; + uint32_t im0_register, im2_register; +// DSL_DEV_WinHost_Message_t m; + + if (mei_arc_swap_buff == NULL) { + mei_arc_swap_buff = + (u32 *) kmalloc (MAXSWAPSIZE * 4, GFP_KERNEL); + if (mei_arc_swap_buff == NULL) { + IFX_MEI_EMSG (">>> malloc fail for codeswap buff!!! <<<\n"); + return DSL_DEV_MEI_ERR_FAILURE; + } + IFX_MEI_DMSG("allocate %dKB swap buff memory at: 0x%p\n", ksize(mei_arc_swap_buff)/1024, mei_arc_swap_buff); + } + + DSL_DEV_PRIVATE(pDev)->img_hdr = + (ARC_IMG_HDR *) DSL_DEV_PRIVATE(pDev)->adsl_mem_info[0].address; + if ((DSL_DEV_PRIVATE(pDev)->img_hdr-> + count) * sizeof (ARC_SWP_PAGE_HDR) > SDRAM_SEGMENT_SIZE) { + IFX_MEI_EMSG ("firmware header size is bigger than 64K segment size\n"); + return DSL_DEV_MEI_ERR_FAILURE; + } + // check image size + for (idx = 0; idx < MAX_BAR_REGISTERS; idx++) { + nSize += DSL_DEV_PRIVATE(pDev)->adsl_mem_info[idx].nCopy; + } + if (nSize != + DSL_DEV_PRIVATE(pDev)->image_size) { + IFX_MEI_EMSG ("Firmware download is not completed. Please download firmware again!\n"); + return DSL_DEV_MEI_ERR_FAILURE; + } + // TODO: check crc + /// + + IFX_MEI_ResetARC (pDev); + IFX_MEI_HaltArc (pDev); + IFX_MEI_BarUpdate (pDev, DSL_DEV_PRIVATE(pDev)->nBar); + + //IFX_MEI_DMSG("Starting to meiDownloadBootCode\n"); + + IFX_MEI_DownloadBootCode (pDev); + + im0_register = (*LTQ_ICU_IM0_IER) & (1 << 20); + im2_register = (*LTQ_ICU_IM2_IER) & (1 << 20); + /* Turn off irq */ + #ifdef CONFIG_SOC_AMAZON_SE +#define IFXMIPS_USB_OC_INT0 (INT_NUM_IM4_IRL0 + 23) + disable_irq (IFXMIPS_USB_OC_INT0); +// disable_irq (IFXMIPS_USB_OC_INT2); + #elif defined(CONFIG_SOC_AR9) +#define IFXMIPS_USB_OC_INT0 (INT_NUM_IM4_IRL1 + 28) + disable_irq (IFXMIPS_USB_OC_INT0); +// disable_irq (IFXMIPS_USB_OC_INT2); + #elif defined(CONFIG_SOC_XWAY) + disable_irq (LTQ_USB_OC_INT); + #else + #error unkonwn arch + #endif + disable_irq (pDev->nIrq[IFX_DYING_GASP]); + + IFX_MEI_RunArc (pDev); + + MEI_WAIT_EVENT_TIMEOUT (DSL_DEV_PRIVATE(pDev)->wait_queue_modemready, 1000); + + #ifdef CONFIG_SOC_AMAZON_SE + MEI_MASK_AND_ACK_IRQ (IFXMIPS_USB_OC_INT0); +// MEI_MASK_AND_ACK_IRQ (IFXMIPS_USB_OC_INT2); + #elif defined(CONFIG_SOC_AR9) + MEI_MASK_AND_ACK_IRQ (IFXMIPS_USB_OC_INT0); +// MEI_MASK_AND_ACK_IRQ (IFXMIPS_USB_OC_INT2); + #elif defined(CONFIG_SOC_XWAY) + MEI_MASK_AND_ACK_IRQ (LTQ_USB_OC_INT); + #else + #error unkonwn arch + #endif + MEI_MASK_AND_ACK_IRQ (pDev->nIrq[IFX_DYING_GASP]); + + /* Re-enable irq */ + enable_irq(pDev->nIrq[IFX_DYING_GASP]); + *LTQ_ICU_IM0_IER |= im0_register; + *LTQ_ICU_IM2_IER |= im2_register; + + if (DSL_DEV_PRIVATE(pDev)->modem_ready != 1) { + IFX_MEI_EMSG ("Modem failed to be ready!\n"); + return DSL_DEV_MEI_ERR_FAILURE; + } else { + IFX_MEI_DMSG("Modem is ready.\n"); + return DSL_DEV_MEI_ERR_SUCCESS; + } +} + +/** + * Get the page's data pointer + * This function caculats the data address from the firmware header. + * + * \param pDev the device pointer + * \param Page The page number. + * \param data Data page or program page. + * \param MaxSize The maximum size to read. + * \param Buffer Pointer to data. + * \param Dest Pointer to the destination address. + * \return The number of bytes to read. + * \ingroup Internal + */ +static int +IFX_MEI_GetPage (DSL_DEV_Device_t * pDev, u32 Page, u32 data, + u32 MaxSize, u32 * Buffer, u32 * Dest) +{ + u32 size; + u32 i; + u32 *p; + u32 idx, offset, nBar = 0; + + if (Page > DSL_DEV_PRIVATE(pDev)->img_hdr->count) + return -2; + /* + ** Get program or data size, depending on "data" flag + */ + size = (data == GET_DATA) ? (DSL_DEV_PRIVATE(pDev)->img_hdr->page[Page].d_size) : + (DSL_DEV_PRIVATE(pDev)->img_hdr->page[Page].p_size); + size &= BOOT_FLAG_MASK; // Clear boot bit! + if (size > MaxSize) + return -1; + + if (size == 0) + return 0; + /* + ** Get program or data offset, depending on "data" flag + */ + i = data ? (DSL_DEV_PRIVATE(pDev)->img_hdr->page[Page].d_offset) : + (DSL_DEV_PRIVATE(pDev)->img_hdr->page[Page].p_offset); + + /* + ** Copy data/program to buffer + */ + + idx = i / SDRAM_SEGMENT_SIZE; + offset = i % SDRAM_SEGMENT_SIZE; + p = (u32 *) ((u8 *) DSL_DEV_PRIVATE(pDev)->adsl_mem_info[idx].address + offset); + + for (i = 0; i < size; i++) { + if (offset + i * 4 - (nBar * SDRAM_SEGMENT_SIZE) >= SDRAM_SEGMENT_SIZE) { + idx++; + nBar++; + p = (u32 *) ((u8 *) KSEG1ADDR ((u32)DSL_DEV_PRIVATE(pDev)->adsl_mem_info[idx].address)); + } + Buffer[i] = *p++; + } + + /* + ** Pass back data/program destination address + */ + *Dest = data ? (DSL_DEV_PRIVATE(pDev)-> img_hdr->page[Page].d_dest) : + (DSL_DEV_PRIVATE(pDev)->img_hdr->page[Page].p_dest); + + return size; +} + +/** + * Free the memory for ARC firmware + * + * \param pDev the device pointer + * \param type Free all memory or free the unused memory after showtime + * \ingroup Internal + */ +const char *free_str[4] = {"Invalid", "Free_Reload", "Free_Showtime", "Free_All"}; +static int +IFX_MEI_DFEMemoryFree (DSL_DEV_Device_t * pDev, int type) +{ + int idx = 0; + smmu_mem_info_t *adsl_mem_info = + DSL_DEV_PRIVATE(pDev)->adsl_mem_info; + + for (idx = 0; idx < MAX_BAR_REGISTERS; idx++) { + if (type == FREE_ALL ||adsl_mem_info[idx].type == type) { + if (adsl_mem_info[idx].size > 0) { + IFX_MEI_DMSG ("Freeing memory %p (%s)\n", adsl_mem_info[idx].org_address, free_str[adsl_mem_info[idx].type]); + if ( idx == XDATA_REGISTER ) { + g_xdata_addr = NULL; + if ( ifx_mei_atm_showtime_exit ) + ifx_mei_atm_showtime_exit(); + } + kfree (adsl_mem_info[idx].org_address); + adsl_mem_info[idx].org_address = 0; + adsl_mem_info[idx].address = 0; + adsl_mem_info[idx].size = 0; + adsl_mem_info[idx].type = 0; + adsl_mem_info[idx].nCopy = 0; + } + } + } + + if(mei_arc_swap_buff != NULL){ + IFX_MEI_DMSG("free %dKB swap buff memory at: 0x%p\n", ksize(mei_arc_swap_buff)/1024, mei_arc_swap_buff); + kfree(mei_arc_swap_buff); + mei_arc_swap_buff=NULL; + } + + return 0; +} +static int +IFX_MEI_DFEMemoryAlloc (DSL_DEV_Device_t * pDev, long size) +{ + unsigned long mem_ptr; + char *org_mem_ptr = NULL; + int idx = 0; + long total_size = 0; + int err = 0; + smmu_mem_info_t *adsl_mem_info = + ((ifx_mei_device_private_t *) pDev->pPriv)->adsl_mem_info; +// DSL_DEV_PRIVATE(pDev)->adsl_mem_info; + int allocate_size = SDRAM_SEGMENT_SIZE; + + IFX_MEI_DMSG("image_size = %ld\n", size); + // Alloc Swap Pages + for (idx = 0; size > 0 && idx < MAX_BAR_REGISTERS; idx++) { + // skip bar15 for XDATA usage. + if (idx == XDATA_REGISTER) + continue; +#if 0 + if (size < SDRAM_SEGMENT_SIZE) { + allocate_size = size; + if (allocate_size < 1024) + allocate_size = 1024; + } +#endif + if (idx == (MAX_BAR_REGISTERS - 1)) + allocate_size = size; + else + allocate_size = SDRAM_SEGMENT_SIZE; + + org_mem_ptr = kmalloc (allocate_size, GFP_KERNEL); + if (org_mem_ptr == NULL) { + IFX_MEI_EMSG ("%d: kmalloc %d bytes memory fail!\n", idx, allocate_size); + err = -ENOMEM; + goto allocate_error; + } + + if (((unsigned long)org_mem_ptr) & (1023)) { + /* Pointer not 1k aligned, so free it and allocate a larger chunk + * for further alignment. + */ + kfree(org_mem_ptr); + org_mem_ptr = kmalloc (allocate_size + 1024, GFP_KERNEL); + if (org_mem_ptr == NULL) { + IFX_MEI_EMSG ("%d: kmalloc %d bytes memory fail!\n", + idx, allocate_size + 1024); + err = -ENOMEM; + goto allocate_error; + } + mem_ptr = (unsigned long) (org_mem_ptr + 1023) & ~(1024 -1); + } else { + mem_ptr = (unsigned long) org_mem_ptr; + } + + adsl_mem_info[idx].address = (char *) mem_ptr; + adsl_mem_info[idx].org_address = org_mem_ptr; + adsl_mem_info[idx].size = allocate_size; + size -= allocate_size; + total_size += allocate_size; + } + if (size > 0) { + IFX_MEI_EMSG ("Image size is too large!\n"); + err = -EFBIG; + goto allocate_error; + } + err = idx; + return err; + + allocate_error: + IFX_MEI_DFEMemoryFree (pDev, FREE_ALL); + return err; +} + +/** + * Program the BAR registers + * + * \param pDev the device pointer + * \param nTotalBar The number of bar to program. + * \ingroup Internal + */ +static int +IFX_MEI_BarUpdate (DSL_DEV_Device_t * pDev, int nTotalBar) +{ + int idx = 0; + smmu_mem_info_t *adsl_mem_info = + DSL_DEV_PRIVATE(pDev)->adsl_mem_info; + + for (idx = 0; idx < nTotalBar; idx++) { + //skip XDATA register + if (idx == XDATA_REGISTER) + continue; + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_XMEM_BAR_BASE + idx * 4, + (((uint32_t) adsl_mem_info[idx].address) & 0x0FFFFFFF)); + } + for (idx = nTotalBar; idx < MAX_BAR_REGISTERS; idx++) { + if (idx == XDATA_REGISTER) + continue; + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_XMEM_BAR_BASE + idx * 4, + (((uint32_t)adsl_mem_info[nTotalBar - 1].address) & 0x0FFFFFFF)); + /* These are for /proc/danube_mei/meminfo purpose */ + adsl_mem_info[idx].address = adsl_mem_info[nTotalBar - 1].address; + adsl_mem_info[idx].org_address = adsl_mem_info[nTotalBar - 1].org_address; + adsl_mem_info[idx].size = 0; /* Prevent it from being freed */ + } + + g_xdata_addr = adsl_mem_info[XDATA_REGISTER].address; + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_XMEM_BAR_BASE + XDATA_REGISTER * 4, + (((uint32_t) adsl_mem_info [XDATA_REGISTER].address) & 0x0FFFFFFF)); + // update MEI_XDATA_BASE_SH + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_XDATA_BASE_SH, + ((unsigned long)adsl_mem_info[XDATA_REGISTER].address) & 0x0FFFFFFF); + + return DSL_DEV_MEI_ERR_SUCCESS; +} + +/* This copies the firmware from secondary storage to 64k memory segment in SDRAM */ +DSL_DEV_MeiError_t +DSL_BSP_FWDownload (DSL_DEV_Device_t * pDev, const char *buf, + unsigned long size, long *loff, long *current_offset) +{ + ARC_IMG_HDR img_hdr_tmp; + smmu_mem_info_t *adsl_mem_info = DSL_DEV_PRIVATE(pDev)->adsl_mem_info; + + size_t nRead = 0, nCopy = 0; + char *mem_ptr; + char *org_mem_ptr = NULL; + ssize_t retval = -ENOMEM; + int idx = 0; + + IFX_MEI_DMSG("\n"); + + if (*loff == 0) { + if (size < sizeof (img_hdr_tmp)) { + IFX_MEI_EMSG ("Firmware size is too small!\n"); + return retval; + } + copy_from_user ((char *) &img_hdr_tmp, buf, sizeof (img_hdr_tmp)); + // header of image_size and crc are not included. + DSL_DEV_PRIVATE(pDev)->image_size = le32_to_cpu (img_hdr_tmp.size) + 8; + + if (DSL_DEV_PRIVATE(pDev)->image_size > 1024 * 1024) { + IFX_MEI_EMSG ("Firmware size is too large!\n"); + return retval; + } + // check if arc is halt + IFX_MEI_ResetARC (pDev); + IFX_MEI_HaltArc (pDev); + + IFX_MEI_DFEMemoryFree (pDev, FREE_ALL); //free all + + retval = IFX_MEI_DFEMemoryAlloc (pDev, DSL_DEV_PRIVATE(pDev)->image_size); + if (retval < 0) { + IFX_MEI_EMSG ("Error: No memory space left.\n"); + goto error; + } + for (idx = 0; idx < retval; idx++) { + //skip XDATA register + if (idx == XDATA_REGISTER) + continue; + if (idx * SDRAM_SEGMENT_SIZE < le32_to_cpu (img_hdr_tmp.page[0].p_offset)) + adsl_mem_info[idx].type = FREE_RELOAD; + else + adsl_mem_info[idx].type = FREE_SHOWTIME; + } + DSL_DEV_PRIVATE(pDev)->nBar = retval; + + DSL_DEV_PRIVATE(pDev)->img_hdr = + (ARC_IMG_HDR *) adsl_mem_info[0].address; + + org_mem_ptr = kmalloc (SDRAM_SEGMENT_SIZE, GFP_KERNEL); + if (org_mem_ptr == NULL) { + IFX_MEI_EMSG ("kmalloc memory fail!\n"); + retval = -ENOMEM; + goto error; + } + + if (((unsigned long)org_mem_ptr) & (1023)) { + /* Pointer not 1k aligned, so free it and allocate a larger chunk + * for further alignment. + */ + kfree(org_mem_ptr); + org_mem_ptr = kmalloc (SDRAM_SEGMENT_SIZE + 1024, GFP_KERNEL); + if (org_mem_ptr == NULL) { + IFX_MEI_EMSG ("kmalloc memory fail!\n"); + retval = -ENOMEM; + goto error; + } + adsl_mem_info[XDATA_REGISTER].address = + (char *) ((unsigned long) (org_mem_ptr + 1023) & ~(1024 -1)); + } else { + adsl_mem_info[XDATA_REGISTER].address = org_mem_ptr; + } + + adsl_mem_info[XDATA_REGISTER].org_address = org_mem_ptr; + adsl_mem_info[XDATA_REGISTER].size = SDRAM_SEGMENT_SIZE; + + adsl_mem_info[XDATA_REGISTER].type = FREE_RELOAD; + IFX_MEI_DMSG("-> IFX_MEI_BarUpdate()\n"); + IFX_MEI_BarUpdate (pDev, (DSL_DEV_PRIVATE(pDev)->nBar)); + } + else if (DSL_DEV_PRIVATE(pDev)-> image_size == 0) { + IFX_MEI_EMSG ("Error: Firmware size=0! \n"); + goto error; + } + + nRead = 0; + while (nRead < size) { + long offset = ((long) (*loff) + nRead) % SDRAM_SEGMENT_SIZE; + idx = (((long) (*loff)) + nRead) / SDRAM_SEGMENT_SIZE; + mem_ptr = (char *) KSEG1ADDR ((unsigned long) (adsl_mem_info[idx].address) + offset); + if ((size - nRead + offset) > SDRAM_SEGMENT_SIZE) + nCopy = SDRAM_SEGMENT_SIZE - offset; + else + nCopy = size - nRead; + copy_from_user (mem_ptr, buf + nRead, nCopy); + for (offset = 0; offset < (nCopy / 4); offset++) { + ((unsigned long *) mem_ptr)[offset] = le32_to_cpu (((unsigned long *) mem_ptr)[offset]); + } + nRead += nCopy; + adsl_mem_info[idx].nCopy += nCopy; + } + + *loff += size; + *current_offset = size; + return DSL_DEV_MEI_ERR_SUCCESS; +error: + IFX_MEI_DFEMemoryFree (pDev, FREE_ALL); + return DSL_DEV_MEI_ERR_FAILURE; +} +/* + * Register a callback event. + * Return: + * -1 if the event already has a callback function registered. + * 0 success + */ +int DSL_BSP_EventCBRegister(DSL_BSP_EventCallBack_t *p) +{ + if (!p) { + IFX_MEI_EMSG("Invalid parameter!\n"); + return -EINVAL; + } + if (p->event > DSL_BSP_CB_LAST || p->event < DSL_BSP_CB_FIRST) { + IFX_MEI_EMSG("Invalid Event %d\n", p->event); + return -EINVAL; + } + if (dsl_bsp_event_callback[p->event].function) { + IFX_MEI_EMSG("Event %d already has a callback function registered!\n", p->event); + return -1; + } else { + dsl_bsp_event_callback[p->event].function = p->function; + dsl_bsp_event_callback[p->event].event = p->event; + dsl_bsp_event_callback[p->event].pData = p->pData; + } + return 0; +} +int DSL_BSP_EventCBUnregister(DSL_BSP_EventCallBack_t *p) +{ + if (!p) { + IFX_MEI_EMSG("Invalid parameter!\n"); + return -EINVAL; + } + if (p->event > DSL_BSP_CB_LAST || p->event < DSL_BSP_CB_FIRST) { + IFX_MEI_EMSG("Invalid Event %d\n", p->event); + return -EINVAL; + } + if (dsl_bsp_event_callback[p->event].function) { + IFX_MEI_EMSG("Unregistering Event %d...\n", p->event); + dsl_bsp_event_callback[p->event].function = NULL; + dsl_bsp_event_callback[p->event].pData = NULL; + } else { + IFX_MEI_EMSG("Event %d is not registered!\n", p->event); + return -1; + } + return 0; +} + +/** + * MEI Dying Gasp interrupt handler + * + * \param int1 + * \param void0 + * \param regs Pointer to the structure of danube mips registers + * \ingroup Internal + */ +/*static irqreturn_t IFX_MEI_Dying_Gasp_IrqHandle (int int1, void *void0) +{ + DSL_DEV_Device_t *pDev = (DSL_DEV_Device_t *) void0; + DSL_BSP_CB_Type_t event; + + if (pDev == NULL) + IFX_MEI_EMSG("Error: Got Interrupt but pDev is NULL!!!!\n"); + +#ifndef CONFIG_SMP + disable_irq (pDev->nIrq[IFX_DYING_GASP]); +#else + disable_irq_nosync(pDev->nIrq[IFX_DYING_GASP]); +#endif + event = DSL_BSP_CB_DYING_GASP; + + if (dsl_bsp_event_callback[event].function) + (*dsl_bsp_event_callback[event].function)(pDev, event, dsl_bsp_event_callback[event].pData); + +#ifdef CONFIG_USE_EMULATOR + IFX_MEI_EMSG("Dying Gasp! Shutting Down... (Work around for Amazon-S Venus emulator)\n"); +#else + IFX_MEI_EMSG("Dying Gasp! Shutting Down...\n"); +// kill_proc (1, SIGINT, 1); +#endif + return IRQ_HANDLED; +} +*/ +extern void ifx_usb_enable_afe_oc(void); + +/** + * MEI interrupt handler + * + * \param int1 + * \param void0 + * \param regs Pointer to the structure of danube mips registers + * \ingroup Internal + */ +static irqreturn_t IFX_MEI_IrqHandle (int int1, void *void0) +{ + u32 scratch; + DSL_DEV_Device_t *pDev = (DSL_DEV_Device_t *) void0; +#if defined(CONFIG_LTQ_MEI_FW_LOOPBACK) && defined(DFE_PING_TEST) + dfe_loopback_irq_handler (pDev); + return IRQ_HANDLED; +#endif //CONFIG_AMAZON_S_MEI_FW_LOOPBACK + DSL_BSP_CB_Type_t event; + + if (pDev == NULL) + IFX_MEI_EMSG("Error: Got Interrupt but pDev is NULL!!!!\n"); + + IFX_MEI_DebugRead (pDev, ARC_MEI_MAILBOXR, &scratch, 1); + if (scratch & OMB_CODESWAP_MESSAGE_MSG_TYPE_MASK) { + IFX_MEI_EMSG("Receive Code Swap Request interrupt!!!\n"); + return IRQ_HANDLED; + } + else if (scratch & OMB_CLEAREOC_INTERRUPT_CODE) { + // clear eoc message interrupt + IFX_MEI_DMSG("OMB_CLEAREOC_INTERRUPT_CODE\n"); + event = DSL_BSP_CB_CEOC_IRQ; + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ARC2ME_STAT, ARC_TO_MEI_MSGAV); + if (dsl_bsp_event_callback[event].function) + (*dsl_bsp_event_callback[event].function)(pDev, event, dsl_bsp_event_callback[event].pData); + } else if (scratch & OMB_REBOOT_INTERRUPT_CODE) { + // Reboot + IFX_MEI_DMSG("OMB_REBOOT_INTERRUPT_CODE\n"); + event = DSL_BSP_CB_FIRMWARE_REBOOT; + + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_ARC2ME_STAT, ARC_TO_MEI_MSGAV); + + if (dsl_bsp_event_callback[event].function) + (*dsl_bsp_event_callback[event].function)(pDev, event, dsl_bsp_event_callback[event].pData); + } else { // normal message + IFX_MEI_MailboxRead (pDev, DSL_DEV_PRIVATE(pDev)->CMV_RxMsg, MSG_LENGTH); + if (DSL_DEV_PRIVATE(pDev)-> cmv_waiting == 1) { + DSL_DEV_PRIVATE(pDev)-> arcmsgav = 1; + DSL_DEV_PRIVATE(pDev)-> cmv_waiting = 0; +#if !defined(BSP_PORT_RTEMS) + MEI_WAKEUP_EVENT (DSL_DEV_PRIVATE(pDev)->wait_queue_arcmsgav); +#endif + } + else { + DSL_DEV_PRIVATE(pDev)-> modem_ready_cnt++; + memcpy ((char *) DSL_DEV_PRIVATE(pDev)->Recent_indicator, + (char *) DSL_DEV_PRIVATE(pDev)->CMV_RxMsg, MSG_LENGTH * 2); + if (((DSL_DEV_PRIVATE(pDev)->CMV_RxMsg[0] & 0xff0) >> 4) == D2H_AUTONOMOUS_MODEM_READY_MSG) { + //check ARC ready message + IFX_MEI_DMSG ("Got MODEM_READY_MSG\n"); + DSL_DEV_PRIVATE(pDev)->modem_ready = 1; + MEI_WAKEUP_EVENT (DSL_DEV_PRIVATE(pDev)->wait_queue_modemready); + } + } + } + + return IRQ_HANDLED; +} + +int +DSL_BSP_ATMLedCBRegister (int (*ifx_adsl_ledcallback) (void)) +{ + g_adsl_ledcallback = ifx_adsl_ledcallback; + return 0; +} + +int +DSL_BSP_ATMLedCBUnregister (int (*ifx_adsl_ledcallback) (void)) +{ + g_adsl_ledcallback = adsl_dummy_ledcallback; + return 0; +} + +#if 0 +int +DSL_BSP_EventCBRegister (int (*ifx_adsl_callback) + (DSL_BSP_CB_Event_t * param)) +{ + int error = 0; + + if (DSL_EventCB == NULL) { + DSL_EventCB = ifx_adsl_callback; + } + else { + error = -EIO; + } + return error; +} + +int +DSL_BSP_EventCBUnregister (int (*ifx_adsl_callback) + (DSL_BSP_CB_Event_t * param)) +{ + int error = 0; + + if (DSL_EventCB == ifx_adsl_callback) { + DSL_EventCB = NULL; + } + else { + error = -EIO; + } + return error; +} + +static int +DSL_BSP_GetEventCB (int (**ifx_adsl_callback) + (DSL_BSP_CB_Event_t * param)) +{ + *ifx_adsl_callback = DSL_EventCB; + return 0; +} +#endif + +#ifdef CONFIG_LTQ_MEI_FW_LOOPBACK +#define mte_reg_base (0x4800*4+0x20000) + +/* Iridia Registers Address Constants */ +#define MTE_Reg(r) (int)(mte_reg_base + (r*4)) + +#define IT_AMODE MTE_Reg(0x0004) + +#define TIMER_DELAY (1024) +#define BC0_BYTES (32) +#define BC1_BYTES (30) +#define NUM_MB (12) +#define TIMEOUT_VALUE 2000 + +static void +BFMWait (u32 cycle) +{ + u32 i; + for (i = 0; i < cycle; i++); +} + +static void +WriteRegLong (u32 addr, u32 data) +{ + //*((volatile u32 *)(addr)) = data; + IFX_MEI_WRITE_REGISTER_L (data, addr); +} + +static u32 +ReadRegLong (u32 addr) +{ + // u32 rd_val; + //rd_val = *((volatile u32 *)(addr)); + // return rd_val; + return IFX_MEI_READ_REGISTER_L (addr); +} + +/* This routine writes the mailbox with the data in an input array */ +static void +WriteMbox (u32 * mboxarray, u32 size) +{ + IFX_MEI_DebugWrite (&dsl_devices[0], IMBOX_BASE, mboxarray, size); + IFX_MEI_DMSG("write to %X\n", IMBOX_BASE); + IFX_MEI_LongWordWriteOffset (&dsl_devices[0], (u32) ME_ME2ARC_INT, MEI_TO_ARC_MSGAV); +} + +/* This routine reads the output mailbox and places the results into an array */ +static void +ReadMbox (u32 * mboxarray, u32 size) +{ + IFX_MEI_DebugRead (&dsl_devices[0], OMBOX_BASE, mboxarray, size); + IFX_MEI_DMSG("read from %X\n", OMBOX_BASE); +} + +static void +MEIWriteARCValue (u32 address, u32 value) +{ + u32 i, check = 0; + + /* Write address register */ + IFX_MEI_WRITE_REGISTER_L (address, ME_DBG_WR_AD + LTQ_MEI_BASE_ADDR); + + /* Write data register */ + IFX_MEI_WRITE_REGISTER_L (value, ME_DBG_DATA + LTQ_MEI_BASE_ADDR); + + /* wait until complete - timeout at 40 */ + for (i = 0; i < 40; i++) { + check = IFX_MEI_READ_REGISTER_L (ME_ARC2ME_STAT + LTQ_MEI_BASE_ADDR); + + if ((check & ARC_TO_MEI_DBG_DONE)) + break; + } + /* clear the flag */ + IFX_MEI_WRITE_REGISTER_L (ARC_TO_MEI_DBG_DONE, ME_ARC2ME_STAT + LTQ_MEI_BASE_ADDR); +} + +void +arc_code_page_download (uint32_t arc_code_length, uint32_t * start_address) +{ + int count; + + IFX_MEI_DMSG("try to download pages,size=%d\n", arc_code_length); + IFX_MEI_ControlModeSet (&dsl_devices[0], MEI_MASTER_MODE); + IFX_MEI_HaltArc (&dsl_devices[0]); + IFX_MEI_LongWordWriteOffset (&dsl_devices[0], (u32) ME_DX_AD, 0); + for (count = 0; count < arc_code_length; count++) { + IFX_MEI_LongWordWriteOffset (&dsl_devices[0], (u32) ME_DX_DATA, + *(start_address + count)); + } + IFX_MEI_ControlModeSet (&dsl_devices[0], JTAG_MASTER_MODE); +} +static int +load_jump_table (unsigned long addr) +{ + int i; + uint32_t addr_le, addr_be; + uint32_t jump_table[32]; + + for (i = 0; i < 16; i++) { + addr_le = i * 8 + addr; + addr_be = ((addr_le >> 16) & 0xffff); + addr_be |= ((addr_le & 0xffff) << 16); + jump_table[i * 2 + 0] = 0x0f802020; + jump_table[i * 2 + 1] = addr_be; + //printk("jt %X %08X %08X\n",i,jump_table[i*2+0],jump_table[i*2+1]); + } + arc_code_page_download (32, &jump_table[0]); +return 0; +} + +int got_int = 0; + +void +dfe_loopback_irq_handler (DSL_DEV_Device_t *pDev) +{ + uint32_t rd_mbox[10]; + + memset (&rd_mbox[0], 0, 10 * 4); + ReadMbox (&rd_mbox[0], 6); + if (rd_mbox[0] == 0x0) { + FX_MEI_DMSG("Get ARC_ACK\n"); + got_int = 1; + } + else if (rd_mbox[0] == 0x5) { + IFX_MEI_DMSG("Get ARC_BUSY\n"); + got_int = 2; + } + else if (rd_mbox[0] == 0x3) { + IFX_MEI_DMSG("Get ARC_EDONE\n"); + if (rd_mbox[1] == 0x0) { + got_int = 3; + IFX_MEI_DMSG("Get E_MEMTEST\n"); + if (rd_mbox[2] != 0x1) { + got_int = 4; + IFX_MEI_DMSG("Get Result %X\n", rd_mbox[2]); + } + } + } + IFX_MEI_LongWordWriteOffset (&dsl_devices[0], (u32) ME_ARC2ME_STAT, + ARC_TO_MEI_DBG_DONE); + MEI_MASK_AND_ACK_IRQ (pDev->nIrq[IFX_DFEIR]); + disable_irq (pDev->nIrq[IFX_DFEIR]); + //got_int = 1; + return; +} + +static void +wait_mem_test_result (void) +{ + uint32_t mbox[5]; + mbox[0] = 0; + + IFX_MEI_DMSG("Waiting Starting\n"); + while (mbox[0] == 0) { + ReadMbox (&mbox[0], 5); + } + IFX_MEI_DMSG("Try to get mem test result.\n"); + ReadMbox (&mbox[0], 5); + if (mbox[0] == 0xA) { + IFX_MEI_DMSG("Success.\n"); + } + else if (mbox[0] == 0xA) { + IFX_MEI_EMSG("Fail,address %X,except data %X,receive data %X\n", + mbox[1], mbox[2], mbox[3]); + } + else { + IFX_MEI_EMSG("Fail\n"); + } +} + +static int +arc_ping_testing (DSL_DEV_Device_t *pDev) +{ +#define MEI_PING 0x00000001 + uint32_t wr_mbox[10], rd_mbox[10]; + int i; + + for (i = 0; i < 10; i++) { + wr_mbox[i] = 0; + rd_mbox[i] = 0; + } + + FX_MEI_DMSG("send ping msg\n"); + wr_mbox[0] = MEI_PING; + WriteMbox (&wr_mbox[0], 10); + + while (got_int == 0) { + MEI_WAIT (100); + } + + IFX_MEI_DMSG("send start event\n"); + got_int = 0; + + wr_mbox[0] = 0x4; + wr_mbox[1] = 0; + wr_mbox[2] = 0; + wr_mbox[3] = (uint32_t) 0xf5acc307e; + wr_mbox[4] = 5; + wr_mbox[5] = 2; + wr_mbox[6] = 0x1c000; + wr_mbox[7] = 64; + wr_mbox[8] = 0; + wr_mbox[9] = 0; + WriteMbox (&wr_mbox[0], 10); + DSL_ENABLE_IRQ (pDev->nIrq[IFX_DFEIR]); + //printk("IFX_MEI_MailboxWrite ret=%d\n",i); + IFX_MEI_LongWordWriteOffset (&dsl_devices[0], + (u32) ME_ME2ARC_INT, + MEI_TO_ARC_MSGAV); + IFX_MEI_DMSG("sleeping\n"); + while (1) { + if (got_int > 0) { + + if (got_int > 3) + IFX_MEI_DMSG("got_int >>>> 3\n"); + else + IFX_MEI_DMSG("got int = %d\n", got_int); + got_int = 0; + //schedule(); + DSL_ENABLE_IRQ (pDev->nIrq[IFX_DFEIR]); + } + //mbox_read(&rd_mbox[0],6); + MEI_WAIT (100); + } + return 0; +} + +static DSL_DEV_MeiError_t +DFE_Loopback_Test (void) +{ + int i = 0; + u32 arc_debug_data = 0, temp; + DSL_DEV_Device_t *pDev = &dsl_devices[0]; + uint32_t wr_mbox[10]; + + IFX_MEI_ResetARC (pDev); + // start the clock + arc_debug_data = ACL_CLK_MODE_ENABLE; + IFX_MEI_DebugWrite (pDev, CRI_CCR0, &arc_debug_data, 1); + +#if defined( DFE_PING_TEST )|| defined( DFE_ATM_LOOPBACK) + // WriteARCreg(AUX_XMEM_LTEST,0); + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); +#define AUX_XMEM_LTEST 0x128 + _IFX_MEI_DBGLongWordWrite (pDev, MEI_DEBUG_DEC_AUX_MASK, AUX_XMEM_LTEST, 0); + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + // WriteARCreg(AUX_XDMA_GAP,0); + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); +#define AUX_XDMA_GAP 0x114 + _IFX_MEI_DBGLongWordWrite (pDev, MEI_DEBUG_DEC_AUX_MASK, AUX_XDMA_GAP, 0); + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); + temp = 0; + _IFX_MEI_DBGLongWordWrite (pDev, MEI_DEBUG_DEC_AUX_MASK, + (u32) ME_XDATA_BASE_SH + LTQ_MEI_BASE_ADDR, temp); + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + i = IFX_MEI_DFEMemoryAlloc (pDev, SDRAM_SEGMENT_SIZE * 16); + if (i >= 0) { + int idx; + + for (idx = 0; idx < i; idx++) { + DSL_DEV_PRIVATE(pDev)->adsl_mem_info[idx].type = FREE_RELOAD; + IFX_MEI_WRITE_REGISTER_L ((((uint32_t) DSL_DEV_PRIVATE(pDev)->adsl_mem_info[idx].address) & 0x0fffffff), + LTQ_MEI_BASE_ADDR + ME_XMEM_BAR_BASE + idx * 4); + IFX_MEI_DMSG("bar%d(%X)=%X\n", idx, + LTQ_MEI_BASE_ADDR + ME_XMEM_BAR_BASE + + idx * 4, (((uint32_t) + ((ifx_mei_device_private_t *) + pDev->pPriv)->adsl_mem_info[idx]. + address) & 0x0fffffff)); + memset ((u8 *) DSL_DEV_PRIVATE(pDev)->adsl_mem_info[idx].address, 0, SDRAM_SEGMENT_SIZE); + } + + IFX_MEI_LongWordWriteOffset (pDev, (u32) ME_XDATA_BASE_SH, + ((unsigned long) DSL_DEV_PRIVATE(pDev)->adsl_mem_info[XDATA_REGISTER].address) & 0x0FFFFFFF); + } + else { + IFX_MEI_EMSG ("cannot load image: no memory\n"); + return DSL_DEV_MEI_ERR_FAILURE; + } + //WriteARCreg(AUX_IC_CTRL,2); + IFX_MEI_DMSG("Setting MEI_MASTER_MODE..\n"); + IFX_MEI_ControlModeSet (pDev, MEI_MASTER_MODE); +#define AUX_IC_CTRL 0x11 + _IFX_MEI_DBGLongWordWrite (pDev, MEI_DEBUG_DEC_AUX_MASK, + AUX_IC_CTRL, 2); + IFX_MEI_DMSG("Setting JTAG_MASTER_MODE..\n"); + IFX_MEI_ControlModeSet (pDev, JTAG_MASTER_MODE); + + IFX_MEI_DMSG("Halting ARC...\n"); + IFX_MEI_HaltArc (&dsl_devices[0]); + +#ifdef DFE_PING_TEST + + IFX_MEI_DMSG("ping test image size=%d\n", sizeof (arc_ahb_access_code)); + memcpy ((u8 *) (DSL_DEV_PRIVATE(pDev)-> + adsl_mem_info[0].address + 0x1004), + &arc_ahb_access_code[0], sizeof (arc_ahb_access_code)); + load_jump_table (0x80000 + 0x1004); + +#endif //DFE_PING_TEST + + IFX_MEI_DMSG("ARC ping test code download complete\n"); +#endif //defined( DFE_PING_TEST )|| defined( DFE_ATM_LOOPBACK) +#ifdef DFE_MEM_TEST + IFX_MEI_LongWordWriteOffset (&dsl_devices[0], (u32) ME_ARC2ME_MASK, MSGAV_EN); + + arc_code_page_download (1537, &code_array[0]); + IFX_MEI_DMSG("ARC mem test code download complete\n"); +#endif //DFE_MEM_TEST +#ifdef DFE_ATM_LOOPBACK + arc_debug_data = 0xf; + arc_code_page_download (sizeof(code_array) / sizeof(*code_array), &code_array[0]); + wr_mbox[0] = 0; //TIMER_DELAY - org: 1024 + wr_mbox[1] = 0; //TXFB_START0 + wr_mbox[2] = 0x7f; //TXFB_END0 - org: 49 + wr_mbox[3] = 0x80; //TXFB_START1 - org: 80 + wr_mbox[4] = 0xff; //TXFB_END1 - org: 109 + wr_mbox[5] = 0x100; //RXFB_START0 - org: 0 + wr_mbox[6] = 0x17f; //RXFB_END0 - org: 49 + wr_mbox[7] = 0x180; //RXFB_START1 - org: 256 + wr_mbox[8] = 0x1ff; //RXFB_END1 - org: 315 + WriteMbox (&wr_mbox[0], 9); + // Start Iridia IT_AMODE (in dmp access) why is it required? + IFX_MEI_DebugWrite (&dsl_devices[0], 0x32010, &arc_debug_data, 1); +#endif //DFE_ATM_LOOPBACK + IFX_MEI_IRQEnable (pDev); + IFX_MEI_DMSG("run ARC...\n"); + IFX_MEI_RunArc (&dsl_devices[0]); + +#ifdef DFE_PING_TEST + arc_ping_testing (pDev); +#endif //DFE_PING_TEST +#ifdef DFE_MEM_TEST + wait_mem_test_result (); +#endif //DFE_MEM_TEST + + IFX_MEI_DFEMemoryFree (pDev, FREE_ALL); + return DSL_DEV_MEI_ERR_SUCCESS; +} + +#endif //CONFIG_AMAZON_S_MEI_FW_LOOPBACK + +static int +IFX_MEI_InitDevNode (int num) +{ + if (num == 0) { + if ((dev_major = register_chrdev (dev_major, IFX_MEI_DEVNAME, &bsp_mei_operations)) < 0) { + IFX_MEI_EMSG ("register_chrdev(%d %s) failed!\n", dev_major, IFX_MEI_DEVNAME); + return -ENODEV; + } + } + return 0; +} + +static int +IFX_MEI_CleanUpDevNode (int num) +{ + if (num == 0) + unregister_chrdev (dev_major, MEI_DIRNAME); + return 0; +} + +static int +IFX_MEI_InitDevice (int num) +{ + DSL_DEV_Device_t *pDev; + u32 temp; + pDev = &dsl_devices[num]; + if (pDev == NULL) + return -ENOMEM; + pDev->pPriv = &sDanube_Mei_Private[num]; + memset (pDev->pPriv, 0, sizeof (ifx_mei_device_private_t)); + + memset (&DSL_DEV_PRIVATE(pDev)-> + adsl_mem_info[0], 0, + sizeof (smmu_mem_info_t) * MAX_BAR_REGISTERS); + + if (num == 0) { + pDev->nIrq[IFX_DFEIR] = LTQ_MEI_INT; + pDev->nIrq[IFX_DYING_GASP] = LTQ_MEI_DYING_GASP_INT; + pDev->base_address = KSEG1 + LTQ_MEI_BASE_ADDR; + + /* Power up MEI */ +#ifdef CONFIG_LANTIQ_AMAZON_SE + *LTQ_PMU_PWDCR &= ~(1 << 9); // enable dsl + *LTQ_PMU_PWDCR &= ~(1 << 15); // enable AHB base +#else + temp = ltq_r32(LTQ_PMU_PWDCR); + temp &= 0xffff7dbe; + ltq_w32(temp, LTQ_PMU_PWDCR); +#endif + } + pDev->nInUse = 0; + DSL_DEV_PRIVATE(pDev)->modem_ready = 0; + DSL_DEV_PRIVATE(pDev)->arcmsgav = 0; + + MEI_INIT_WAKELIST ("arcq", DSL_DEV_PRIVATE(pDev)->wait_queue_arcmsgav); // for ARCMSGAV + MEI_INIT_WAKELIST ("arcr", DSL_DEV_PRIVATE(pDev)->wait_queue_modemready); // for arc modem ready + + MEI_MUTEX_INIT (DSL_DEV_PRIVATE(pDev)->mei_cmv_sema, 1); // semaphore initialization, mutex +#if 0 + MEI_MASK_AND_ACK_IRQ (pDev->nIrq[IFX_DFEIR]); + MEI_MASK_AND_ACK_IRQ (pDev->nIrq[IFX_DYING_GASP]); +#endif + if (request_irq (pDev->nIrq[IFX_DFEIR], IFX_MEI_IrqHandle, 0, "DFEIR", pDev) != 0) { + IFX_MEI_EMSG ("request_irq %d failed!\n", pDev->nIrq[IFX_DFEIR]); + return -1; + } + /*if (request_irq (pDev->nIrq[IFX_DYING_GASP], IFX_MEI_Dying_Gasp_IrqHandle, 0, "DYING_GASP", pDev) != 0) { + IFX_MEI_EMSG ("request_irq %d failed!\n", pDev->nIrq[IFX_DYING_GASP]); + return -1; + }*/ +// IFX_MEI_DMSG("Device %d initialized. IER %#x\n", num, bsp_get_irq_ier(pDev->nIrq[IFX_DYING_GASP])); + return 0; +} + +static int +IFX_MEI_ExitDevice (int num) +{ + DSL_DEV_Device_t *pDev; + pDev = &dsl_devices[num]; + + if (pDev == NULL) + return -EIO; + + disable_irq (pDev->nIrq[IFX_DFEIR]); + disable_irq (pDev->nIrq[IFX_DYING_GASP]); + + free_irq(pDev->nIrq[IFX_DFEIR], pDev); + free_irq(pDev->nIrq[IFX_DYING_GASP], pDev); + + return 0; +} + +static DSL_DEV_Device_t * +IFX_BSP_HandleGet (int maj, int num) +{ + if (num > BSP_MAX_DEVICES) + return NULL; + return &dsl_devices[num]; +} + +DSL_DEV_Device_t * +DSL_BSP_DriverHandleGet (int maj, int num) +{ + DSL_DEV_Device_t *pDev; + + if (num > BSP_MAX_DEVICES) + return NULL; + + pDev = &dsl_devices[num]; + if (!try_module_get(pDev->owner)) + return NULL; + + pDev->nInUse++; + return pDev; +} + +int +DSL_BSP_DriverHandleDelete (DSL_DEV_Device_t * nHandle) +{ + DSL_DEV_Device_t *pDev = (DSL_DEV_Device_t *) nHandle; + if (pDev->nInUse) + pDev->nInUse--; + module_put(pDev->owner); + return 0; +} + +static int +IFX_MEI_Open (DSL_DRV_inode_t * ino, DSL_DRV_file_t * fil) +{ + int maj = MAJOR (ino->i_rdev); + int num = MINOR (ino->i_rdev); + + DSL_DEV_Device_t *pDev = NULL; + if ((pDev = DSL_BSP_DriverHandleGet (maj, num)) == NULL) { + IFX_MEI_EMSG("open(%d:%d) fail!\n", maj, num); + return -EIO; + } + fil->private_data = pDev; + return 0; +} + +static int +IFX_MEI_Release (DSL_DRV_inode_t * ino, DSL_DRV_file_t * fil) +{ + //int maj = MAJOR(ino->i_rdev); + int num = MINOR (ino->i_rdev); + DSL_DEV_Device_t *pDev; + + pDev = &dsl_devices[num]; + if (pDev == NULL) + return -EIO; + DSL_BSP_DriverHandleDelete (pDev); + return 0; +} + +/** + * Callback function for linux userspace program writing + */ +static ssize_t +IFX_MEI_Write (DSL_DRV_file_t * filp, const char *buf, size_t size, loff_t * loff) +{ + DSL_DEV_MeiError_t mei_error = DSL_DEV_MEI_ERR_FAILURE; + long offset = 0; + DSL_DEV_Device_t *pDev = (DSL_DEV_Device_t *) filp->private_data; + + if (pDev == NULL) + return -EIO; + + mei_error = + DSL_BSP_FWDownload (pDev, buf, size, (long *) loff, &offset); + + if (mei_error == DSL_DEV_MEI_ERR_FAILURE) + return -EIO; + return (ssize_t) offset; +} + +/** + * Callback function for linux userspace program ioctling + */ +static int +IFX_MEI_IoctlCopyFrom (int from_kernel, char *dest, char *from, int size) +{ + int ret = 0; + + if (!from_kernel) + ret = copy_from_user ((char *) dest, (char *) from, size); + else + ret = (int)memcpy ((char *) dest, (char *) from, size); + return ret; +} + +static int +IFX_MEI_IoctlCopyTo (int from_kernel, char *dest, char *from, int size) +{ + int ret = 0; + + if (!from_kernel) + ret = copy_to_user ((char *) dest, (char *) from, size); + else + ret = (int)memcpy ((char *) dest, (char *) from, size); + return ret; +} + +int +IFX_MEI_Ioctls (DSL_DEV_Device_t * pDev, int from_kernel, unsigned int command, unsigned long lon) +{ + int i = 0; + int meierr = DSL_DEV_MEI_ERR_SUCCESS; + u32 base_address = LTQ_MEI_BASE_ADDR; + DSL_DEV_WinHost_Message_t winhost_msg, m; +// DSL_DEV_MeiDebug_t debugrdwr; + DSL_DEV_MeiReg_t regrdwr; + + switch (command) { + + case DSL_FIO_BSP_CMV_WINHOST: + IFX_MEI_IoctlCopyFrom (from_kernel, (char *) winhost_msg.msg.TxMessage, + (char *) lon, MSG_LENGTH * 2); + + if ((meierr = DSL_BSP_SendCMV (pDev, winhost_msg.msg.TxMessage, YES_REPLY, + winhost_msg.msg.RxMessage)) != DSL_DEV_MEI_ERR_SUCCESS) { + IFX_MEI_EMSG ("WINHOST CMV fail :TxMessage:%X %X %X %X, RxMessage:%X %X %X %X %X\n", + winhost_msg.msg.TxMessage[0], winhost_msg.msg.TxMessage[1], winhost_msg.msg.TxMessage[2], winhost_msg.msg.TxMessage[3], + winhost_msg.msg.RxMessage[0], winhost_msg.msg.RxMessage[1], winhost_msg.msg.RxMessage[2], winhost_msg.msg.RxMessage[3], + winhost_msg.msg.RxMessage[4]); + meierr = DSL_DEV_MEI_ERR_FAILURE; + } + else { + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, + (char *) winhost_msg.msg.RxMessage, + MSG_LENGTH * 2); + } + break; + + case DSL_FIO_BSP_CMV_READ: + IFX_MEI_IoctlCopyFrom (from_kernel, (char *) (®rdwr), + (char *) lon, sizeof (DSL_DEV_MeiReg_t)); + + IFX_MEI_LongWordRead ((u32) regrdwr.iAddress, + (u32 *) & (regrdwr.iData)); + + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, + (char *) (®rdwr), + sizeof (DSL_DEV_MeiReg_t)); + + break; + + case DSL_FIO_BSP_CMV_WRITE: + IFX_MEI_IoctlCopyFrom (from_kernel, (char *) (®rdwr), + (char *) lon, sizeof (DSL_DEV_MeiReg_t)); + + IFX_MEI_LongWordWrite ((u32) regrdwr.iAddress, + regrdwr.iData); + break; + + case DSL_FIO_BSP_GET_BASE_ADDRESS: + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, + (char *) (&base_address), + sizeof (base_address)); + break; + + case DSL_FIO_BSP_IS_MODEM_READY: + i = IFX_MEI_IsModemReady (pDev); + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, + (char *) (&i), sizeof (int)); + meierr = DSL_DEV_MEI_ERR_SUCCESS; + break; + case DSL_FIO_BSP_RESET: + case DSL_FIO_BSP_REBOOT: + meierr = IFX_MEI_CpuModeSet (pDev, DSL_CPU_RESET); + meierr = IFX_MEI_CpuModeSet (pDev, DSL_CPU_HALT); + break; + + case DSL_FIO_BSP_HALT: + meierr = IFX_MEI_CpuModeSet (pDev, DSL_CPU_HALT); + break; + + case DSL_FIO_BSP_RUN: + meierr = IFX_MEI_CpuModeSet (pDev, DSL_CPU_RUN); + break; + case DSL_FIO_BSP_BOOTDOWNLOAD: + meierr = IFX_MEI_DownloadBootCode (pDev); + break; + case DSL_FIO_BSP_JTAG_ENABLE: + meierr = IFX_MEI_ArcJtagEnable (pDev, 1); + break; + + case DSL_FIO_BSP_REMOTE: + IFX_MEI_IoctlCopyFrom (from_kernel, (char *) (&i), + (char *) lon, sizeof (int)); + + meierr = IFX_MEI_AdslMailboxIRQEnable (pDev, i); + break; + + case DSL_FIO_BSP_DSL_START: + IFX_MEI_DMSG("DSL_FIO_BSP_DSL_START\n"); + if ((meierr = IFX_MEI_RunAdslModem (pDev)) != DSL_DEV_MEI_ERR_SUCCESS) { + IFX_MEI_EMSG ("IFX_MEI_RunAdslModem() error..."); + meierr = DSL_DEV_MEI_ERR_FAILURE; + } + break; + +/* case DSL_FIO_BSP_DEBUG_READ: + case DSL_FIO_BSP_DEBUG_WRITE: + IFX_MEI_IoctlCopyFrom (from_kernel, + (char *) (&debugrdwr), + (char *) lon, + sizeof (debugrdwr)); + + if (command == DSL_FIO_BSP_DEBUG_READ) + meierr = DSL_BSP_MemoryDebugAccess (pDev, + DSL_BSP_MEMORY_READ, + debugrdwr. + iAddress, + debugrdwr. + buffer, + debugrdwr. + iCount); + else + meierr = DSL_BSP_MemoryDebugAccess (pDev, + DSL_BSP_MEMORY_WRITE, + debugrdwr. + iAddress, + debugrdwr. + buffer, + debugrdwr. + iCount); + + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, (char *) (&debugrdwr), sizeof (debugrdwr)); + break;*/ + case DSL_FIO_BSP_GET_VERSION: + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, (char *) (&bsp_mei_version), sizeof (DSL_DEV_Version_t)); + break; + +#define LTQ_MPS_CHIPID_VERSION_GET(value) (((value) >> 28) & ((1 << 4) - 1)) + case DSL_FIO_BSP_GET_CHIP_INFO: + bsp_chip_info.major = 1; + bsp_chip_info.minor = LTQ_MPS_CHIPID_VERSION_GET(*LTQ_MPS_CHIPID); + IFX_MEI_IoctlCopyTo (from_kernel, (char *) lon, (char *) (&bsp_chip_info), sizeof (DSL_DEV_HwVersion_t)); + meierr = DSL_DEV_MEI_ERR_SUCCESS; + break; + + case DSL_FIO_BSP_FREE_RESOURCE: + makeCMV (H2D_CMV_READ, DSL_CMV_GROUP_STAT, 4, 0, 1, NULL, m.msg.TxMessage); + if (DSL_BSP_SendCMV (pDev, m.msg.TxMessage, YES_REPLY, m.msg.RxMessage) != DSL_DEV_MEI_ERR_SUCCESS) { + meierr = DSL_DEV_MEI_ERR_FAILURE; + return -EIO; + } + IFX_MEI_DMSG("RxMessage[4] = %#x\n", m.msg.RxMessage[4]); + if (!(m.msg.RxMessage[4] & DSL_DEV_STAT_CODESWAP_COMPLETE)) { + meierr = DSL_DEV_MEI_ERR_FAILURE; + return -EAGAIN; + } + IFX_MEI_DMSG("Freeing all memories marked FREE_SHOWTIME\n"); + IFX_MEI_DFEMemoryFree (pDev, FREE_SHOWTIME); + meierr = DSL_DEV_MEI_ERR_SUCCESS; + break; +#ifdef CONFIG_IFXMIPS_AMAZON_SE + case DSL_FIO_ARC_MUX_TEST: + AMAZON_SE_MEI_ARC_MUX_Test(); + break; +#endif + default: +// IFX_MEI_EMSG("Invalid IOCTL command: %d\n"); + break; + } + return meierr; +} + +#ifdef CONFIG_IFXMIPS_AMAZON_SE +void AMAZON_SE_MEI_ARC_MUX_Test(void) +{ + u32 *p, i; + *LTQ_RCU_RST |= LTQ_RCU_RST_REQ_MUX_ARC; + + p = (u32*)(DFE_LDST_BASE_ADDR + IRAM0_BASE); + IFX_MEI_EMSG("Writing to IRAM0(%p)...\n", p); + for (i = 0; i < IRAM0_SIZE/sizeof(u32); i++, p++) { + *p = 0xdeadbeef; + if (*p != 0xdeadbeef) + IFX_MEI_EMSG("%p: %#x\n", p, *p); + } + + p = (u32*)(DFE_LDST_BASE_ADDR + IRAM1_BASE); + IFX_MEI_EMSG("Writing to IRAM1(%p)...\n", p); + for (i = 0; i < IRAM1_SIZE/sizeof(u32); i++, p++) { + *p = 0xdeadbeef; + if (*p != 0xdeadbeef) + IFX_MEI_EMSG("%p: %#x\n", p, *p); + } + + p = (u32*)(DFE_LDST_BASE_ADDR + BRAM_BASE); + IFX_MEI_EMSG("Writing to BRAM(%p)...\n", p); + for (i = 0; i < BRAM_SIZE/sizeof(u32); i++, p++) { + *p = 0xdeadbeef; + if (*p != 0xdeadbeef) + IFX_MEI_EMSG("%p: %#x\n", p, *p); + } + + p = (u32*)(DFE_LDST_BASE_ADDR + XRAM_BASE); + IFX_MEI_EMSG("Writing to XRAM(%p)...\n", p); + for (i = 0; i < XRAM_SIZE/sizeof(u32); i++, p++) { + *p = 0xdeadbeef; + if (*p != 0xdeadbeef) + IFX_MEI_EMSG("%p: %#x\n", p, *p); + } + + p = (u32*)(DFE_LDST_BASE_ADDR + YRAM_BASE); + IFX_MEI_EMSG("Writing to YRAM(%p)...\n", p); + for (i = 0; i < YRAM_SIZE/sizeof(u32); i++, p++) { + *p = 0xdeadbeef; + if (*p != 0xdeadbeef) + IFX_MEI_EMSG("%p: %#x\n", p, *p); + } + + p = (u32*)(DFE_LDST_BASE_ADDR + EXT_MEM_BASE); + IFX_MEI_EMSG("Writing to EXT_MEM(%p)...\n", p); + for (i = 0; i < EXT_MEM_SIZE/sizeof(u32); i++, p++) { + *p = 0xdeadbeef; + if (*p != 0xdeadbeef) + IFX_MEI_EMSG("%p: %#x\n", p, *p); + } + *LTQ_RCU_RST &= ~LTQ_RCU_RST_REQ_MUX_ARC; +} +#endif +int +DSL_BSP_KernelIoctls (DSL_DEV_Device_t * pDev, unsigned int command, + unsigned long lon) +{ + int error = 0; + + error = IFX_MEI_Ioctls (pDev, 1, command, lon); + return error; +} + +static long +IFX_MEI_UserIoctls (DSL_DRV_file_t * fil, + unsigned int command, unsigned long lon) +{ + int error = 0; + DSL_DEV_Device_t *pDev; + + pDev = IFX_BSP_HandleGet (0, 0); + if (pDev == NULL) + return -EIO; + + error = IFX_MEI_Ioctls (pDev, 0, command, lon); + return error; +} + +static int adsl_dummy_ledcallback(void) +{ + return 0; +} + +int ifx_mei_atm_led_blink(void) +{ + return g_adsl_ledcallback(); +} +EXPORT_SYMBOL(ifx_mei_atm_led_blink); + +int ifx_mei_atm_showtime_check(int *is_showtime, struct port_cell_info *port_cell, void **xdata_addr) +{ + int i; + + if ( is_showtime ) { + *is_showtime = g_tx_link_rate[0] == 0 && g_tx_link_rate[1] == 0 ? 0 : 1; + } + + if ( port_cell ) { + for ( i = 0; i < port_cell->port_num && i < 2; i++ ) + port_cell->tx_link_rate[i] = g_tx_link_rate[i]; + } + + if ( xdata_addr ) { + if ( g_tx_link_rate[0] == 0 && g_tx_link_rate[1] == 0 ) + *xdata_addr = NULL; + else + *xdata_addr = g_xdata_addr; + } + + return 0; +} +EXPORT_SYMBOL(ifx_mei_atm_showtime_check); + +/* + * Writing function for linux proc filesystem + */ +static int ltq_mei_probe(struct platform_device *pdev) +{ + int i = 0; + static struct class *dsl_class; + + pr_info("IFX MEI Version %ld.%02ld.%02ld\n", bsp_mei_version.major, bsp_mei_version.minor, bsp_mei_version.revision); + + for (i = 0; i < BSP_MAX_DEVICES; i++) { + if (IFX_MEI_InitDevice (i) != 0) { + IFX_MEI_EMSG("Init device fail!\n"); + return -EIO; + } + IFX_MEI_InitDevNode (i); + } + for (i = 0; i <= DSL_BSP_CB_LAST ; i++) + dsl_bsp_event_callback[i].function = NULL; + +#ifdef CONFIG_LTQ_MEI_FW_LOOPBACK + IFX_MEI_DMSG("Start loopback test...\n"); + DFE_Loopback_Test (); +#endif + dsl_class = class_create(THIS_MODULE, "ifx_mei"); + device_create(dsl_class, NULL, MKDEV(MEI_MAJOR, 0), NULL, "ifx_mei"); + return 0; +} + +static int ltq_mei_remove(struct platform_device *pdev) +{ + int i = 0; + int num; + + for (num = 0; num < BSP_MAX_DEVICES; num++) { + IFX_MEI_CleanUpDevNode (num); + } + + for (i = 0; i < BSP_MAX_DEVICES; i++) { + for (i = 0; i < BSP_MAX_DEVICES; i++) { + IFX_MEI_ExitDevice (i); + } + } + return 0; +} + +static const struct of_device_id ltq_mei_match[] = { + { .compatible = "lantiq,mei-xway"}, + {}, +}; + +static struct platform_driver ltq_mei_driver = { + .probe = ltq_mei_probe, + .remove = ltq_mei_remove, + .driver = { + .name = "lantiq,mei-xway", + .owner = THIS_MODULE, + .of_match_table = ltq_mei_match, + }, +}; + +module_platform_driver(ltq_mei_driver); + +/* export function for DSL Driver */ + +/* The functions of MEI_DriverHandleGet and MEI_DriverHandleDelete are +something like open/close in kernel space , where the open could be used +to register a callback for autonomous messages and returns a mei driver context pointer (comparable to the file descriptor in user space) + The context will be required for the multi line chips future! */ + +EXPORT_SYMBOL (DSL_BSP_DriverHandleGet); +EXPORT_SYMBOL (DSL_BSP_DriverHandleDelete); + +EXPORT_SYMBOL (DSL_BSP_ATMLedCBRegister); +EXPORT_SYMBOL (DSL_BSP_ATMLedCBUnregister); +EXPORT_SYMBOL (DSL_BSP_KernelIoctls); +EXPORT_SYMBOL (DSL_BSP_AdslLedInit); +//EXPORT_SYMBOL (DSL_BSP_AdslLedSet); +EXPORT_SYMBOL (DSL_BSP_FWDownload); +EXPORT_SYMBOL (DSL_BSP_Showtime); + +EXPORT_SYMBOL (DSL_BSP_MemoryDebugAccess); +EXPORT_SYMBOL (DSL_BSP_SendCMV); + +// provide a register/unregister function for DSL driver to register a event callback function +EXPORT_SYMBOL (DSL_BSP_EventCBRegister); +EXPORT_SYMBOL (DSL_BSP_EventCBUnregister); + +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3