18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2003, 2004 48c2ecf20Sopenharmony_ci * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2005-2007 Matthieu Castet <castet.matthieu@free.fr> 78c2ecf20Sopenharmony_ci * Copyright (c) 2005-2007 Stanislaw Gruszka <stf_xl@wp.pl> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * HISTORY : some part of the code was base on ueagle 1.3 BSD driver, 108c2ecf20Sopenharmony_ci * Damien Bergamini agree to put his code under a DUAL GPL/BSD license. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The rest of the code was was rewritten from scratch. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 178c2ecf20Sopenharmony_ci#include <linux/crc32.h> 188c2ecf20Sopenharmony_ci#include <linux/usb.h> 198c2ecf20Sopenharmony_ci#include <linux/firmware.h> 208c2ecf20Sopenharmony_ci#include <linux/ctype.h> 218c2ecf20Sopenharmony_ci#include <linux/sched.h> 228c2ecf20Sopenharmony_ci#include <linux/kthread.h> 238c2ecf20Sopenharmony_ci#include <linux/mutex.h> 248c2ecf20Sopenharmony_ci#include <linux/freezer.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "usbatm.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define EAGLEUSBVERSION "ueagle 1.4" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Debug macros 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#define uea_dbg(usb_dev, format, args...) \ 398c2ecf20Sopenharmony_ci do { \ 408c2ecf20Sopenharmony_ci if (debug >= 1) \ 418c2ecf20Sopenharmony_ci dev_dbg(&(usb_dev)->dev, \ 428c2ecf20Sopenharmony_ci "[ueagle-atm dbg] %s: " format, \ 438c2ecf20Sopenharmony_ci __func__, ##args); \ 448c2ecf20Sopenharmony_ci } while (0) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define uea_vdbg(usb_dev, format, args...) \ 478c2ecf20Sopenharmony_ci do { \ 488c2ecf20Sopenharmony_ci if (debug >= 2) \ 498c2ecf20Sopenharmony_ci dev_dbg(&(usb_dev)->dev, \ 508c2ecf20Sopenharmony_ci "[ueagle-atm vdbg] " format, ##args); \ 518c2ecf20Sopenharmony_ci } while (0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define uea_enters(usb_dev) \ 548c2ecf20Sopenharmony_ci uea_vdbg(usb_dev, "entering %s\n" , __func__) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define uea_leaves(usb_dev) \ 578c2ecf20Sopenharmony_ci uea_vdbg(usb_dev, "leaving %s\n" , __func__) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define uea_err(usb_dev, format, args...) \ 608c2ecf20Sopenharmony_ci dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define uea_warn(usb_dev, format, args...) \ 638c2ecf20Sopenharmony_ci dev_warn(&(usb_dev)->dev , "[Ueagle-atm] " format, ##args) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define uea_info(usb_dev, format, args...) \ 668c2ecf20Sopenharmony_ci dev_info(&(usb_dev)->dev , "[ueagle-atm] " format, ##args) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct intr_pkt; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* cmv's from firmware */ 718c2ecf20Sopenharmony_cistruct uea_cmvs_v1 { 728c2ecf20Sopenharmony_ci u32 address; 738c2ecf20Sopenharmony_ci u16 offset; 748c2ecf20Sopenharmony_ci u32 data; 758c2ecf20Sopenharmony_ci} __packed; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct uea_cmvs_v2 { 788c2ecf20Sopenharmony_ci u32 group; 798c2ecf20Sopenharmony_ci u32 address; 808c2ecf20Sopenharmony_ci u32 offset; 818c2ecf20Sopenharmony_ci u32 data; 828c2ecf20Sopenharmony_ci} __packed; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* information about currently processed cmv */ 858c2ecf20Sopenharmony_cistruct cmv_dsc_e1 { 868c2ecf20Sopenharmony_ci u8 function; 878c2ecf20Sopenharmony_ci u16 idx; 888c2ecf20Sopenharmony_ci u32 address; 898c2ecf20Sopenharmony_ci u16 offset; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct cmv_dsc_e4 { 938c2ecf20Sopenharmony_ci u16 function; 948c2ecf20Sopenharmony_ci u16 offset; 958c2ecf20Sopenharmony_ci u16 address; 968c2ecf20Sopenharmony_ci u16 group; 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciunion cmv_dsc { 1008c2ecf20Sopenharmony_ci struct cmv_dsc_e1 e1; 1018c2ecf20Sopenharmony_ci struct cmv_dsc_e4 e4; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct uea_softc { 1058c2ecf20Sopenharmony_ci struct usb_device *usb_dev; 1068c2ecf20Sopenharmony_ci struct usbatm_data *usbatm; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci int modem_index; 1098c2ecf20Sopenharmony_ci unsigned int driver_info; 1108c2ecf20Sopenharmony_ci int annex; 1118c2ecf20Sopenharmony_ci#define ANNEXA 0 1128c2ecf20Sopenharmony_ci#define ANNEXB 1 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci int booting; 1158c2ecf20Sopenharmony_ci int reset; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci wait_queue_head_t sync_q; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci struct task_struct *kthread; 1208c2ecf20Sopenharmony_ci u32 data; 1218c2ecf20Sopenharmony_ci u32 data1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci int cmv_ack; 1248c2ecf20Sopenharmony_ci union cmv_dsc cmv_dsc; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci struct work_struct task; 1278c2ecf20Sopenharmony_ci u16 pageno; 1288c2ecf20Sopenharmony_ci u16 ovl; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci const struct firmware *dsp_firm; 1318c2ecf20Sopenharmony_ci struct urb *urb_int; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci void (*dispatch_cmv)(struct uea_softc *, struct intr_pkt *); 1348c2ecf20Sopenharmony_ci void (*schedule_load_page)(struct uea_softc *, struct intr_pkt *); 1358c2ecf20Sopenharmony_ci int (*stat)(struct uea_softc *); 1368c2ecf20Sopenharmony_ci int (*send_cmvs)(struct uea_softc *); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* keep in sync with eaglectl */ 1398c2ecf20Sopenharmony_ci struct uea_stats { 1408c2ecf20Sopenharmony_ci struct { 1418c2ecf20Sopenharmony_ci u32 state; 1428c2ecf20Sopenharmony_ci u32 flags; 1438c2ecf20Sopenharmony_ci u32 mflags; 1448c2ecf20Sopenharmony_ci u32 vidcpe; 1458c2ecf20Sopenharmony_ci u32 vidco; 1468c2ecf20Sopenharmony_ci u32 dsrate; 1478c2ecf20Sopenharmony_ci u32 usrate; 1488c2ecf20Sopenharmony_ci u32 dsunc; 1498c2ecf20Sopenharmony_ci u32 usunc; 1508c2ecf20Sopenharmony_ci u32 dscorr; 1518c2ecf20Sopenharmony_ci u32 uscorr; 1528c2ecf20Sopenharmony_ci u32 txflow; 1538c2ecf20Sopenharmony_ci u32 rxflow; 1548c2ecf20Sopenharmony_ci u32 usattenuation; 1558c2ecf20Sopenharmony_ci u32 dsattenuation; 1568c2ecf20Sopenharmony_ci u32 dsmargin; 1578c2ecf20Sopenharmony_ci u32 usmargin; 1588c2ecf20Sopenharmony_ci u32 firmid; 1598c2ecf20Sopenharmony_ci } phy; 1608c2ecf20Sopenharmony_ci } stats; 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * Elsa IDs 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci#define ELSA_VID 0x05CC 1678c2ecf20Sopenharmony_ci#define ELSA_PID_PSTFIRM 0x3350 1688c2ecf20Sopenharmony_ci#define ELSA_PID_PREFIRM 0x3351 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define ELSA_PID_A_PREFIRM 0x3352 1718c2ecf20Sopenharmony_ci#define ELSA_PID_A_PSTFIRM 0x3353 1728c2ecf20Sopenharmony_ci#define ELSA_PID_B_PREFIRM 0x3362 1738c2ecf20Sopenharmony_ci#define ELSA_PID_B_PSTFIRM 0x3363 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * Devolo IDs : pots if (pid & 0x10) 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci#define DEVOLO_VID 0x1039 1798c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_I_A_PID_PSTFIRM 0x2110 1808c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_I_A_PID_PREFIRM 0x2111 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_I_B_PID_PSTFIRM 0x2100 1838c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_I_B_PID_PREFIRM 0x2101 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_II_A_PID_PSTFIRM 0x2130 1868c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_II_A_PID_PREFIRM 0x2131 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_II_B_PID_PSTFIRM 0x2120 1898c2ecf20Sopenharmony_ci#define DEVOLO_EAGLE_II_B_PID_PREFIRM 0x2121 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * Reference design USB IDs 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci#define ANALOG_VID 0x1110 1958c2ecf20Sopenharmony_ci#define ADI930_PID_PREFIRM 0x9001 1968c2ecf20Sopenharmony_ci#define ADI930_PID_PSTFIRM 0x9000 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci#define EAGLE_I_PID_PREFIRM 0x9010 /* Eagle I */ 1998c2ecf20Sopenharmony_ci#define EAGLE_I_PID_PSTFIRM 0x900F /* Eagle I */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define EAGLE_IIC_PID_PREFIRM 0x9024 /* Eagle IIC */ 2028c2ecf20Sopenharmony_ci#define EAGLE_IIC_PID_PSTFIRM 0x9023 /* Eagle IIC */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#define EAGLE_II_PID_PREFIRM 0x9022 /* Eagle II */ 2058c2ecf20Sopenharmony_ci#define EAGLE_II_PID_PSTFIRM 0x9021 /* Eagle II */ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#define EAGLE_III_PID_PREFIRM 0x9032 /* Eagle III */ 2088c2ecf20Sopenharmony_ci#define EAGLE_III_PID_PSTFIRM 0x9031 /* Eagle III */ 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define EAGLE_IV_PID_PREFIRM 0x9042 /* Eagle IV */ 2118c2ecf20Sopenharmony_ci#define EAGLE_IV_PID_PSTFIRM 0x9041 /* Eagle IV */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * USR USB IDs 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci#define USR_VID 0x0BAF 2178c2ecf20Sopenharmony_ci#define MILLER_A_PID_PREFIRM 0x00F2 2188c2ecf20Sopenharmony_ci#define MILLER_A_PID_PSTFIRM 0x00F1 2198c2ecf20Sopenharmony_ci#define MILLER_B_PID_PREFIRM 0x00FA 2208c2ecf20Sopenharmony_ci#define MILLER_B_PID_PSTFIRM 0x00F9 2218c2ecf20Sopenharmony_ci#define HEINEKEN_A_PID_PREFIRM 0x00F6 2228c2ecf20Sopenharmony_ci#define HEINEKEN_A_PID_PSTFIRM 0x00F5 2238c2ecf20Sopenharmony_ci#define HEINEKEN_B_PID_PREFIRM 0x00F8 2248c2ecf20Sopenharmony_ci#define HEINEKEN_B_PID_PSTFIRM 0x00F7 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci#define PREFIRM 0 2278c2ecf20Sopenharmony_ci#define PSTFIRM (1<<7) 2288c2ecf20Sopenharmony_ci#define AUTO_ANNEX_A (1<<8) 2298c2ecf20Sopenharmony_ci#define AUTO_ANNEX_B (1<<9) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cienum { 2328c2ecf20Sopenharmony_ci ADI930 = 0, 2338c2ecf20Sopenharmony_ci EAGLE_I, 2348c2ecf20Sopenharmony_ci EAGLE_II, 2358c2ecf20Sopenharmony_ci EAGLE_III, 2368c2ecf20Sopenharmony_ci EAGLE_IV 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* macros for both struct usb_device_id and struct uea_softc */ 2408c2ecf20Sopenharmony_ci#define UEA_IS_PREFIRM(x) \ 2418c2ecf20Sopenharmony_ci (!((x)->driver_info & PSTFIRM)) 2428c2ecf20Sopenharmony_ci#define UEA_CHIP_VERSION(x) \ 2438c2ecf20Sopenharmony_ci ((x)->driver_info & 0xf) 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci#define IS_ISDN(x) \ 2468c2ecf20Sopenharmony_ci ((x)->annex & ANNEXB) 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci#define INS_TO_USBDEV(ins) (ins->usb_dev) 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define GET_STATUS(data) \ 2518c2ecf20Sopenharmony_ci ((data >> 8) & 0xf) 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci#define IS_OPERATIONAL(sc) \ 2548c2ecf20Sopenharmony_ci ((UEA_CHIP_VERSION(sc) != EAGLE_IV) ? \ 2558c2ecf20Sopenharmony_ci (GET_STATUS(sc->stats.phy.state) == 2) : \ 2568c2ecf20Sopenharmony_ci (sc->stats.phy.state == 7)) 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * Set of macros to handle unaligned data in the firmware blob. 2608c2ecf20Sopenharmony_ci * The FW_GET_BYTE() macro is provided only for consistency. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define FW_GET_BYTE(p) (*((__u8 *) (p))) 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#define FW_DIR "ueagle-atm/" 2668c2ecf20Sopenharmony_ci#define EAGLE_FIRMWARE FW_DIR "eagle.fw" 2678c2ecf20Sopenharmony_ci#define ADI930_FIRMWARE FW_DIR "adi930.fw" 2688c2ecf20Sopenharmony_ci#define EAGLE_I_FIRMWARE FW_DIR "eagleI.fw" 2698c2ecf20Sopenharmony_ci#define EAGLE_II_FIRMWARE FW_DIR "eagleII.fw" 2708c2ecf20Sopenharmony_ci#define EAGLE_III_FIRMWARE FW_DIR "eagleIII.fw" 2718c2ecf20Sopenharmony_ci#define EAGLE_IV_FIRMWARE FW_DIR "eagleIV.fw" 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#define DSP4I_FIRMWARE FW_DIR "DSP4i.bin" 2748c2ecf20Sopenharmony_ci#define DSP4P_FIRMWARE FW_DIR "DSP4p.bin" 2758c2ecf20Sopenharmony_ci#define DSP9I_FIRMWARE FW_DIR "DSP9i.bin" 2768c2ecf20Sopenharmony_ci#define DSP9P_FIRMWARE FW_DIR "DSP9p.bin" 2778c2ecf20Sopenharmony_ci#define DSPEI_FIRMWARE FW_DIR "DSPei.bin" 2788c2ecf20Sopenharmony_ci#define DSPEP_FIRMWARE FW_DIR "DSPep.bin" 2798c2ecf20Sopenharmony_ci#define FPGA930_FIRMWARE FW_DIR "930-fpga.bin" 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#define CMV4P_FIRMWARE FW_DIR "CMV4p.bin" 2828c2ecf20Sopenharmony_ci#define CMV4PV2_FIRMWARE FW_DIR "CMV4p.bin.v2" 2838c2ecf20Sopenharmony_ci#define CMV4I_FIRMWARE FW_DIR "CMV4i.bin" 2848c2ecf20Sopenharmony_ci#define CMV4IV2_FIRMWARE FW_DIR "CMV4i.bin.v2" 2858c2ecf20Sopenharmony_ci#define CMV9P_FIRMWARE FW_DIR "CMV9p.bin" 2868c2ecf20Sopenharmony_ci#define CMV9PV2_FIRMWARE FW_DIR "CMV9p.bin.v2" 2878c2ecf20Sopenharmony_ci#define CMV9I_FIRMWARE FW_DIR "CMV9i.bin" 2888c2ecf20Sopenharmony_ci#define CMV9IV2_FIRMWARE FW_DIR "CMV9i.bin.v2" 2898c2ecf20Sopenharmony_ci#define CMVEP_FIRMWARE FW_DIR "CMVep.bin" 2908c2ecf20Sopenharmony_ci#define CMVEPV2_FIRMWARE FW_DIR "CMVep.bin.v2" 2918c2ecf20Sopenharmony_ci#define CMVEI_FIRMWARE FW_DIR "CMVei.bin" 2928c2ecf20Sopenharmony_ci#define CMVEIV2_FIRMWARE FW_DIR "CMVei.bin.v2" 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#define UEA_FW_NAME_MAX 30 2958c2ecf20Sopenharmony_ci#define NB_MODEM 4 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#define BULK_TIMEOUT 300 2988c2ecf20Sopenharmony_ci#define CTRL_TIMEOUT 1000 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#define ACK_TIMEOUT msecs_to_jiffies(3000) 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#define UEA_INTR_IFACE_NO 0 3038c2ecf20Sopenharmony_ci#define UEA_US_IFACE_NO 1 3048c2ecf20Sopenharmony_ci#define UEA_DS_IFACE_NO 2 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci#define FASTEST_ISO_INTF 8 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#define UEA_BULK_DATA_PIPE 0x02 3098c2ecf20Sopenharmony_ci#define UEA_IDMA_PIPE 0x04 3108c2ecf20Sopenharmony_ci#define UEA_INTR_PIPE 0x04 3118c2ecf20Sopenharmony_ci#define UEA_ISO_DATA_PIPE 0x08 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci#define UEA_E1_SET_BLOCK 0x0001 3148c2ecf20Sopenharmony_ci#define UEA_E4_SET_BLOCK 0x002c 3158c2ecf20Sopenharmony_ci#define UEA_SET_MODE 0x0003 3168c2ecf20Sopenharmony_ci#define UEA_SET_2183_DATA 0x0004 3178c2ecf20Sopenharmony_ci#define UEA_SET_TIMEOUT 0x0011 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci#define UEA_LOOPBACK_OFF 0x0002 3208c2ecf20Sopenharmony_ci#define UEA_LOOPBACK_ON 0x0003 3218c2ecf20Sopenharmony_ci#define UEA_BOOT_IDMA 0x0006 3228c2ecf20Sopenharmony_ci#define UEA_START_RESET 0x0007 3238c2ecf20Sopenharmony_ci#define UEA_END_RESET 0x0008 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci#define UEA_SWAP_MAILBOX (0x3fcd | 0x4000) 3268c2ecf20Sopenharmony_ci#define UEA_MPTX_START (0x3fce | 0x4000) 3278c2ecf20Sopenharmony_ci#define UEA_MPTX_MAILBOX (0x3fd6 | 0x4000) 3288c2ecf20Sopenharmony_ci#define UEA_MPRX_MAILBOX (0x3fdf | 0x4000) 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* block information in eagle4 dsp firmware */ 3318c2ecf20Sopenharmony_cistruct block_index { 3328c2ecf20Sopenharmony_ci __le32 PageOffset; 3338c2ecf20Sopenharmony_ci __le32 NotLastBlock; 3348c2ecf20Sopenharmony_ci __le32 dummy; 3358c2ecf20Sopenharmony_ci __le32 PageSize; 3368c2ecf20Sopenharmony_ci __le32 PageAddress; 3378c2ecf20Sopenharmony_ci __le16 dummy1; 3388c2ecf20Sopenharmony_ci __le16 PageNumber; 3398c2ecf20Sopenharmony_ci} __packed; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci#define E4_IS_BOOT_PAGE(PageSize) ((le32_to_cpu(PageSize)) & 0x80000000) 3428c2ecf20Sopenharmony_ci#define E4_PAGE_BYTES(PageSize) ((le32_to_cpu(PageSize) & 0x7fffffff) * 4) 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci#define E4_L1_STRING_HEADER 0x10 3458c2ecf20Sopenharmony_ci#define E4_MAX_PAGE_NUMBER 0x58 3468c2ecf20Sopenharmony_ci#define E4_NO_SWAPPAGE_HEADERS 0x31 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* l1_code is eagle4 dsp firmware format */ 3498c2ecf20Sopenharmony_cistruct l1_code { 3508c2ecf20Sopenharmony_ci u8 string_header[E4_L1_STRING_HEADER]; 3518c2ecf20Sopenharmony_ci u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER]; 3528c2ecf20Sopenharmony_ci struct block_index page_header[E4_NO_SWAPPAGE_HEADERS]; 3538c2ecf20Sopenharmony_ci u8 code[]; 3548c2ecf20Sopenharmony_ci} __packed; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/* structures describing a block within a DSP page */ 3578c2ecf20Sopenharmony_cistruct block_info_e1 { 3588c2ecf20Sopenharmony_ci __le16 wHdr; 3598c2ecf20Sopenharmony_ci __le16 wAddress; 3608c2ecf20Sopenharmony_ci __le16 wSize; 3618c2ecf20Sopenharmony_ci __le16 wOvlOffset; 3628c2ecf20Sopenharmony_ci __le16 wOvl; /* overlay */ 3638c2ecf20Sopenharmony_ci __le16 wLast; 3648c2ecf20Sopenharmony_ci} __packed; 3658c2ecf20Sopenharmony_ci#define E1_BLOCK_INFO_SIZE 12 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistruct block_info_e4 { 3688c2ecf20Sopenharmony_ci __be16 wHdr; 3698c2ecf20Sopenharmony_ci __u8 bBootPage; 3708c2ecf20Sopenharmony_ci __u8 bPageNumber; 3718c2ecf20Sopenharmony_ci __be32 dwSize; 3728c2ecf20Sopenharmony_ci __be32 dwAddress; 3738c2ecf20Sopenharmony_ci __be16 wReserved; 3748c2ecf20Sopenharmony_ci} __packed; 3758c2ecf20Sopenharmony_ci#define E4_BLOCK_INFO_SIZE 14 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci#define UEA_BIHDR 0xabcd 3788c2ecf20Sopenharmony_ci#define UEA_RESERVED 0xffff 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* constants describing cmv type */ 3818c2ecf20Sopenharmony_ci#define E1_PREAMBLE 0x535c 3828c2ecf20Sopenharmony_ci#define E1_MODEMTOHOST 0x01 3838c2ecf20Sopenharmony_ci#define E1_HOSTTOMODEM 0x10 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci#define E1_MEMACCESS 0x1 3868c2ecf20Sopenharmony_ci#define E1_ADSLDIRECTIVE 0x7 3878c2ecf20Sopenharmony_ci#define E1_FUNCTION_TYPE(f) ((f) >> 4) 3888c2ecf20Sopenharmony_ci#define E1_FUNCTION_SUBTYPE(f) ((f) & 0x0f) 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci#define E4_MEMACCESS 0 3918c2ecf20Sopenharmony_ci#define E4_ADSLDIRECTIVE 0xf 3928c2ecf20Sopenharmony_ci#define E4_FUNCTION_TYPE(f) ((f) >> 8) 3938c2ecf20Sopenharmony_ci#define E4_FUNCTION_SIZE(f) ((f) & 0x0f) 3948c2ecf20Sopenharmony_ci#define E4_FUNCTION_SUBTYPE(f) (((f) >> 4) & 0x0f) 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* for MEMACCESS */ 3978c2ecf20Sopenharmony_ci#define E1_REQUESTREAD 0x0 3988c2ecf20Sopenharmony_ci#define E1_REQUESTWRITE 0x1 3998c2ecf20Sopenharmony_ci#define E1_REPLYREAD 0x2 4008c2ecf20Sopenharmony_ci#define E1_REPLYWRITE 0x3 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#define E4_REQUESTREAD 0x0 4038c2ecf20Sopenharmony_ci#define E4_REQUESTWRITE 0x4 4048c2ecf20Sopenharmony_ci#define E4_REPLYREAD (E4_REQUESTREAD | 1) 4058c2ecf20Sopenharmony_ci#define E4_REPLYWRITE (E4_REQUESTWRITE | 1) 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* for ADSLDIRECTIVE */ 4088c2ecf20Sopenharmony_ci#define E1_KERNELREADY 0x0 4098c2ecf20Sopenharmony_ci#define E1_MODEMREADY 0x1 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#define E4_KERNELREADY 0x0 4128c2ecf20Sopenharmony_ci#define E4_MODEMREADY 0x1 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci#define E1_MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf)) 4158c2ecf20Sopenharmony_ci#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | \ 4168c2ecf20Sopenharmony_ci ((st) & 0xf) << 4 | ((s) & 0xf)) 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci#define E1_MAKESA(a, b, c, d) \ 4198c2ecf20Sopenharmony_ci (((c) & 0xff) << 24 | \ 4208c2ecf20Sopenharmony_ci ((d) & 0xff) << 16 | \ 4218c2ecf20Sopenharmony_ci ((a) & 0xff) << 8 | \ 4228c2ecf20Sopenharmony_ci ((b) & 0xff)) 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci#define E1_GETSA1(a) ((a >> 8) & 0xff) 4258c2ecf20Sopenharmony_ci#define E1_GETSA2(a) (a & 0xff) 4268c2ecf20Sopenharmony_ci#define E1_GETSA3(a) ((a >> 24) & 0xff) 4278c2ecf20Sopenharmony_ci#define E1_GETSA4(a) ((a >> 16) & 0xff) 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci#define E1_SA_CNTL E1_MAKESA('C', 'N', 'T', 'L') 4308c2ecf20Sopenharmony_ci#define E1_SA_DIAG E1_MAKESA('D', 'I', 'A', 'G') 4318c2ecf20Sopenharmony_ci#define E1_SA_INFO E1_MAKESA('I', 'N', 'F', 'O') 4328c2ecf20Sopenharmony_ci#define E1_SA_OPTN E1_MAKESA('O', 'P', 'T', 'N') 4338c2ecf20Sopenharmony_ci#define E1_SA_RATE E1_MAKESA('R', 'A', 'T', 'E') 4348c2ecf20Sopenharmony_ci#define E1_SA_STAT E1_MAKESA('S', 'T', 'A', 'T') 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci#define E4_SA_CNTL 1 4378c2ecf20Sopenharmony_ci#define E4_SA_STAT 2 4388c2ecf20Sopenharmony_ci#define E4_SA_INFO 3 4398c2ecf20Sopenharmony_ci#define E4_SA_TEST 4 4408c2ecf20Sopenharmony_ci#define E4_SA_OPTN 5 4418c2ecf20Sopenharmony_ci#define E4_SA_RATE 6 4428c2ecf20Sopenharmony_ci#define E4_SA_DIAG 7 4438c2ecf20Sopenharmony_ci#define E4_SA_CNFG 8 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/* structures representing a CMV (Configuration and Management Variable) */ 4468c2ecf20Sopenharmony_cistruct cmv_e1 { 4478c2ecf20Sopenharmony_ci __le16 wPreamble; 4488c2ecf20Sopenharmony_ci __u8 bDirection; 4498c2ecf20Sopenharmony_ci __u8 bFunction; 4508c2ecf20Sopenharmony_ci __le16 wIndex; 4518c2ecf20Sopenharmony_ci __le32 dwSymbolicAddress; 4528c2ecf20Sopenharmony_ci __le16 wOffsetAddress; 4538c2ecf20Sopenharmony_ci __le32 dwData; 4548c2ecf20Sopenharmony_ci} __packed; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistruct cmv_e4 { 4578c2ecf20Sopenharmony_ci __be16 wGroup; 4588c2ecf20Sopenharmony_ci __be16 wFunction; 4598c2ecf20Sopenharmony_ci __be16 wOffset; 4608c2ecf20Sopenharmony_ci __be16 wAddress; 4618c2ecf20Sopenharmony_ci __be32 dwData[6]; 4628c2ecf20Sopenharmony_ci} __packed; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci/* structures representing swap information */ 4658c2ecf20Sopenharmony_cistruct swap_info_e1 { 4668c2ecf20Sopenharmony_ci __u8 bSwapPageNo; 4678c2ecf20Sopenharmony_ci __u8 bOvl; /* overlay */ 4688c2ecf20Sopenharmony_ci} __packed; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistruct swap_info_e4 { 4718c2ecf20Sopenharmony_ci __u8 bSwapPageNo; 4728c2ecf20Sopenharmony_ci} __packed; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* structures representing interrupt data */ 4758c2ecf20Sopenharmony_ci#define e1_bSwapPageNo u.e1.s1.swapinfo.bSwapPageNo 4768c2ecf20Sopenharmony_ci#define e1_bOvl u.e1.s1.swapinfo.bOvl 4778c2ecf20Sopenharmony_ci#define e4_bSwapPageNo u.e4.s1.swapinfo.bSwapPageNo 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci#define INT_LOADSWAPPAGE 0x0001 4808c2ecf20Sopenharmony_ci#define INT_INCOMINGCMV 0x0002 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciunion intr_data_e1 { 4838c2ecf20Sopenharmony_ci struct { 4848c2ecf20Sopenharmony_ci struct swap_info_e1 swapinfo; 4858c2ecf20Sopenharmony_ci __le16 wDataSize; 4868c2ecf20Sopenharmony_ci } __packed s1; 4878c2ecf20Sopenharmony_ci struct { 4888c2ecf20Sopenharmony_ci struct cmv_e1 cmv; 4898c2ecf20Sopenharmony_ci __le16 wDataSize; 4908c2ecf20Sopenharmony_ci } __packed s2; 4918c2ecf20Sopenharmony_ci} __packed; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciunion intr_data_e4 { 4948c2ecf20Sopenharmony_ci struct { 4958c2ecf20Sopenharmony_ci struct swap_info_e4 swapinfo; 4968c2ecf20Sopenharmony_ci __le16 wDataSize; 4978c2ecf20Sopenharmony_ci } __packed s1; 4988c2ecf20Sopenharmony_ci struct { 4998c2ecf20Sopenharmony_ci struct cmv_e4 cmv; 5008c2ecf20Sopenharmony_ci __le16 wDataSize; 5018c2ecf20Sopenharmony_ci } __packed s2; 5028c2ecf20Sopenharmony_ci} __packed; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistruct intr_pkt { 5058c2ecf20Sopenharmony_ci __u8 bType; 5068c2ecf20Sopenharmony_ci __u8 bNotification; 5078c2ecf20Sopenharmony_ci __le16 wValue; 5088c2ecf20Sopenharmony_ci __le16 wIndex; 5098c2ecf20Sopenharmony_ci __le16 wLength; 5108c2ecf20Sopenharmony_ci __le16 wInterrupt; 5118c2ecf20Sopenharmony_ci union { 5128c2ecf20Sopenharmony_ci union intr_data_e1 e1; 5138c2ecf20Sopenharmony_ci union intr_data_e4 e4; 5148c2ecf20Sopenharmony_ci } u; 5158c2ecf20Sopenharmony_ci} __packed; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci#define E1_INTR_PKT_SIZE 28 5188c2ecf20Sopenharmony_ci#define E4_INTR_PKT_SIZE 64 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic struct usb_driver uea_driver; 5218c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(uea_mutex); 5228c2ecf20Sopenharmony_cistatic const char * const chip_name[] = { 5238c2ecf20Sopenharmony_ci "ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int modem_index; 5268c2ecf20Sopenharmony_cistatic unsigned int debug; 5278c2ecf20Sopenharmony_cistatic unsigned int altsetting[NB_MODEM] = { 5288c2ecf20Sopenharmony_ci [0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF}; 5298c2ecf20Sopenharmony_cistatic bool sync_wait[NB_MODEM]; 5308c2ecf20Sopenharmony_cistatic char *cmv_file[NB_MODEM]; 5318c2ecf20Sopenharmony_cistatic int annex[NB_MODEM]; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cimodule_param(debug, uint, 0644); 5348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)"); 5358c2ecf20Sopenharmony_cimodule_param_array(altsetting, uint, NULL, 0644); 5368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(altsetting, "alternate setting for incoming traffic: 0=bulk, " 5378c2ecf20Sopenharmony_ci "1=isoc slowest, ... , 8=isoc fastest (default)"); 5388c2ecf20Sopenharmony_cimodule_param_array(sync_wait, bool, NULL, 0644); 5398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM"); 5408c2ecf20Sopenharmony_cimodule_param_array(cmv_file, charp, NULL, 0644); 5418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cmv_file, 5428c2ecf20Sopenharmony_ci "file name with configuration and management variables"); 5438c2ecf20Sopenharmony_cimodule_param_array(annex, uint, NULL, 0644); 5448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(annex, 5458c2ecf20Sopenharmony_ci "manually set annex a/b (0=auto, 1=annex a, 2=annex b)"); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci#define uea_wait(sc, cond, timeo) \ 5488c2ecf20Sopenharmony_ci({ \ 5498c2ecf20Sopenharmony_ci int _r = wait_event_interruptible_timeout(sc->sync_q, \ 5508c2ecf20Sopenharmony_ci (cond) || kthread_should_stop(), timeo); \ 5518c2ecf20Sopenharmony_ci if (kthread_should_stop()) \ 5528c2ecf20Sopenharmony_ci _r = -ENODEV; \ 5538c2ecf20Sopenharmony_ci _r; \ 5548c2ecf20Sopenharmony_ci}) 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci#define UPDATE_ATM_STAT(type, val) \ 5578c2ecf20Sopenharmony_ci do { \ 5588c2ecf20Sopenharmony_ci if (sc->usbatm->atm_dev) \ 5598c2ecf20Sopenharmony_ci sc->usbatm->atm_dev->type = val; \ 5608c2ecf20Sopenharmony_ci } while (0) 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci#define UPDATE_ATM_SIGNAL(val) \ 5638c2ecf20Sopenharmony_ci do { \ 5648c2ecf20Sopenharmony_ci if (sc->usbatm->atm_dev) \ 5658c2ecf20Sopenharmony_ci atm_dev_signal_change(sc->usbatm->atm_dev, val); \ 5668c2ecf20Sopenharmony_ci } while (0) 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* Firmware loading */ 5708c2ecf20Sopenharmony_ci#define LOAD_INTERNAL 0xA0 5718c2ecf20Sopenharmony_ci#define F8051_USBCS 0x7f92 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* 5748c2ecf20Sopenharmony_ci * uea_send_modem_cmd - Send a command for pre-firmware devices. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_cistatic int uea_send_modem_cmd(struct usb_device *usb, 5778c2ecf20Sopenharmony_ci u16 addr, u16 size, const u8 *buff) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci int ret = -ENOMEM; 5808c2ecf20Sopenharmony_ci u8 *xfer_buff; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci xfer_buff = kmemdup(buff, size, GFP_KERNEL); 5838c2ecf20Sopenharmony_ci if (xfer_buff) { 5848c2ecf20Sopenharmony_ci ret = usb_control_msg(usb, 5858c2ecf20Sopenharmony_ci usb_sndctrlpipe(usb, 0), 5868c2ecf20Sopenharmony_ci LOAD_INTERNAL, 5878c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 5888c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, addr, 0, xfer_buff, 5898c2ecf20Sopenharmony_ci size, CTRL_TIMEOUT); 5908c2ecf20Sopenharmony_ci kfree(xfer_buff); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (ret < 0) 5948c2ecf20Sopenharmony_ci return ret; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return (ret == size) ? 0 : -EIO; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic void uea_upload_pre_firmware(const struct firmware *fw_entry, 6008c2ecf20Sopenharmony_ci void *context) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci struct usb_device *usb = context; 6038c2ecf20Sopenharmony_ci const u8 *pfw; 6048c2ecf20Sopenharmony_ci u8 value; 6058c2ecf20Sopenharmony_ci u32 crc = 0; 6068c2ecf20Sopenharmony_ci int ret, size; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci uea_enters(usb); 6098c2ecf20Sopenharmony_ci if (!fw_entry) { 6108c2ecf20Sopenharmony_ci uea_err(usb, "firmware is not available\n"); 6118c2ecf20Sopenharmony_ci goto err; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci pfw = fw_entry->data; 6158c2ecf20Sopenharmony_ci size = fw_entry->size; 6168c2ecf20Sopenharmony_ci if (size < 4) 6178c2ecf20Sopenharmony_ci goto err_fw_corrupted; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci crc = get_unaligned_le32(pfw); 6208c2ecf20Sopenharmony_ci pfw += 4; 6218c2ecf20Sopenharmony_ci size -= 4; 6228c2ecf20Sopenharmony_ci if (crc32_be(0, pfw, size) != crc) 6238c2ecf20Sopenharmony_ci goto err_fw_corrupted; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* 6268c2ecf20Sopenharmony_ci * Start to upload firmware : send reset 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci value = 1; 6298c2ecf20Sopenharmony_ci ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (ret < 0) { 6328c2ecf20Sopenharmony_ci uea_err(usb, "modem reset failed with error %d\n", ret); 6338c2ecf20Sopenharmony_ci goto err; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci while (size > 3) { 6378c2ecf20Sopenharmony_ci u8 len = FW_GET_BYTE(pfw); 6388c2ecf20Sopenharmony_ci u16 add = get_unaligned_le16(pfw + 1); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci size -= len + 3; 6418c2ecf20Sopenharmony_ci if (size < 0) 6428c2ecf20Sopenharmony_ci goto err_fw_corrupted; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ret = uea_send_modem_cmd(usb, add, len, pfw + 3); 6458c2ecf20Sopenharmony_ci if (ret < 0) { 6468c2ecf20Sopenharmony_ci uea_err(usb, "uploading firmware data failed " 6478c2ecf20Sopenharmony_ci "with error %d\n", ret); 6488c2ecf20Sopenharmony_ci goto err; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci pfw += len + 3; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (size != 0) 6548c2ecf20Sopenharmony_ci goto err_fw_corrupted; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* 6578c2ecf20Sopenharmony_ci * Tell the modem we finish : de-assert reset 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci value = 0; 6608c2ecf20Sopenharmony_ci ret = uea_send_modem_cmd(usb, F8051_USBCS, 1, &value); 6618c2ecf20Sopenharmony_ci if (ret < 0) 6628c2ecf20Sopenharmony_ci uea_err(usb, "modem de-assert failed with error %d\n", ret); 6638c2ecf20Sopenharmony_ci else 6648c2ecf20Sopenharmony_ci uea_info(usb, "firmware uploaded\n"); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci goto err; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cierr_fw_corrupted: 6698c2ecf20Sopenharmony_ci uea_err(usb, "firmware is corrupted\n"); 6708c2ecf20Sopenharmony_cierr: 6718c2ecf20Sopenharmony_ci release_firmware(fw_entry); 6728c2ecf20Sopenharmony_ci uea_leaves(usb); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/* 6768c2ecf20Sopenharmony_ci * uea_load_firmware - Load usb firmware for pre-firmware devices. 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_cistatic int uea_load_firmware(struct usb_device *usb, unsigned int ver) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci int ret; 6818c2ecf20Sopenharmony_ci char *fw_name = EAGLE_FIRMWARE; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci uea_enters(usb); 6848c2ecf20Sopenharmony_ci uea_info(usb, "pre-firmware device, uploading firmware\n"); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci switch (ver) { 6878c2ecf20Sopenharmony_ci case ADI930: 6888c2ecf20Sopenharmony_ci fw_name = ADI930_FIRMWARE; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci case EAGLE_I: 6918c2ecf20Sopenharmony_ci fw_name = EAGLE_I_FIRMWARE; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case EAGLE_II: 6948c2ecf20Sopenharmony_ci fw_name = EAGLE_II_FIRMWARE; 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci case EAGLE_III: 6978c2ecf20Sopenharmony_ci fw_name = EAGLE_III_FIRMWARE; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci case EAGLE_IV: 7008c2ecf20Sopenharmony_ci fw_name = EAGLE_IV_FIRMWARE; 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, 7058c2ecf20Sopenharmony_ci GFP_KERNEL, usb, 7068c2ecf20Sopenharmony_ci uea_upload_pre_firmware); 7078c2ecf20Sopenharmony_ci if (ret) 7088c2ecf20Sopenharmony_ci uea_err(usb, "firmware %s is not available\n", fw_name); 7098c2ecf20Sopenharmony_ci else 7108c2ecf20Sopenharmony_ci uea_info(usb, "loading firmware %s\n", fw_name); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci uea_leaves(usb); 7138c2ecf20Sopenharmony_ci return ret; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci/* modem management : dsp firmware, send/read CMV, monitoring statistic 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci/* 7208c2ecf20Sopenharmony_ci * Make sure that the DSP code provided is safe to use. 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_cistatic int check_dsp_e1(const u8 *dsp, unsigned int len) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci u8 pagecount, blockcount; 7258c2ecf20Sopenharmony_ci u16 blocksize; 7268c2ecf20Sopenharmony_ci u32 pageoffset; 7278c2ecf20Sopenharmony_ci unsigned int i, j, p, pp; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci pagecount = FW_GET_BYTE(dsp); 7308c2ecf20Sopenharmony_ci p = 1; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* enough space for page offsets? */ 7338c2ecf20Sopenharmony_ci if (p + 4 * pagecount > len) 7348c2ecf20Sopenharmony_ci return 1; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci for (i = 0; i < pagecount; i++) { 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci pageoffset = get_unaligned_le32(dsp + p); 7398c2ecf20Sopenharmony_ci p += 4; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (pageoffset == 0) 7428c2ecf20Sopenharmony_ci continue; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* enough space for blockcount? */ 7458c2ecf20Sopenharmony_ci if (pageoffset >= len) 7468c2ecf20Sopenharmony_ci return 1; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci pp = pageoffset; 7498c2ecf20Sopenharmony_ci blockcount = FW_GET_BYTE(dsp + pp); 7508c2ecf20Sopenharmony_ci pp += 1; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci for (j = 0; j < blockcount; j++) { 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* enough space for block header? */ 7558c2ecf20Sopenharmony_ci if (pp + 4 > len) 7568c2ecf20Sopenharmony_ci return 1; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci pp += 2; /* skip blockaddr */ 7598c2ecf20Sopenharmony_ci blocksize = get_unaligned_le16(dsp + pp); 7608c2ecf20Sopenharmony_ci pp += 2; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* enough space for block data? */ 7638c2ecf20Sopenharmony_ci if (pp + blocksize > len) 7648c2ecf20Sopenharmony_ci return 1; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci pp += blocksize; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic int check_dsp_e4(const u8 *dsp, int len) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci int i; 7768c2ecf20Sopenharmony_ci struct l1_code *p = (struct l1_code *) dsp; 7778c2ecf20Sopenharmony_ci unsigned int sum = p->code - dsp; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (len < sum) 7808c2ecf20Sopenharmony_ci return 1; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (strcmp("STRATIPHY ANEXA", p->string_header) != 0 && 7838c2ecf20Sopenharmony_ci strcmp("STRATIPHY ANEXB", p->string_header) != 0) 7848c2ecf20Sopenharmony_ci return 1; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci for (i = 0; i < E4_MAX_PAGE_NUMBER; i++) { 7878c2ecf20Sopenharmony_ci struct block_index *blockidx; 7888c2ecf20Sopenharmony_ci u8 blockno = p->page_number_to_block_index[i]; 7898c2ecf20Sopenharmony_ci if (blockno >= E4_NO_SWAPPAGE_HEADERS) 7908c2ecf20Sopenharmony_ci continue; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci do { 7938c2ecf20Sopenharmony_ci u64 l; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (blockno >= E4_NO_SWAPPAGE_HEADERS) 7968c2ecf20Sopenharmony_ci return 1; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci blockidx = &p->page_header[blockno++]; 7998c2ecf20Sopenharmony_ci if ((u8 *)(blockidx + 1) - dsp >= len) 8008c2ecf20Sopenharmony_ci return 1; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (le16_to_cpu(blockidx->PageNumber) != i) 8038c2ecf20Sopenharmony_ci return 1; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci l = E4_PAGE_BYTES(blockidx->PageSize); 8068c2ecf20Sopenharmony_ci sum += l; 8078c2ecf20Sopenharmony_ci l += le32_to_cpu(blockidx->PageOffset); 8088c2ecf20Sopenharmony_ci if (l > len) 8098c2ecf20Sopenharmony_ci return 1; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* zero is zero regardless endianes */ 8128c2ecf20Sopenharmony_ci } while (blockidx->NotLastBlock); 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return (sum == len) ? 0 : 1; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci/* 8198c2ecf20Sopenharmony_ci * send data to the idma pipe 8208c2ecf20Sopenharmony_ci * */ 8218c2ecf20Sopenharmony_cistatic int uea_idma_write(struct uea_softc *sc, const void *data, u32 size) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci int ret = -ENOMEM; 8248c2ecf20Sopenharmony_ci u8 *xfer_buff; 8258c2ecf20Sopenharmony_ci int bytes_read; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci xfer_buff = kmemdup(data, size, GFP_KERNEL); 8288c2ecf20Sopenharmony_ci if (!xfer_buff) { 8298c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n"); 8308c2ecf20Sopenharmony_ci return ret; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci ret = usb_bulk_msg(sc->usb_dev, 8348c2ecf20Sopenharmony_ci usb_sndbulkpipe(sc->usb_dev, UEA_IDMA_PIPE), 8358c2ecf20Sopenharmony_ci xfer_buff, size, &bytes_read, BULK_TIMEOUT); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci kfree(xfer_buff); 8388c2ecf20Sopenharmony_ci if (ret < 0) 8398c2ecf20Sopenharmony_ci return ret; 8408c2ecf20Sopenharmony_ci if (size != bytes_read) { 8418c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "size != bytes_read %d %d\n", size, 8428c2ecf20Sopenharmony_ci bytes_read); 8438c2ecf20Sopenharmony_ci return -EIO; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return 0; 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int request_dsp(struct uea_softc *sc) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci int ret; 8528c2ecf20Sopenharmony_ci char *dsp_name; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { 8558c2ecf20Sopenharmony_ci if (IS_ISDN(sc)) 8568c2ecf20Sopenharmony_ci dsp_name = DSP4I_FIRMWARE; 8578c2ecf20Sopenharmony_ci else 8588c2ecf20Sopenharmony_ci dsp_name = DSP4P_FIRMWARE; 8598c2ecf20Sopenharmony_ci } else if (UEA_CHIP_VERSION(sc) == ADI930) { 8608c2ecf20Sopenharmony_ci if (IS_ISDN(sc)) 8618c2ecf20Sopenharmony_ci dsp_name = DSP9I_FIRMWARE; 8628c2ecf20Sopenharmony_ci else 8638c2ecf20Sopenharmony_ci dsp_name = DSP9P_FIRMWARE; 8648c2ecf20Sopenharmony_ci } else { 8658c2ecf20Sopenharmony_ci if (IS_ISDN(sc)) 8668c2ecf20Sopenharmony_ci dsp_name = DSPEI_FIRMWARE; 8678c2ecf20Sopenharmony_ci else 8688c2ecf20Sopenharmony_ci dsp_name = DSPEP_FIRMWARE; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev); 8728c2ecf20Sopenharmony_ci if (ret < 0) { 8738c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 8748c2ecf20Sopenharmony_ci "requesting firmware %s failed with error %d\n", 8758c2ecf20Sopenharmony_ci dsp_name, ret); 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == EAGLE_IV) 8808c2ecf20Sopenharmony_ci ret = check_dsp_e4(sc->dsp_firm->data, sc->dsp_firm->size); 8818c2ecf20Sopenharmony_ci else 8828c2ecf20Sopenharmony_ci ret = check_dsp_e1(sc->dsp_firm->data, sc->dsp_firm->size); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (ret) { 8858c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", 8868c2ecf20Sopenharmony_ci dsp_name); 8878c2ecf20Sopenharmony_ci release_firmware(sc->dsp_firm); 8888c2ecf20Sopenharmony_ci sc->dsp_firm = NULL; 8898c2ecf20Sopenharmony_ci return -EILSEQ; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return 0; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci/* 8968c2ecf20Sopenharmony_ci * The uea_load_page() function must be called within a process context 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_cistatic void uea_load_page_e1(struct work_struct *work) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct uea_softc *sc = container_of(work, struct uea_softc, task); 9018c2ecf20Sopenharmony_ci u16 pageno = sc->pageno; 9028c2ecf20Sopenharmony_ci u16 ovl = sc->ovl; 9038c2ecf20Sopenharmony_ci struct block_info_e1 bi; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci const u8 *p; 9068c2ecf20Sopenharmony_ci u8 pagecount, blockcount; 9078c2ecf20Sopenharmony_ci u16 blockaddr, blocksize; 9088c2ecf20Sopenharmony_ci u32 pageoffset; 9098c2ecf20Sopenharmony_ci int i; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* reload firmware when reboot start and it's loaded already */ 9128c2ecf20Sopenharmony_ci if (ovl == 0 && pageno == 0) { 9138c2ecf20Sopenharmony_ci release_firmware(sc->dsp_firm); 9148c2ecf20Sopenharmony_ci sc->dsp_firm = NULL; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (sc->dsp_firm == NULL && request_dsp(sc) < 0) 9188c2ecf20Sopenharmony_ci return; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci p = sc->dsp_firm->data; 9218c2ecf20Sopenharmony_ci pagecount = FW_GET_BYTE(p); 9228c2ecf20Sopenharmony_ci p += 1; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (pageno >= pagecount) 9258c2ecf20Sopenharmony_ci goto bad1; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci p += 4 * pageno; 9288c2ecf20Sopenharmony_ci pageoffset = get_unaligned_le32(p); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (pageoffset == 0) 9318c2ecf20Sopenharmony_ci goto bad1; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci p = sc->dsp_firm->data + pageoffset; 9348c2ecf20Sopenharmony_ci blockcount = FW_GET_BYTE(p); 9358c2ecf20Sopenharmony_ci p += 1; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), 9388c2ecf20Sopenharmony_ci "sending %u blocks for DSP page %u\n", blockcount, pageno); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci bi.wHdr = cpu_to_le16(UEA_BIHDR); 9418c2ecf20Sopenharmony_ci bi.wOvl = cpu_to_le16(ovl); 9428c2ecf20Sopenharmony_ci bi.wOvlOffset = cpu_to_le16(ovl | 0x8000); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci for (i = 0; i < blockcount; i++) { 9458c2ecf20Sopenharmony_ci blockaddr = get_unaligned_le16(p); 9468c2ecf20Sopenharmony_ci p += 2; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci blocksize = get_unaligned_le16(p); 9498c2ecf20Sopenharmony_ci p += 2; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci bi.wSize = cpu_to_le16(blocksize); 9528c2ecf20Sopenharmony_ci bi.wAddress = cpu_to_le16(blockaddr); 9538c2ecf20Sopenharmony_ci bi.wLast = cpu_to_le16((i == blockcount - 1) ? 1 : 0); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* send block info through the IDMA pipe */ 9568c2ecf20Sopenharmony_ci if (uea_idma_write(sc, &bi, E1_BLOCK_INFO_SIZE)) 9578c2ecf20Sopenharmony_ci goto bad2; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* send block data through the IDMA pipe */ 9608c2ecf20Sopenharmony_ci if (uea_idma_write(sc, p, blocksize)) 9618c2ecf20Sopenharmony_ci goto bad2; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci p += blocksize; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cibad2: 9698c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", i); 9708c2ecf20Sopenharmony_ci return; 9718c2ecf20Sopenharmony_cibad1: 9728c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno); 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic void __uea_load_page_e4(struct uea_softc *sc, u8 pageno, int boot) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct block_info_e4 bi; 9788c2ecf20Sopenharmony_ci struct block_index *blockidx; 9798c2ecf20Sopenharmony_ci struct l1_code *p = (struct l1_code *) sc->dsp_firm->data; 9808c2ecf20Sopenharmony_ci u8 blockno = p->page_number_to_block_index[pageno]; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci bi.wHdr = cpu_to_be16(UEA_BIHDR); 9838c2ecf20Sopenharmony_ci bi.bBootPage = boot; 9848c2ecf20Sopenharmony_ci bi.bPageNumber = pageno; 9858c2ecf20Sopenharmony_ci bi.wReserved = cpu_to_be16(UEA_RESERVED); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci do { 9888c2ecf20Sopenharmony_ci const u8 *blockoffset; 9898c2ecf20Sopenharmony_ci unsigned int blocksize; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci blockidx = &p->page_header[blockno]; 9928c2ecf20Sopenharmony_ci blocksize = E4_PAGE_BYTES(blockidx->PageSize); 9938c2ecf20Sopenharmony_ci blockoffset = sc->dsp_firm->data + le32_to_cpu( 9948c2ecf20Sopenharmony_ci blockidx->PageOffset); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci bi.dwSize = cpu_to_be32(blocksize); 9978c2ecf20Sopenharmony_ci bi.dwAddress = cpu_to_be32(le32_to_cpu(blockidx->PageAddress)); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), 10008c2ecf20Sopenharmony_ci "sending block %u for DSP page " 10018c2ecf20Sopenharmony_ci "%u size %u address %x\n", 10028c2ecf20Sopenharmony_ci blockno, pageno, blocksize, 10038c2ecf20Sopenharmony_ci le32_to_cpu(blockidx->PageAddress)); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* send block info through the IDMA pipe */ 10068c2ecf20Sopenharmony_ci if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE)) 10078c2ecf20Sopenharmony_ci goto bad; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* send block data through the IDMA pipe */ 10108c2ecf20Sopenharmony_ci if (uea_idma_write(sc, blockoffset, blocksize)) 10118c2ecf20Sopenharmony_ci goto bad; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci blockno++; 10148c2ecf20Sopenharmony_ci } while (blockidx->NotLastBlock); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cibad: 10198c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", blockno); 10208c2ecf20Sopenharmony_ci return; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic void uea_load_page_e4(struct work_struct *work) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci struct uea_softc *sc = container_of(work, struct uea_softc, task); 10268c2ecf20Sopenharmony_ci u8 pageno = sc->pageno; 10278c2ecf20Sopenharmony_ci int i; 10288c2ecf20Sopenharmony_ci struct block_info_e4 bi; 10298c2ecf20Sopenharmony_ci struct l1_code *p; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "sending DSP page %u\n", pageno); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* reload firmware when reboot start and it's loaded already */ 10348c2ecf20Sopenharmony_ci if (pageno == 0) { 10358c2ecf20Sopenharmony_ci release_firmware(sc->dsp_firm); 10368c2ecf20Sopenharmony_ci sc->dsp_firm = NULL; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (sc->dsp_firm == NULL && request_dsp(sc) < 0) 10408c2ecf20Sopenharmony_ci return; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci p = (struct l1_code *) sc->dsp_firm->data; 10438c2ecf20Sopenharmony_ci if (pageno >= le16_to_cpu(p->page_header[0].PageNumber)) { 10448c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "invalid DSP " 10458c2ecf20Sopenharmony_ci "page %u requested\n", pageno); 10468c2ecf20Sopenharmony_ci return; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (pageno != 0) { 10508c2ecf20Sopenharmony_ci __uea_load_page_e4(sc, pageno, 0); 10518c2ecf20Sopenharmony_ci return; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), 10558c2ecf20Sopenharmony_ci "sending Main DSP page %u\n", p->page_header[0].PageNumber); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci for (i = 0; i < le16_to_cpu(p->page_header[0].PageNumber); i++) { 10588c2ecf20Sopenharmony_ci if (E4_IS_BOOT_PAGE(p->page_header[i].PageSize)) 10598c2ecf20Sopenharmony_ci __uea_load_page_e4(sc, i, 1); 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc) , "sending start bi\n"); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci bi.wHdr = cpu_to_be16(UEA_BIHDR); 10658c2ecf20Sopenharmony_ci bi.bBootPage = 0; 10668c2ecf20Sopenharmony_ci bi.bPageNumber = 0xff; 10678c2ecf20Sopenharmony_ci bi.wReserved = cpu_to_be16(UEA_RESERVED); 10688c2ecf20Sopenharmony_ci bi.dwSize = cpu_to_be32(E4_PAGE_BYTES(p->page_header[0].PageSize)); 10698c2ecf20Sopenharmony_ci bi.dwAddress = cpu_to_be32(le32_to_cpu(p->page_header[0].PageAddress)); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* send block info through the IDMA pipe */ 10728c2ecf20Sopenharmony_ci if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE)) 10738c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "sending DSP start bi failed\n"); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic inline void wake_up_cmv_ack(struct uea_softc *sc) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci BUG_ON(sc->cmv_ack); 10798c2ecf20Sopenharmony_ci sc->cmv_ack = 1; 10808c2ecf20Sopenharmony_ci wake_up(&sc->sync_q); 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic inline int wait_cmv_ack(struct uea_softc *sc) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci int ret = uea_wait(sc, sc->cmv_ack , ACK_TIMEOUT); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci sc->cmv_ack = 0; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "wait_event_timeout : %d ms\n", 10908c2ecf20Sopenharmony_ci jiffies_to_msecs(ret)); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (ret < 0) 10938c2ecf20Sopenharmony_ci return ret; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci return (ret == 0) ? -ETIMEDOUT : 0; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic int uea_request(struct uea_softc *sc, 11018c2ecf20Sopenharmony_ci u16 value, u16 index, u16 size, const void *data) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci u8 *xfer_buff; 11048c2ecf20Sopenharmony_ci int ret = -ENOMEM; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci xfer_buff = kmemdup(data, size, GFP_KERNEL); 11078c2ecf20Sopenharmony_ci if (!xfer_buff) { 11088c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n"); 11098c2ecf20Sopenharmony_ci return ret; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci ret = usb_control_msg(sc->usb_dev, usb_sndctrlpipe(sc->usb_dev, 0), 11138c2ecf20Sopenharmony_ci UCDC_SEND_ENCAPSULATED_COMMAND, 11148c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 11158c2ecf20Sopenharmony_ci value, index, xfer_buff, size, CTRL_TIMEOUT); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci kfree(xfer_buff); 11188c2ecf20Sopenharmony_ci if (ret < 0) { 11198c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "usb_control_msg error %d\n", ret); 11208c2ecf20Sopenharmony_ci return ret; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (ret != size) { 11248c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 11258c2ecf20Sopenharmony_ci "usb_control_msg send only %d bytes (instead of %d)\n", 11268c2ecf20Sopenharmony_ci ret, size); 11278c2ecf20Sopenharmony_ci return -EIO; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic int uea_cmv_e1(struct uea_softc *sc, 11348c2ecf20Sopenharmony_ci u8 function, u32 address, u16 offset, u32 data) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct cmv_e1 cmv; 11378c2ecf20Sopenharmony_ci int ret; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 11408c2ecf20Sopenharmony_ci uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, " 11418c2ecf20Sopenharmony_ci "offset : 0x%04x, data : 0x%08x\n", 11428c2ecf20Sopenharmony_ci E1_FUNCTION_TYPE(function), 11438c2ecf20Sopenharmony_ci E1_FUNCTION_SUBTYPE(function), 11448c2ecf20Sopenharmony_ci E1_GETSA1(address), E1_GETSA2(address), 11458c2ecf20Sopenharmony_ci E1_GETSA3(address), 11468c2ecf20Sopenharmony_ci E1_GETSA4(address), offset, data); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* we send a request, but we expect a reply */ 11498c2ecf20Sopenharmony_ci sc->cmv_dsc.e1.function = function | 0x2; 11508c2ecf20Sopenharmony_ci sc->cmv_dsc.e1.idx++; 11518c2ecf20Sopenharmony_ci sc->cmv_dsc.e1.address = address; 11528c2ecf20Sopenharmony_ci sc->cmv_dsc.e1.offset = offset; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci cmv.wPreamble = cpu_to_le16(E1_PREAMBLE); 11558c2ecf20Sopenharmony_ci cmv.bDirection = E1_HOSTTOMODEM; 11568c2ecf20Sopenharmony_ci cmv.bFunction = function; 11578c2ecf20Sopenharmony_ci cmv.wIndex = cpu_to_le16(sc->cmv_dsc.e1.idx); 11588c2ecf20Sopenharmony_ci put_unaligned_le32(address, &cmv.dwSymbolicAddress); 11598c2ecf20Sopenharmony_ci cmv.wOffsetAddress = cpu_to_le16(offset); 11608c2ecf20Sopenharmony_ci put_unaligned_le32(data >> 16 | data << 16, &cmv.dwData); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START, 11638c2ecf20Sopenharmony_ci sizeof(cmv), &cmv); 11648c2ecf20Sopenharmony_ci if (ret < 0) 11658c2ecf20Sopenharmony_ci return ret; 11668c2ecf20Sopenharmony_ci ret = wait_cmv_ack(sc); 11678c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 11688c2ecf20Sopenharmony_ci return ret; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic int uea_cmv_e4(struct uea_softc *sc, 11728c2ecf20Sopenharmony_ci u16 function, u16 group, u16 address, u16 offset, u32 data) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci struct cmv_e4 cmv; 11758c2ecf20Sopenharmony_ci int ret; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 11788c2ecf20Sopenharmony_ci memset(&cmv, 0, sizeof(cmv)); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, " 11818c2ecf20Sopenharmony_ci "Address : 0x%04x, offset : 0x%04x, data : 0x%08x\n", 11828c2ecf20Sopenharmony_ci E4_FUNCTION_TYPE(function), E4_FUNCTION_SUBTYPE(function), 11838c2ecf20Sopenharmony_ci group, address, offset, data); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* we send a request, but we expect a reply */ 11868c2ecf20Sopenharmony_ci sc->cmv_dsc.e4.function = function | (0x1 << 4); 11878c2ecf20Sopenharmony_ci sc->cmv_dsc.e4.offset = offset; 11888c2ecf20Sopenharmony_ci sc->cmv_dsc.e4.address = address; 11898c2ecf20Sopenharmony_ci sc->cmv_dsc.e4.group = group; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci cmv.wFunction = cpu_to_be16(function); 11928c2ecf20Sopenharmony_ci cmv.wGroup = cpu_to_be16(group); 11938c2ecf20Sopenharmony_ci cmv.wAddress = cpu_to_be16(address); 11948c2ecf20Sopenharmony_ci cmv.wOffset = cpu_to_be16(offset); 11958c2ecf20Sopenharmony_ci cmv.dwData[0] = cpu_to_be32(data); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, 11988c2ecf20Sopenharmony_ci sizeof(cmv), &cmv); 11998c2ecf20Sopenharmony_ci if (ret < 0) 12008c2ecf20Sopenharmony_ci return ret; 12018c2ecf20Sopenharmony_ci ret = wait_cmv_ack(sc); 12028c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 12038c2ecf20Sopenharmony_ci return ret; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic inline int uea_read_cmv_e1(struct uea_softc *sc, 12078c2ecf20Sopenharmony_ci u32 address, u16 offset, u32 *data) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTREAD), 12108c2ecf20Sopenharmony_ci address, offset, 0); 12118c2ecf20Sopenharmony_ci if (ret < 0) 12128c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 12138c2ecf20Sopenharmony_ci "reading cmv failed with error %d\n", ret); 12148c2ecf20Sopenharmony_ci else 12158c2ecf20Sopenharmony_ci *data = sc->data; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci return ret; 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_cistatic inline int uea_read_cmv_e4(struct uea_softc *sc, 12218c2ecf20Sopenharmony_ci u8 size, u16 group, u16 address, u16 offset, u32 *data) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, 12248c2ecf20Sopenharmony_ci E4_REQUESTREAD, size), 12258c2ecf20Sopenharmony_ci group, address, offset, 0); 12268c2ecf20Sopenharmony_ci if (ret < 0) 12278c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 12288c2ecf20Sopenharmony_ci "reading cmv failed with error %d\n", ret); 12298c2ecf20Sopenharmony_ci else { 12308c2ecf20Sopenharmony_ci *data = sc->data; 12318c2ecf20Sopenharmony_ci /* size is in 16-bit word quantities */ 12328c2ecf20Sopenharmony_ci if (size > 2) 12338c2ecf20Sopenharmony_ci *(data + 1) = sc->data1; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci return ret; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic inline int uea_write_cmv_e1(struct uea_softc *sc, 12398c2ecf20Sopenharmony_ci u32 address, u16 offset, u32 data) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTWRITE), 12428c2ecf20Sopenharmony_ci address, offset, data); 12438c2ecf20Sopenharmony_ci if (ret < 0) 12448c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 12458c2ecf20Sopenharmony_ci "writing cmv failed with error %d\n", ret); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci return ret; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic inline int uea_write_cmv_e4(struct uea_softc *sc, 12518c2ecf20Sopenharmony_ci u8 size, u16 group, u16 address, u16 offset, u32 data) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, 12548c2ecf20Sopenharmony_ci E4_REQUESTWRITE, size), 12558c2ecf20Sopenharmony_ci group, address, offset, data); 12568c2ecf20Sopenharmony_ci if (ret < 0) 12578c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 12588c2ecf20Sopenharmony_ci "writing cmv failed with error %d\n", ret); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return ret; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci int ret; 12668c2ecf20Sopenharmony_ci u16 timeout; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci /* in bulk mode the modem have problem with high rate 12698c2ecf20Sopenharmony_ci * changing internal timing could improve things, but the 12708c2ecf20Sopenharmony_ci * value is mysterious. 12718c2ecf20Sopenharmony_ci * ADI930 don't support it (-EPIPE error). 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == ADI930 || 12758c2ecf20Sopenharmony_ci altsetting[sc->modem_index] > 0 || 12768c2ecf20Sopenharmony_ci sc->stats.phy.dsrate == dsrate) 12778c2ecf20Sopenharmony_ci return; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* Original timming (1Mbit/s) from ADI (used in windows driver) */ 12808c2ecf20Sopenharmony_ci timeout = (dsrate <= 1024*1024) ? 0 : 1; 12818c2ecf20Sopenharmony_ci ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL); 12828c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n", 12838c2ecf20Sopenharmony_ci timeout, ret < 0 ? " failed" : ""); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci/* 12888c2ecf20Sopenharmony_ci * Monitor the modem and update the stat 12898c2ecf20Sopenharmony_ci * return 0 if everything is ok 12908c2ecf20Sopenharmony_ci * return < 0 if an error occurs (-EAGAIN reboot needed) 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_cistatic int uea_stat_e1(struct uea_softc *sc) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci u32 data; 12958c2ecf20Sopenharmony_ci int ret; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 12988c2ecf20Sopenharmony_ci data = sc->stats.phy.state; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state); 13018c2ecf20Sopenharmony_ci if (ret < 0) 13028c2ecf20Sopenharmony_ci return ret; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci switch (GET_STATUS(sc->stats.phy.state)) { 13058c2ecf20Sopenharmony_ci case 0: /* not yet synchronized */ 13068c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), 13078c2ecf20Sopenharmony_ci "modem not yet synchronized\n"); 13088c2ecf20Sopenharmony_ci return 0; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci case 1: /* initialization */ 13118c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n"); 13128c2ecf20Sopenharmony_ci return 0; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci case 2: /* operational */ 13158c2ecf20Sopenharmony_ci uea_vdbg(INS_TO_USBDEV(sc), "modem operational\n"); 13168c2ecf20Sopenharmony_ci break; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci case 3: /* fail ... */ 13198c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem synchronization failed" 13208c2ecf20Sopenharmony_ci " (may be try other cmv/dsp)\n"); 13218c2ecf20Sopenharmony_ci return -EAGAIN; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci case 4 ... 6: /* test state */ 13248c2ecf20Sopenharmony_ci uea_warn(INS_TO_USBDEV(sc), 13258c2ecf20Sopenharmony_ci "modem in test mode - not supported\n"); 13268c2ecf20Sopenharmony_ci return -EAGAIN; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci case 7: /* fast-retain ... */ 13298c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem in fast-retain mode\n"); 13308c2ecf20Sopenharmony_ci return 0; 13318c2ecf20Sopenharmony_ci default: 13328c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "modem invalid SW mode %d\n", 13338c2ecf20Sopenharmony_ci GET_STATUS(sc->stats.phy.state)); 13348c2ecf20Sopenharmony_ci return -EAGAIN; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (GET_STATUS(data) != 2) { 13388c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL); 13398c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem operational\n"); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* release the dsp firmware as it is not needed until 13428c2ecf20Sopenharmony_ci * the next failure 13438c2ecf20Sopenharmony_ci */ 13448c2ecf20Sopenharmony_ci release_firmware(sc->dsp_firm); 13458c2ecf20Sopenharmony_ci sc->dsp_firm = NULL; 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* always update it as atm layer could not be init when we switch to 13498c2ecf20Sopenharmony_ci * operational state 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_ci UPDATE_ATM_SIGNAL(ATM_PHY_SIG_FOUND); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* wake up processes waiting for synchronization */ 13548c2ecf20Sopenharmony_ci wake_up(&sc->sync_q); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 2, &sc->stats.phy.flags); 13578c2ecf20Sopenharmony_ci if (ret < 0) 13588c2ecf20Sopenharmony_ci return ret; 13598c2ecf20Sopenharmony_ci sc->stats.phy.mflags |= sc->stats.phy.flags; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci /* in case of a flags ( for example delineation LOSS (& 0x10)), 13628c2ecf20Sopenharmony_ci * we check the status again in order to detect the failure earlier 13638c2ecf20Sopenharmony_ci */ 13648c2ecf20Sopenharmony_ci if (sc->stats.phy.flags) { 13658c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n", 13668c2ecf20Sopenharmony_ci sc->stats.phy.flags); 13678c2ecf20Sopenharmony_ci return 0; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_RATE, 0, &data); 13718c2ecf20Sopenharmony_ci if (ret < 0) 13728c2ecf20Sopenharmony_ci return ret; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci uea_set_bulk_timeout(sc, (data >> 16) * 32); 13758c2ecf20Sopenharmony_ci sc->stats.phy.dsrate = (data >> 16) * 32; 13768c2ecf20Sopenharmony_ci sc->stats.phy.usrate = (data & 0xffff) * 32; 13778c2ecf20Sopenharmony_ci UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 23, &data); 13808c2ecf20Sopenharmony_ci if (ret < 0) 13818c2ecf20Sopenharmony_ci return ret; 13828c2ecf20Sopenharmony_ci sc->stats.phy.dsattenuation = (data & 0xff) / 2; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 47, &data); 13858c2ecf20Sopenharmony_ci if (ret < 0) 13868c2ecf20Sopenharmony_ci return ret; 13878c2ecf20Sopenharmony_ci sc->stats.phy.usattenuation = (data & 0xff) / 2; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 25, &sc->stats.phy.dsmargin); 13908c2ecf20Sopenharmony_ci if (ret < 0) 13918c2ecf20Sopenharmony_ci return ret; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 49, &sc->stats.phy.usmargin); 13948c2ecf20Sopenharmony_ci if (ret < 0) 13958c2ecf20Sopenharmony_ci return ret; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 51, &sc->stats.phy.rxflow); 13988c2ecf20Sopenharmony_ci if (ret < 0) 13998c2ecf20Sopenharmony_ci return ret; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 52, &sc->stats.phy.txflow); 14028c2ecf20Sopenharmony_ci if (ret < 0) 14038c2ecf20Sopenharmony_ci return ret; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 54, &sc->stats.phy.dsunc); 14068c2ecf20Sopenharmony_ci if (ret < 0) 14078c2ecf20Sopenharmony_ci return ret; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci /* only for atu-c */ 14108c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 58, &sc->stats.phy.usunc); 14118c2ecf20Sopenharmony_ci if (ret < 0) 14128c2ecf20Sopenharmony_ci return ret; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 53, &sc->stats.phy.dscorr); 14158c2ecf20Sopenharmony_ci if (ret < 0) 14168c2ecf20Sopenharmony_ci return ret; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* only for atu-c */ 14198c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 57, &sc->stats.phy.uscorr); 14208c2ecf20Sopenharmony_ci if (ret < 0) 14218c2ecf20Sopenharmony_ci return ret; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_INFO, 8, &sc->stats.phy.vidco); 14248c2ecf20Sopenharmony_ci if (ret < 0) 14258c2ecf20Sopenharmony_ci return ret; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_INFO, 13, &sc->stats.phy.vidcpe); 14288c2ecf20Sopenharmony_ci if (ret < 0) 14298c2ecf20Sopenharmony_ci return ret; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci return 0; 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_cistatic int uea_stat_e4(struct uea_softc *sc) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci u32 data; 14378c2ecf20Sopenharmony_ci u32 tmp_arr[2]; 14388c2ecf20Sopenharmony_ci int ret; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 14418c2ecf20Sopenharmony_ci data = sc->stats.phy.state; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci /* XXX only need to be done before operationnal... */ 14448c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 1, E4_SA_STAT, 0, 0, &sc->stats.phy.state); 14458c2ecf20Sopenharmony_ci if (ret < 0) 14468c2ecf20Sopenharmony_ci return ret; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci switch (sc->stats.phy.state) { 14498c2ecf20Sopenharmony_ci case 0x0: /* not yet synchronized */ 14508c2ecf20Sopenharmony_ci case 0x1: 14518c2ecf20Sopenharmony_ci case 0x3: 14528c2ecf20Sopenharmony_ci case 0x4: 14538c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "modem not yet " 14548c2ecf20Sopenharmony_ci "synchronized\n"); 14558c2ecf20Sopenharmony_ci return 0; 14568c2ecf20Sopenharmony_ci case 0x5: /* initialization */ 14578c2ecf20Sopenharmony_ci case 0x6: 14588c2ecf20Sopenharmony_ci case 0x9: 14598c2ecf20Sopenharmony_ci case 0xa: 14608c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n"); 14618c2ecf20Sopenharmony_ci return 0; 14628c2ecf20Sopenharmony_ci case 0x2: /* fail ... */ 14638c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem synchronization " 14648c2ecf20Sopenharmony_ci "failed (may be try other cmv/dsp)\n"); 14658c2ecf20Sopenharmony_ci return -EAGAIN; 14668c2ecf20Sopenharmony_ci case 0x7: /* operational */ 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci default: 14698c2ecf20Sopenharmony_ci uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n", 14708c2ecf20Sopenharmony_ci sc->stats.phy.state); 14718c2ecf20Sopenharmony_ci return 0; 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (data != 7) { 14758c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL); 14768c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem operational\n"); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* release the dsp firmware as it is not needed until 14798c2ecf20Sopenharmony_ci * the next failure 14808c2ecf20Sopenharmony_ci */ 14818c2ecf20Sopenharmony_ci release_firmware(sc->dsp_firm); 14828c2ecf20Sopenharmony_ci sc->dsp_firm = NULL; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci /* always update it as atm layer could not be init when we switch to 14868c2ecf20Sopenharmony_ci * operational state 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_ci UPDATE_ATM_SIGNAL(ATM_PHY_SIG_FOUND); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* wake up processes waiting for synchronization */ 14918c2ecf20Sopenharmony_ci wake_up(&sc->sync_q); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci /* TODO improve this state machine : 14948c2ecf20Sopenharmony_ci * we need some CMV info : what they do and their unit 14958c2ecf20Sopenharmony_ci * we should find the equivalent of eagle3- CMV 14968c2ecf20Sopenharmony_ci */ 14978c2ecf20Sopenharmony_ci /* check flags */ 14988c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 1, E4_SA_DIAG, 0, 0, &sc->stats.phy.flags); 14998c2ecf20Sopenharmony_ci if (ret < 0) 15008c2ecf20Sopenharmony_ci return ret; 15018c2ecf20Sopenharmony_ci sc->stats.phy.mflags |= sc->stats.phy.flags; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci /* in case of a flags ( for example delineation LOSS (& 0x10)), 15048c2ecf20Sopenharmony_ci * we check the status again in order to detect the failure earlier 15058c2ecf20Sopenharmony_ci */ 15068c2ecf20Sopenharmony_ci if (sc->stats.phy.flags) { 15078c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n", 15088c2ecf20Sopenharmony_ci sc->stats.phy.flags); 15098c2ecf20Sopenharmony_ci if (sc->stats.phy.flags & 1) /* delineation LOSS */ 15108c2ecf20Sopenharmony_ci return -EAGAIN; 15118c2ecf20Sopenharmony_ci if (sc->stats.phy.flags & 0x4000) /* Reset Flag */ 15128c2ecf20Sopenharmony_ci return -EAGAIN; 15138c2ecf20Sopenharmony_ci return 0; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* rate data may be in upper or lower half of 64 bit word, strange */ 15178c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 0, 0, tmp_arr); 15188c2ecf20Sopenharmony_ci if (ret < 0) 15198c2ecf20Sopenharmony_ci return ret; 15208c2ecf20Sopenharmony_ci data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1]; 15218c2ecf20Sopenharmony_ci sc->stats.phy.usrate = data / 1000; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 1, 0, tmp_arr); 15248c2ecf20Sopenharmony_ci if (ret < 0) 15258c2ecf20Sopenharmony_ci return ret; 15268c2ecf20Sopenharmony_ci data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1]; 15278c2ecf20Sopenharmony_ci uea_set_bulk_timeout(sc, data / 1000); 15288c2ecf20Sopenharmony_ci sc->stats.phy.dsrate = data / 1000; 15298c2ecf20Sopenharmony_ci UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 1, &data); 15328c2ecf20Sopenharmony_ci if (ret < 0) 15338c2ecf20Sopenharmony_ci return ret; 15348c2ecf20Sopenharmony_ci sc->stats.phy.dsattenuation = data / 10; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 1, &data); 15378c2ecf20Sopenharmony_ci if (ret < 0) 15388c2ecf20Sopenharmony_ci return ret; 15398c2ecf20Sopenharmony_ci sc->stats.phy.usattenuation = data / 10; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 3, &data); 15428c2ecf20Sopenharmony_ci if (ret < 0) 15438c2ecf20Sopenharmony_ci return ret; 15448c2ecf20Sopenharmony_ci sc->stats.phy.dsmargin = data / 2; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 3, &data); 15478c2ecf20Sopenharmony_ci if (ret < 0) 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci sc->stats.phy.usmargin = data / 10; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci return 0; 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_cistatic void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver) 15558c2ecf20Sopenharmony_ci{ 15568c2ecf20Sopenharmony_ci char file_arr[] = "CMVxy.bin"; 15578c2ecf20Sopenharmony_ci char *file; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci kernel_param_lock(THIS_MODULE); 15608c2ecf20Sopenharmony_ci /* set proper name corresponding modem version and line type */ 15618c2ecf20Sopenharmony_ci if (cmv_file[sc->modem_index] == NULL) { 15628c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == ADI930) 15638c2ecf20Sopenharmony_ci file_arr[3] = '9'; 15648c2ecf20Sopenharmony_ci else if (UEA_CHIP_VERSION(sc) == EAGLE_IV) 15658c2ecf20Sopenharmony_ci file_arr[3] = '4'; 15668c2ecf20Sopenharmony_ci else 15678c2ecf20Sopenharmony_ci file_arr[3] = 'e'; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci file_arr[4] = IS_ISDN(sc) ? 'i' : 'p'; 15708c2ecf20Sopenharmony_ci file = file_arr; 15718c2ecf20Sopenharmony_ci } else 15728c2ecf20Sopenharmony_ci file = cmv_file[sc->modem_index]; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci strcpy(cmv_name, FW_DIR); 15758c2ecf20Sopenharmony_ci strlcat(cmv_name, file, UEA_FW_NAME_MAX); 15768c2ecf20Sopenharmony_ci if (ver == 2) 15778c2ecf20Sopenharmony_ci strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX); 15788c2ecf20Sopenharmony_ci kernel_param_unlock(THIS_MODULE); 15798c2ecf20Sopenharmony_ci} 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_cistatic int request_cmvs_old(struct uea_softc *sc, 15828c2ecf20Sopenharmony_ci void **cmvs, const struct firmware **fw) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci int ret, size; 15858c2ecf20Sopenharmony_ci u8 *data; 15868c2ecf20Sopenharmony_ci char cmv_name[UEA_FW_NAME_MAX]; /* 30 bytes stack variable */ 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci cmvs_file_name(sc, cmv_name, 1); 15898c2ecf20Sopenharmony_ci ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev); 15908c2ecf20Sopenharmony_ci if (ret < 0) { 15918c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 15928c2ecf20Sopenharmony_ci "requesting firmware %s failed with error %d\n", 15938c2ecf20Sopenharmony_ci cmv_name, ret); 15948c2ecf20Sopenharmony_ci return ret; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci data = (u8 *) (*fw)->data; 15988c2ecf20Sopenharmony_ci size = (*fw)->size; 15998c2ecf20Sopenharmony_ci if (size < 1) 16008c2ecf20Sopenharmony_ci goto err_fw_corrupted; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci if (size != *data * sizeof(struct uea_cmvs_v1) + 1) 16038c2ecf20Sopenharmony_ci goto err_fw_corrupted; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci *cmvs = (void *)(data + 1); 16068c2ecf20Sopenharmony_ci return *data; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cierr_fw_corrupted: 16098c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name); 16108c2ecf20Sopenharmony_ci release_firmware(*fw); 16118c2ecf20Sopenharmony_ci return -EILSEQ; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic int request_cmvs(struct uea_softc *sc, 16158c2ecf20Sopenharmony_ci void **cmvs, const struct firmware **fw, int *ver) 16168c2ecf20Sopenharmony_ci{ 16178c2ecf20Sopenharmony_ci int ret, size; 16188c2ecf20Sopenharmony_ci u32 crc; 16198c2ecf20Sopenharmony_ci u8 *data; 16208c2ecf20Sopenharmony_ci char cmv_name[UEA_FW_NAME_MAX]; /* 30 bytes stack variable */ 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci cmvs_file_name(sc, cmv_name, 2); 16238c2ecf20Sopenharmony_ci ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev); 16248c2ecf20Sopenharmony_ci if (ret < 0) { 16258c2ecf20Sopenharmony_ci /* if caller can handle old version, try to provide it */ 16268c2ecf20Sopenharmony_ci if (*ver == 1) { 16278c2ecf20Sopenharmony_ci uea_warn(INS_TO_USBDEV(sc), "requesting " 16288c2ecf20Sopenharmony_ci "firmware %s failed, " 16298c2ecf20Sopenharmony_ci "try to get older cmvs\n", cmv_name); 16308c2ecf20Sopenharmony_ci return request_cmvs_old(sc, cmvs, fw); 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 16338c2ecf20Sopenharmony_ci "requesting firmware %s failed with error %d\n", 16348c2ecf20Sopenharmony_ci cmv_name, ret); 16358c2ecf20Sopenharmony_ci return ret; 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci size = (*fw)->size; 16398c2ecf20Sopenharmony_ci data = (u8 *) (*fw)->data; 16408c2ecf20Sopenharmony_ci if (size < 4 || strncmp(data, "cmv2", 4) != 0) { 16418c2ecf20Sopenharmony_ci if (*ver == 1) { 16428c2ecf20Sopenharmony_ci uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted," 16438c2ecf20Sopenharmony_ci " try to get older cmvs\n", cmv_name); 16448c2ecf20Sopenharmony_ci release_firmware(*fw); 16458c2ecf20Sopenharmony_ci return request_cmvs_old(sc, cmvs, fw); 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci goto err_fw_corrupted; 16488c2ecf20Sopenharmony_ci } 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci *ver = 2; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci data += 4; 16538c2ecf20Sopenharmony_ci size -= 4; 16548c2ecf20Sopenharmony_ci if (size < 5) 16558c2ecf20Sopenharmony_ci goto err_fw_corrupted; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci crc = get_unaligned_le32(data); 16588c2ecf20Sopenharmony_ci data += 4; 16598c2ecf20Sopenharmony_ci size -= 4; 16608c2ecf20Sopenharmony_ci if (crc32_be(0, data, size) != crc) 16618c2ecf20Sopenharmony_ci goto err_fw_corrupted; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (size != *data * sizeof(struct uea_cmvs_v2) + 1) 16648c2ecf20Sopenharmony_ci goto err_fw_corrupted; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci *cmvs = (void *) (data + 1); 16678c2ecf20Sopenharmony_ci return *data; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cierr_fw_corrupted: 16708c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name); 16718c2ecf20Sopenharmony_ci release_firmware(*fw); 16728c2ecf20Sopenharmony_ci return -EILSEQ; 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_cistatic int uea_send_cmvs_e1(struct uea_softc *sc) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci int i, ret, len; 16788c2ecf20Sopenharmony_ci void *cmvs_ptr; 16798c2ecf20Sopenharmony_ci const struct firmware *cmvs_fw; 16808c2ecf20Sopenharmony_ci int ver = 1; /* we can handle v1 cmv firmware version; */ 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci /* Enter in R-IDLE (cmv) until instructed otherwise */ 16838c2ecf20Sopenharmony_ci ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 1); 16848c2ecf20Sopenharmony_ci if (ret < 0) 16858c2ecf20Sopenharmony_ci return ret; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci /* Dump firmware version */ 16888c2ecf20Sopenharmony_ci ret = uea_read_cmv_e1(sc, E1_SA_INFO, 10, &sc->stats.phy.firmid); 16898c2ecf20Sopenharmony_ci if (ret < 0) 16908c2ecf20Sopenharmony_ci return ret; 16918c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n", 16928c2ecf20Sopenharmony_ci sc->stats.phy.firmid); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci /* get options */ 16958c2ecf20Sopenharmony_ci ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver); 16968c2ecf20Sopenharmony_ci if (ret < 0) 16978c2ecf20Sopenharmony_ci return ret; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* send options */ 17008c2ecf20Sopenharmony_ci if (ver == 1) { 17018c2ecf20Sopenharmony_ci struct uea_cmvs_v1 *cmvs_v1 = cmvs_ptr; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci uea_warn(INS_TO_USBDEV(sc), "use deprecated cmvs version, " 17048c2ecf20Sopenharmony_ci "please update your firmware\n"); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 17078c2ecf20Sopenharmony_ci ret = uea_write_cmv_e1(sc, 17088c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v1[i].address), 17098c2ecf20Sopenharmony_ci get_unaligned_le16(&cmvs_v1[i].offset), 17108c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v1[i].data)); 17118c2ecf20Sopenharmony_ci if (ret < 0) 17128c2ecf20Sopenharmony_ci goto out; 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci } else if (ver == 2) { 17158c2ecf20Sopenharmony_ci struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 17188c2ecf20Sopenharmony_ci ret = uea_write_cmv_e1(sc, 17198c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v2[i].address), 17208c2ecf20Sopenharmony_ci (u16) get_unaligned_le32(&cmvs_v2[i].offset), 17218c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v2[i].data)); 17228c2ecf20Sopenharmony_ci if (ret < 0) 17238c2ecf20Sopenharmony_ci goto out; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci } else { 17268c2ecf20Sopenharmony_ci /* This really should not happen */ 17278c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver); 17288c2ecf20Sopenharmony_ci goto out; 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci /* Enter in R-ACT-REQ */ 17328c2ecf20Sopenharmony_ci ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 2); 17338c2ecf20Sopenharmony_ci uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n"); 17348c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem started, waiting " 17358c2ecf20Sopenharmony_ci "synchronization...\n"); 17368c2ecf20Sopenharmony_ciout: 17378c2ecf20Sopenharmony_ci release_firmware(cmvs_fw); 17388c2ecf20Sopenharmony_ci return ret; 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_cistatic int uea_send_cmvs_e4(struct uea_softc *sc) 17428c2ecf20Sopenharmony_ci{ 17438c2ecf20Sopenharmony_ci int i, ret, len; 17448c2ecf20Sopenharmony_ci void *cmvs_ptr; 17458c2ecf20Sopenharmony_ci const struct firmware *cmvs_fw; 17468c2ecf20Sopenharmony_ci int ver = 2; /* we can only handle v2 cmv firmware version; */ 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci /* Enter in R-IDLE (cmv) until instructed otherwise */ 17498c2ecf20Sopenharmony_ci ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 1); 17508c2ecf20Sopenharmony_ci if (ret < 0) 17518c2ecf20Sopenharmony_ci return ret; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci /* Dump firmware version */ 17548c2ecf20Sopenharmony_ci /* XXX don't read the 3th byte as it is always 6 */ 17558c2ecf20Sopenharmony_ci ret = uea_read_cmv_e4(sc, 2, E4_SA_INFO, 55, 0, &sc->stats.phy.firmid); 17568c2ecf20Sopenharmony_ci if (ret < 0) 17578c2ecf20Sopenharmony_ci return ret; 17588c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n", 17598c2ecf20Sopenharmony_ci sc->stats.phy.firmid); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci /* get options */ 17638c2ecf20Sopenharmony_ci ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver); 17648c2ecf20Sopenharmony_ci if (ret < 0) 17658c2ecf20Sopenharmony_ci return ret; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci /* send options */ 17688c2ecf20Sopenharmony_ci if (ver == 2) { 17698c2ecf20Sopenharmony_ci struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 17728c2ecf20Sopenharmony_ci ret = uea_write_cmv_e4(sc, 1, 17738c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v2[i].group), 17748c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v2[i].address), 17758c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v2[i].offset), 17768c2ecf20Sopenharmony_ci get_unaligned_le32(&cmvs_v2[i].data)); 17778c2ecf20Sopenharmony_ci if (ret < 0) 17788c2ecf20Sopenharmony_ci goto out; 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci } else { 17818c2ecf20Sopenharmony_ci /* This really should not happen */ 17828c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver); 17838c2ecf20Sopenharmony_ci goto out; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* Enter in R-ACT-REQ */ 17878c2ecf20Sopenharmony_ci ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 2); 17888c2ecf20Sopenharmony_ci uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n"); 17898c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "modem started, waiting " 17908c2ecf20Sopenharmony_ci "synchronization...\n"); 17918c2ecf20Sopenharmony_ciout: 17928c2ecf20Sopenharmony_ci release_firmware(cmvs_fw); 17938c2ecf20Sopenharmony_ci return ret; 17948c2ecf20Sopenharmony_ci} 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci/* Start boot post firmware modem: 17978c2ecf20Sopenharmony_ci * - send reset commands through usb control pipe 17988c2ecf20Sopenharmony_ci * - start workqueue for DSP loading 17998c2ecf20Sopenharmony_ci * - send CMV options to modem 18008c2ecf20Sopenharmony_ci */ 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_cistatic int uea_start_reset(struct uea_softc *sc) 18038c2ecf20Sopenharmony_ci{ 18048c2ecf20Sopenharmony_ci u16 zero = 0; /* ;-) */ 18058c2ecf20Sopenharmony_ci int ret; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 18088c2ecf20Sopenharmony_ci uea_info(INS_TO_USBDEV(sc), "(re)booting started\n"); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* mask interrupt */ 18118c2ecf20Sopenharmony_ci sc->booting = 1; 18128c2ecf20Sopenharmony_ci /* We need to set this here because, a ack timeout could have occurred, 18138c2ecf20Sopenharmony_ci * but before we start the reboot, the ack occurs and set this to 1. 18148c2ecf20Sopenharmony_ci * So we will failed to wait Ready CMV. 18158c2ecf20Sopenharmony_ci */ 18168c2ecf20Sopenharmony_ci sc->cmv_ack = 0; 18178c2ecf20Sopenharmony_ci UPDATE_ATM_SIGNAL(ATM_PHY_SIG_LOST); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci /* reset statistics */ 18208c2ecf20Sopenharmony_ci memset(&sc->stats, 0, sizeof(struct uea_stats)); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* tell the modem that we want to boot in IDMA mode */ 18238c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL); 18248c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_BOOT_IDMA, 0, NULL); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci /* enter reset mode */ 18278c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci /* original driver use 200ms, but windows driver use 100ms */ 18308c2ecf20Sopenharmony_ci ret = uea_wait(sc, 0, msecs_to_jiffies(100)); 18318c2ecf20Sopenharmony_ci if (ret < 0) 18328c2ecf20Sopenharmony_ci return ret; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci /* leave reset mode */ 18358c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) != EAGLE_IV) { 18388c2ecf20Sopenharmony_ci /* clear tx and rx mailboxes */ 18398c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero); 18408c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero); 18418c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero); 18428c2ecf20Sopenharmony_ci } 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci ret = uea_wait(sc, 0, msecs_to_jiffies(1000)); 18458c2ecf20Sopenharmony_ci if (ret < 0) 18468c2ecf20Sopenharmony_ci return ret; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == EAGLE_IV) 18498c2ecf20Sopenharmony_ci sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, 18508c2ecf20Sopenharmony_ci E4_MODEMREADY, 1); 18518c2ecf20Sopenharmony_ci else 18528c2ecf20Sopenharmony_ci sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, 18538c2ecf20Sopenharmony_ci E1_MODEMREADY); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* demask interrupt */ 18568c2ecf20Sopenharmony_ci sc->booting = 0; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci /* start loading DSP */ 18598c2ecf20Sopenharmony_ci sc->pageno = 0; 18608c2ecf20Sopenharmony_ci sc->ovl = 0; 18618c2ecf20Sopenharmony_ci schedule_work(&sc->task); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* wait for modem ready CMV */ 18648c2ecf20Sopenharmony_ci ret = wait_cmv_ack(sc); 18658c2ecf20Sopenharmony_ci if (ret < 0) 18668c2ecf20Sopenharmony_ci return ret; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci uea_vdbg(INS_TO_USBDEV(sc), "Ready CMV received\n"); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci ret = sc->send_cmvs(sc); 18718c2ecf20Sopenharmony_ci if (ret < 0) 18728c2ecf20Sopenharmony_ci return ret; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci sc->reset = 0; 18758c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 18768c2ecf20Sopenharmony_ci return ret; 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci/* 18808c2ecf20Sopenharmony_ci * In case of an error wait 1s before rebooting the modem 18818c2ecf20Sopenharmony_ci * if the modem don't request reboot (-EAGAIN). 18828c2ecf20Sopenharmony_ci * Monitor the modem every 1s. 18838c2ecf20Sopenharmony_ci */ 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_cistatic int uea_kthread(void *data) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct uea_softc *sc = data; 18888c2ecf20Sopenharmony_ci int ret = -EAGAIN; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci set_freezable(); 18918c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 18928c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 18938c2ecf20Sopenharmony_ci if (ret < 0 || sc->reset) 18948c2ecf20Sopenharmony_ci ret = uea_start_reset(sc); 18958c2ecf20Sopenharmony_ci if (!ret) 18968c2ecf20Sopenharmony_ci ret = sc->stat(sc); 18978c2ecf20Sopenharmony_ci if (ret != -EAGAIN) 18988c2ecf20Sopenharmony_ci uea_wait(sc, 0, msecs_to_jiffies(1000)); 18998c2ecf20Sopenharmony_ci try_to_freeze(); 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 19028c2ecf20Sopenharmony_ci return ret; 19038c2ecf20Sopenharmony_ci} 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci/* Load second usb firmware for ADI930 chip */ 19068c2ecf20Sopenharmony_cistatic int load_XILINX_firmware(struct uea_softc *sc) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci const struct firmware *fw_entry; 19098c2ecf20Sopenharmony_ci int ret, size, u, ln; 19108c2ecf20Sopenharmony_ci const u8 *pfw; 19118c2ecf20Sopenharmony_ci u8 value; 19128c2ecf20Sopenharmony_ci char *fw_name = FPGA930_FIRMWARE; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev); 19178c2ecf20Sopenharmony_ci if (ret) { 19188c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n", 19198c2ecf20Sopenharmony_ci fw_name); 19208c2ecf20Sopenharmony_ci goto err0; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci pfw = fw_entry->data; 19248c2ecf20Sopenharmony_ci size = fw_entry->size; 19258c2ecf20Sopenharmony_ci if (size != 0x577B) { 19268c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", 19278c2ecf20Sopenharmony_ci fw_name); 19288c2ecf20Sopenharmony_ci ret = -EILSEQ; 19298c2ecf20Sopenharmony_ci goto err1; 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci for (u = 0; u < size; u += ln) { 19328c2ecf20Sopenharmony_ci ln = min(size - u, 64); 19338c2ecf20Sopenharmony_ci ret = uea_request(sc, 0xe, 0, ln, pfw + u); 19348c2ecf20Sopenharmony_ci if (ret < 0) { 19358c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 19368c2ecf20Sopenharmony_ci "elsa download data failed (%d)\n", ret); 19378c2ecf20Sopenharmony_ci goto err1; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* finish to send the fpga */ 19428c2ecf20Sopenharmony_ci ret = uea_request(sc, 0xe, 1, 0, NULL); 19438c2ecf20Sopenharmony_ci if (ret < 0) { 19448c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 19458c2ecf20Sopenharmony_ci "elsa download data failed (%d)\n", ret); 19468c2ecf20Sopenharmony_ci goto err1; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* Tell the modem we finish : de-assert reset */ 19508c2ecf20Sopenharmony_ci value = 0; 19518c2ecf20Sopenharmony_ci ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value); 19528c2ecf20Sopenharmony_ci if (ret < 0) 19538c2ecf20Sopenharmony_ci uea_err(sc->usb_dev, "elsa de-assert failed with error" 19548c2ecf20Sopenharmony_ci " %d\n", ret); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_cierr1: 19578c2ecf20Sopenharmony_ci release_firmware(fw_entry); 19588c2ecf20Sopenharmony_cierr0: 19598c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 19608c2ecf20Sopenharmony_ci return ret; 19618c2ecf20Sopenharmony_ci} 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci/* The modem send us an ack. First with check if it right */ 19648c2ecf20Sopenharmony_cistatic void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) 19658c2ecf20Sopenharmony_ci{ 19668c2ecf20Sopenharmony_ci struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1; 19678c2ecf20Sopenharmony_ci struct cmv_e1 *cmv = &intr->u.e1.s2.cmv; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 19708c2ecf20Sopenharmony_ci if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE) 19718c2ecf20Sopenharmony_ci goto bad1; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci if (cmv->bDirection != E1_MODEMTOHOST) 19748c2ecf20Sopenharmony_ci goto bad1; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to 19778c2ecf20Sopenharmony_ci * the first MEMACCESS cmv. Ignore it... 19788c2ecf20Sopenharmony_ci */ 19798c2ecf20Sopenharmony_ci if (cmv->bFunction != dsc->function) { 19808c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == ADI930 19818c2ecf20Sopenharmony_ci && cmv->bFunction == E1_MAKEFUNCTION(2, 2)) { 19828c2ecf20Sopenharmony_ci cmv->wIndex = cpu_to_le16(dsc->idx); 19838c2ecf20Sopenharmony_ci put_unaligned_le32(dsc->address, 19848c2ecf20Sopenharmony_ci &cmv->dwSymbolicAddress); 19858c2ecf20Sopenharmony_ci cmv->wOffsetAddress = cpu_to_le16(dsc->offset); 19868c2ecf20Sopenharmony_ci } else 19878c2ecf20Sopenharmony_ci goto bad2; 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, 19918c2ecf20Sopenharmony_ci E1_MODEMREADY)) { 19928c2ecf20Sopenharmony_ci wake_up_cmv_ack(sc); 19938c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 19948c2ecf20Sopenharmony_ci return; 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* in case of MEMACCESS */ 19988c2ecf20Sopenharmony_ci if (le16_to_cpu(cmv->wIndex) != dsc->idx || 19998c2ecf20Sopenharmony_ci get_unaligned_le32(&cmv->dwSymbolicAddress) != dsc->address || 20008c2ecf20Sopenharmony_ci le16_to_cpu(cmv->wOffsetAddress) != dsc->offset) 20018c2ecf20Sopenharmony_ci goto bad2; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci sc->data = get_unaligned_le32(&cmv->dwData); 20048c2ecf20Sopenharmony_ci sc->data = sc->data << 16 | sc->data >> 16; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci wake_up_cmv_ack(sc); 20078c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 20088c2ecf20Sopenharmony_ci return; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_cibad2: 20118c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "unexpected cmv received, " 20128c2ecf20Sopenharmony_ci "Function : %d, Subfunction : %d\n", 20138c2ecf20Sopenharmony_ci E1_FUNCTION_TYPE(cmv->bFunction), 20148c2ecf20Sopenharmony_ci E1_FUNCTION_SUBTYPE(cmv->bFunction)); 20158c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 20168c2ecf20Sopenharmony_ci return; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_cibad1: 20198c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "invalid cmv received, " 20208c2ecf20Sopenharmony_ci "wPreamble %d, bDirection %d\n", 20218c2ecf20Sopenharmony_ci le16_to_cpu(cmv->wPreamble), cmv->bDirection); 20228c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 20238c2ecf20Sopenharmony_ci} 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci/* The modem send us an ack. First with check if it right */ 20268c2ecf20Sopenharmony_cistatic void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) 20278c2ecf20Sopenharmony_ci{ 20288c2ecf20Sopenharmony_ci struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4; 20298c2ecf20Sopenharmony_ci struct cmv_e4 *cmv = &intr->u.e4.s2.cmv; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 20328c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n", 20338c2ecf20Sopenharmony_ci be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction), 20348c2ecf20Sopenharmony_ci be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress), 20358c2ecf20Sopenharmony_ci be32_to_cpu(cmv->dwData[0]), be32_to_cpu(cmv->dwData[1])); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci if (be16_to_cpu(cmv->wFunction) != dsc->function) 20388c2ecf20Sopenharmony_ci goto bad2; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, 20418c2ecf20Sopenharmony_ci E4_MODEMREADY, 1)) { 20428c2ecf20Sopenharmony_ci wake_up_cmv_ack(sc); 20438c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 20448c2ecf20Sopenharmony_ci return; 20458c2ecf20Sopenharmony_ci } 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci /* in case of MEMACCESS */ 20488c2ecf20Sopenharmony_ci if (be16_to_cpu(cmv->wOffset) != dsc->offset || 20498c2ecf20Sopenharmony_ci be16_to_cpu(cmv->wGroup) != dsc->group || 20508c2ecf20Sopenharmony_ci be16_to_cpu(cmv->wAddress) != dsc->address) 20518c2ecf20Sopenharmony_ci goto bad2; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci sc->data = be32_to_cpu(cmv->dwData[0]); 20548c2ecf20Sopenharmony_ci sc->data1 = be32_to_cpu(cmv->dwData[1]); 20558c2ecf20Sopenharmony_ci wake_up_cmv_ack(sc); 20568c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 20578c2ecf20Sopenharmony_ci return; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_cibad2: 20608c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "unexpected cmv received, " 20618c2ecf20Sopenharmony_ci "Function : %d, Subfunction : %d\n", 20628c2ecf20Sopenharmony_ci E4_FUNCTION_TYPE(cmv->wFunction), 20638c2ecf20Sopenharmony_ci E4_FUNCTION_SUBTYPE(cmv->wFunction)); 20648c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 20658c2ecf20Sopenharmony_ci return; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic void uea_schedule_load_page_e1(struct uea_softc *sc, 20698c2ecf20Sopenharmony_ci struct intr_pkt *intr) 20708c2ecf20Sopenharmony_ci{ 20718c2ecf20Sopenharmony_ci sc->pageno = intr->e1_bSwapPageNo; 20728c2ecf20Sopenharmony_ci sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4; 20738c2ecf20Sopenharmony_ci schedule_work(&sc->task); 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cistatic void uea_schedule_load_page_e4(struct uea_softc *sc, 20778c2ecf20Sopenharmony_ci struct intr_pkt *intr) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci sc->pageno = intr->e4_bSwapPageNo; 20808c2ecf20Sopenharmony_ci schedule_work(&sc->task); 20818c2ecf20Sopenharmony_ci} 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci/* 20848c2ecf20Sopenharmony_ci * interrupt handler 20858c2ecf20Sopenharmony_ci */ 20868c2ecf20Sopenharmony_cistatic void uea_intr(struct urb *urb) 20878c2ecf20Sopenharmony_ci{ 20888c2ecf20Sopenharmony_ci struct uea_softc *sc = urb->context; 20898c2ecf20Sopenharmony_ci struct intr_pkt *intr = urb->transfer_buffer; 20908c2ecf20Sopenharmony_ci int status = urb->status; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci if (unlikely(status < 0)) { 20958c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", 20968c2ecf20Sopenharmony_ci status); 20978c2ecf20Sopenharmony_ci return; 20988c2ecf20Sopenharmony_ci } 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci /* device-to-host interrupt */ 21018c2ecf20Sopenharmony_ci if (intr->bType != 0x08 || sc->booting) { 21028c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "wrong interrupt\n"); 21038c2ecf20Sopenharmony_ci goto resubmit; 21048c2ecf20Sopenharmony_ci } 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci switch (le16_to_cpu(intr->wInterrupt)) { 21078c2ecf20Sopenharmony_ci case INT_LOADSWAPPAGE: 21088c2ecf20Sopenharmony_ci sc->schedule_load_page(sc, intr); 21098c2ecf20Sopenharmony_ci break; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci case INT_INCOMINGCMV: 21128c2ecf20Sopenharmony_ci sc->dispatch_cmv(sc, intr); 21138c2ecf20Sopenharmony_ci break; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci default: 21168c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "unknown interrupt %u\n", 21178c2ecf20Sopenharmony_ci le16_to_cpu(intr->wInterrupt)); 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ciresubmit: 21218c2ecf20Sopenharmony_ci usb_submit_urb(sc->urb_int, GFP_ATOMIC); 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci/* 21258c2ecf20Sopenharmony_ci * Start the modem : init the data and start kernel thread 21268c2ecf20Sopenharmony_ci */ 21278c2ecf20Sopenharmony_cistatic int uea_boot(struct uea_softc *sc, struct usb_interface *intf) 21288c2ecf20Sopenharmony_ci{ 21298c2ecf20Sopenharmony_ci struct intr_pkt *intr; 21308c2ecf20Sopenharmony_ci int ret = -ENOMEM; 21318c2ecf20Sopenharmony_ci int size; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { 21368c2ecf20Sopenharmony_ci size = E4_INTR_PKT_SIZE; 21378c2ecf20Sopenharmony_ci sc->dispatch_cmv = uea_dispatch_cmv_e4; 21388c2ecf20Sopenharmony_ci sc->schedule_load_page = uea_schedule_load_page_e4; 21398c2ecf20Sopenharmony_ci sc->stat = uea_stat_e4; 21408c2ecf20Sopenharmony_ci sc->send_cmvs = uea_send_cmvs_e4; 21418c2ecf20Sopenharmony_ci INIT_WORK(&sc->task, uea_load_page_e4); 21428c2ecf20Sopenharmony_ci } else { 21438c2ecf20Sopenharmony_ci size = E1_INTR_PKT_SIZE; 21448c2ecf20Sopenharmony_ci sc->dispatch_cmv = uea_dispatch_cmv_e1; 21458c2ecf20Sopenharmony_ci sc->schedule_load_page = uea_schedule_load_page_e1; 21468c2ecf20Sopenharmony_ci sc->stat = uea_stat_e1; 21478c2ecf20Sopenharmony_ci sc->send_cmvs = uea_send_cmvs_e1; 21488c2ecf20Sopenharmony_ci INIT_WORK(&sc->task, uea_load_page_e1); 21498c2ecf20Sopenharmony_ci } 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci init_waitqueue_head(&sc->sync_q); 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == ADI930) 21548c2ecf20Sopenharmony_ci load_XILINX_firmware(sc); 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci if (intf->cur_altsetting->desc.bNumEndpoints < 1) { 21578c2ecf20Sopenharmony_ci ret = -ENODEV; 21588c2ecf20Sopenharmony_ci goto err0; 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci intr = kmalloc(size, GFP_KERNEL); 21628c2ecf20Sopenharmony_ci if (!intr) 21638c2ecf20Sopenharmony_ci goto err0; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci sc->urb_int = usb_alloc_urb(0, GFP_KERNEL); 21668c2ecf20Sopenharmony_ci if (!sc->urb_int) 21678c2ecf20Sopenharmony_ci goto err1; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci usb_fill_int_urb(sc->urb_int, sc->usb_dev, 21708c2ecf20Sopenharmony_ci usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE), 21718c2ecf20Sopenharmony_ci intr, size, uea_intr, sc, 21728c2ecf20Sopenharmony_ci intf->cur_altsetting->endpoint[0].desc.bInterval); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci ret = usb_submit_urb(sc->urb_int, GFP_KERNEL); 21758c2ecf20Sopenharmony_ci if (ret < 0) { 21768c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), 21778c2ecf20Sopenharmony_ci "urb submission failed with error %d\n", ret); 21788c2ecf20Sopenharmony_ci goto err1; 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* Create worker thread, but don't start it here. Start it after 21828c2ecf20Sopenharmony_ci * all usbatm generic initialization is done. 21838c2ecf20Sopenharmony_ci */ 21848c2ecf20Sopenharmony_ci sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm"); 21858c2ecf20Sopenharmony_ci if (IS_ERR(sc->kthread)) { 21868c2ecf20Sopenharmony_ci uea_err(INS_TO_USBDEV(sc), "failed to create thread\n"); 21878c2ecf20Sopenharmony_ci ret = PTR_ERR(sc->kthread); 21888c2ecf20Sopenharmony_ci goto err2; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 21928c2ecf20Sopenharmony_ci return 0; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_cierr2: 21958c2ecf20Sopenharmony_ci usb_kill_urb(sc->urb_int); 21968c2ecf20Sopenharmony_cierr1: 21978c2ecf20Sopenharmony_ci usb_free_urb(sc->urb_int); 21988c2ecf20Sopenharmony_ci sc->urb_int = NULL; 21998c2ecf20Sopenharmony_ci kfree(intr); 22008c2ecf20Sopenharmony_cierr0: 22018c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 22028c2ecf20Sopenharmony_ci return ret; 22038c2ecf20Sopenharmony_ci} 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci/* 22068c2ecf20Sopenharmony_ci * Stop the modem : kill kernel thread and free data 22078c2ecf20Sopenharmony_ci */ 22088c2ecf20Sopenharmony_cistatic void uea_stop(struct uea_softc *sc) 22098c2ecf20Sopenharmony_ci{ 22108c2ecf20Sopenharmony_ci int ret; 22118c2ecf20Sopenharmony_ci uea_enters(INS_TO_USBDEV(sc)); 22128c2ecf20Sopenharmony_ci ret = kthread_stop(sc->kthread); 22138c2ecf20Sopenharmony_ci uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci usb_kill_urb(sc->urb_int); 22188c2ecf20Sopenharmony_ci kfree(sc->urb_int->transfer_buffer); 22198c2ecf20Sopenharmony_ci usb_free_urb(sc->urb_int); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci /* flush the work item, when no one can schedule it */ 22228c2ecf20Sopenharmony_ci flush_work(&sc->task); 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci release_firmware(sc->dsp_firm); 22258c2ecf20Sopenharmony_ci uea_leaves(INS_TO_USBDEV(sc)); 22268c2ecf20Sopenharmony_ci} 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci/* syfs interface */ 22298c2ecf20Sopenharmony_cistatic struct uea_softc *dev_to_uea(struct device *dev) 22308c2ecf20Sopenharmony_ci{ 22318c2ecf20Sopenharmony_ci struct usb_interface *intf; 22328c2ecf20Sopenharmony_ci struct usbatm_data *usbatm; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci intf = to_usb_interface(dev); 22358c2ecf20Sopenharmony_ci if (!intf) 22368c2ecf20Sopenharmony_ci return NULL; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci usbatm = usb_get_intfdata(intf); 22398c2ecf20Sopenharmony_ci if (!usbatm) 22408c2ecf20Sopenharmony_ci return NULL; 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci return usbatm->driver_data; 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic ssize_t stat_status_show(struct device *dev, struct device_attribute *attr, 22468c2ecf20Sopenharmony_ci char *buf) 22478c2ecf20Sopenharmony_ci{ 22488c2ecf20Sopenharmony_ci int ret = -ENODEV; 22498c2ecf20Sopenharmony_ci struct uea_softc *sc; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci mutex_lock(&uea_mutex); 22528c2ecf20Sopenharmony_ci sc = dev_to_uea(dev); 22538c2ecf20Sopenharmony_ci if (!sc) 22548c2ecf20Sopenharmony_ci goto out; 22558c2ecf20Sopenharmony_ci ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state); 22568c2ecf20Sopenharmony_ciout: 22578c2ecf20Sopenharmony_ci mutex_unlock(&uea_mutex); 22588c2ecf20Sopenharmony_ci return ret; 22598c2ecf20Sopenharmony_ci} 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_cistatic ssize_t stat_status_store(struct device *dev, struct device_attribute *attr, 22628c2ecf20Sopenharmony_ci const char *buf, size_t count) 22638c2ecf20Sopenharmony_ci{ 22648c2ecf20Sopenharmony_ci int ret = -ENODEV; 22658c2ecf20Sopenharmony_ci struct uea_softc *sc; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci mutex_lock(&uea_mutex); 22688c2ecf20Sopenharmony_ci sc = dev_to_uea(dev); 22698c2ecf20Sopenharmony_ci if (!sc) 22708c2ecf20Sopenharmony_ci goto out; 22718c2ecf20Sopenharmony_ci sc->reset = 1; 22728c2ecf20Sopenharmony_ci ret = count; 22738c2ecf20Sopenharmony_ciout: 22748c2ecf20Sopenharmony_ci mutex_unlock(&uea_mutex); 22758c2ecf20Sopenharmony_ci return ret; 22768c2ecf20Sopenharmony_ci} 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(stat_status); 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_cistatic ssize_t stat_human_status_show(struct device *dev, 22818c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 22828c2ecf20Sopenharmony_ci{ 22838c2ecf20Sopenharmony_ci int ret = -ENODEV; 22848c2ecf20Sopenharmony_ci int modem_state; 22858c2ecf20Sopenharmony_ci struct uea_softc *sc; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci mutex_lock(&uea_mutex); 22888c2ecf20Sopenharmony_ci sc = dev_to_uea(dev); 22898c2ecf20Sopenharmony_ci if (!sc) 22908c2ecf20Sopenharmony_ci goto out; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { 22938c2ecf20Sopenharmony_ci switch (sc->stats.phy.state) { 22948c2ecf20Sopenharmony_ci case 0x0: /* not yet synchronized */ 22958c2ecf20Sopenharmony_ci case 0x1: 22968c2ecf20Sopenharmony_ci case 0x3: 22978c2ecf20Sopenharmony_ci case 0x4: 22988c2ecf20Sopenharmony_ci modem_state = 0; 22998c2ecf20Sopenharmony_ci break; 23008c2ecf20Sopenharmony_ci case 0x5: /* initialization */ 23018c2ecf20Sopenharmony_ci case 0x6: 23028c2ecf20Sopenharmony_ci case 0x9: 23038c2ecf20Sopenharmony_ci case 0xa: 23048c2ecf20Sopenharmony_ci modem_state = 1; 23058c2ecf20Sopenharmony_ci break; 23068c2ecf20Sopenharmony_ci case 0x7: /* operational */ 23078c2ecf20Sopenharmony_ci modem_state = 2; 23088c2ecf20Sopenharmony_ci break; 23098c2ecf20Sopenharmony_ci case 0x2: /* fail ... */ 23108c2ecf20Sopenharmony_ci modem_state = 3; 23118c2ecf20Sopenharmony_ci break; 23128c2ecf20Sopenharmony_ci default: /* unknown */ 23138c2ecf20Sopenharmony_ci modem_state = 4; 23148c2ecf20Sopenharmony_ci break; 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci } else 23178c2ecf20Sopenharmony_ci modem_state = GET_STATUS(sc->stats.phy.state); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci switch (modem_state) { 23208c2ecf20Sopenharmony_ci case 0: 23218c2ecf20Sopenharmony_ci ret = sprintf(buf, "Modem is booting\n"); 23228c2ecf20Sopenharmony_ci break; 23238c2ecf20Sopenharmony_ci case 1: 23248c2ecf20Sopenharmony_ci ret = sprintf(buf, "Modem is initializing\n"); 23258c2ecf20Sopenharmony_ci break; 23268c2ecf20Sopenharmony_ci case 2: 23278c2ecf20Sopenharmony_ci ret = sprintf(buf, "Modem is operational\n"); 23288c2ecf20Sopenharmony_ci break; 23298c2ecf20Sopenharmony_ci case 3: 23308c2ecf20Sopenharmony_ci ret = sprintf(buf, "Modem synchronization failed\n"); 23318c2ecf20Sopenharmony_ci break; 23328c2ecf20Sopenharmony_ci default: 23338c2ecf20Sopenharmony_ci ret = sprintf(buf, "Modem state is unknown\n"); 23348c2ecf20Sopenharmony_ci break; 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ciout: 23378c2ecf20Sopenharmony_ci mutex_unlock(&uea_mutex); 23388c2ecf20Sopenharmony_ci return ret; 23398c2ecf20Sopenharmony_ci} 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(stat_human_status); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_cistatic ssize_t stat_delin_show(struct device *dev, struct device_attribute *attr, 23448c2ecf20Sopenharmony_ci char *buf) 23458c2ecf20Sopenharmony_ci{ 23468c2ecf20Sopenharmony_ci int ret = -ENODEV; 23478c2ecf20Sopenharmony_ci struct uea_softc *sc; 23488c2ecf20Sopenharmony_ci char *delin = "GOOD"; 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci mutex_lock(&uea_mutex); 23518c2ecf20Sopenharmony_ci sc = dev_to_uea(dev); 23528c2ecf20Sopenharmony_ci if (!sc) 23538c2ecf20Sopenharmony_ci goto out; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { 23568c2ecf20Sopenharmony_ci if (sc->stats.phy.flags & 0x4000) 23578c2ecf20Sopenharmony_ci delin = "RESET"; 23588c2ecf20Sopenharmony_ci else if (sc->stats.phy.flags & 0x0001) 23598c2ecf20Sopenharmony_ci delin = "LOSS"; 23608c2ecf20Sopenharmony_ci } else { 23618c2ecf20Sopenharmony_ci if (sc->stats.phy.flags & 0x0C00) 23628c2ecf20Sopenharmony_ci delin = "ERROR"; 23638c2ecf20Sopenharmony_ci else if (sc->stats.phy.flags & 0x0030) 23648c2ecf20Sopenharmony_ci delin = "LOSS"; 23658c2ecf20Sopenharmony_ci } 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci ret = sprintf(buf, "%s\n", delin); 23688c2ecf20Sopenharmony_ciout: 23698c2ecf20Sopenharmony_ci mutex_unlock(&uea_mutex); 23708c2ecf20Sopenharmony_ci return ret; 23718c2ecf20Sopenharmony_ci} 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(stat_delin); 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci#define UEA_ATTR(name, reset) \ 23768c2ecf20Sopenharmony_ci \ 23778c2ecf20Sopenharmony_cistatic ssize_t stat_##name##_show(struct device *dev, \ 23788c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 23798c2ecf20Sopenharmony_ci{ \ 23808c2ecf20Sopenharmony_ci int ret = -ENODEV; \ 23818c2ecf20Sopenharmony_ci struct uea_softc *sc; \ 23828c2ecf20Sopenharmony_ci \ 23838c2ecf20Sopenharmony_ci mutex_lock(&uea_mutex); \ 23848c2ecf20Sopenharmony_ci sc = dev_to_uea(dev); \ 23858c2ecf20Sopenharmony_ci if (!sc) \ 23868c2ecf20Sopenharmony_ci goto out; \ 23878c2ecf20Sopenharmony_ci ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \ 23888c2ecf20Sopenharmony_ci if (reset) \ 23898c2ecf20Sopenharmony_ci sc->stats.phy.name = 0; \ 23908c2ecf20Sopenharmony_ciout: \ 23918c2ecf20Sopenharmony_ci mutex_unlock(&uea_mutex); \ 23928c2ecf20Sopenharmony_ci return ret; \ 23938c2ecf20Sopenharmony_ci} \ 23948c2ecf20Sopenharmony_ci \ 23958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(stat_##name) 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ciUEA_ATTR(mflags, 1); 23988c2ecf20Sopenharmony_ciUEA_ATTR(vidcpe, 0); 23998c2ecf20Sopenharmony_ciUEA_ATTR(usrate, 0); 24008c2ecf20Sopenharmony_ciUEA_ATTR(dsrate, 0); 24018c2ecf20Sopenharmony_ciUEA_ATTR(usattenuation, 0); 24028c2ecf20Sopenharmony_ciUEA_ATTR(dsattenuation, 0); 24038c2ecf20Sopenharmony_ciUEA_ATTR(usmargin, 0); 24048c2ecf20Sopenharmony_ciUEA_ATTR(dsmargin, 0); 24058c2ecf20Sopenharmony_ciUEA_ATTR(txflow, 0); 24068c2ecf20Sopenharmony_ciUEA_ATTR(rxflow, 0); 24078c2ecf20Sopenharmony_ciUEA_ATTR(uscorr, 0); 24088c2ecf20Sopenharmony_ciUEA_ATTR(dscorr, 0); 24098c2ecf20Sopenharmony_ciUEA_ATTR(usunc, 0); 24108c2ecf20Sopenharmony_ciUEA_ATTR(dsunc, 0); 24118c2ecf20Sopenharmony_ciUEA_ATTR(firmid, 0); 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci/* Retrieve the device End System Identifier (MAC) */ 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic int uea_getesi(struct uea_softc *sc, u_char *esi) 24168c2ecf20Sopenharmony_ci{ 24178c2ecf20Sopenharmony_ci unsigned char mac_str[2 * ETH_ALEN + 1]; 24188c2ecf20Sopenharmony_ci int i; 24198c2ecf20Sopenharmony_ci if (usb_string 24208c2ecf20Sopenharmony_ci (sc->usb_dev, sc->usb_dev->descriptor.iSerialNumber, mac_str, 24218c2ecf20Sopenharmony_ci sizeof(mac_str)) != 2 * ETH_ALEN) 24228c2ecf20Sopenharmony_ci return 1; 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 24258c2ecf20Sopenharmony_ci esi[i] = hex_to_bin(mac_str[2 * i]) * 16 + 24268c2ecf20Sopenharmony_ci hex_to_bin(mac_str[2 * i + 1]); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci return 0; 24298c2ecf20Sopenharmony_ci} 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci/* ATM stuff */ 24328c2ecf20Sopenharmony_cistatic int uea_atm_open(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci struct uea_softc *sc = usbatm->driver_data; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci return uea_getesi(sc, atm_dev->esi); 24378c2ecf20Sopenharmony_ci} 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_cistatic int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf) 24408c2ecf20Sopenharmony_ci{ 24418c2ecf20Sopenharmony_ci struct uea_softc *sc = usbatm->driver_data; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci wait_event_interruptible(sc->sync_q, IS_OPERATIONAL(sc)); 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci return 0; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_cistatic int claim_interface(struct usb_device *usb_dev, 24508c2ecf20Sopenharmony_ci struct usbatm_data *usbatm, int ifnum) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci int ret; 24538c2ecf20Sopenharmony_ci struct usb_interface *intf = usb_ifnum_to_if(usb_dev, ifnum); 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci if (!intf) { 24568c2ecf20Sopenharmony_ci uea_err(usb_dev, "interface %d not found\n", ifnum); 24578c2ecf20Sopenharmony_ci return -ENODEV; 24588c2ecf20Sopenharmony_ci } 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci ret = usb_driver_claim_interface(&uea_driver, intf, usbatm); 24618c2ecf20Sopenharmony_ci if (ret != 0) 24628c2ecf20Sopenharmony_ci uea_err(usb_dev, "can't claim interface %d, error %d\n", ifnum, 24638c2ecf20Sopenharmony_ci ret); 24648c2ecf20Sopenharmony_ci return ret; 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic struct attribute *uea_attrs[] = { 24688c2ecf20Sopenharmony_ci &dev_attr_stat_status.attr, 24698c2ecf20Sopenharmony_ci &dev_attr_stat_mflags.attr, 24708c2ecf20Sopenharmony_ci &dev_attr_stat_human_status.attr, 24718c2ecf20Sopenharmony_ci &dev_attr_stat_delin.attr, 24728c2ecf20Sopenharmony_ci &dev_attr_stat_vidcpe.attr, 24738c2ecf20Sopenharmony_ci &dev_attr_stat_usrate.attr, 24748c2ecf20Sopenharmony_ci &dev_attr_stat_dsrate.attr, 24758c2ecf20Sopenharmony_ci &dev_attr_stat_usattenuation.attr, 24768c2ecf20Sopenharmony_ci &dev_attr_stat_dsattenuation.attr, 24778c2ecf20Sopenharmony_ci &dev_attr_stat_usmargin.attr, 24788c2ecf20Sopenharmony_ci &dev_attr_stat_dsmargin.attr, 24798c2ecf20Sopenharmony_ci &dev_attr_stat_txflow.attr, 24808c2ecf20Sopenharmony_ci &dev_attr_stat_rxflow.attr, 24818c2ecf20Sopenharmony_ci &dev_attr_stat_uscorr.attr, 24828c2ecf20Sopenharmony_ci &dev_attr_stat_dscorr.attr, 24838c2ecf20Sopenharmony_ci &dev_attr_stat_usunc.attr, 24848c2ecf20Sopenharmony_ci &dev_attr_stat_dsunc.attr, 24858c2ecf20Sopenharmony_ci &dev_attr_stat_firmid.attr, 24868c2ecf20Sopenharmony_ci NULL, 24878c2ecf20Sopenharmony_ci}; 24888c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(uea); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_cistatic int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, 24918c2ecf20Sopenharmony_ci const struct usb_device_id *id) 24928c2ecf20Sopenharmony_ci{ 24938c2ecf20Sopenharmony_ci struct usb_device *usb = interface_to_usbdev(intf); 24948c2ecf20Sopenharmony_ci struct uea_softc *sc; 24958c2ecf20Sopenharmony_ci int ret, ifnum = intf->altsetting->desc.bInterfaceNumber; 24968c2ecf20Sopenharmony_ci unsigned int alt; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci uea_enters(usb); 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci /* interface 0 is for firmware/monitoring */ 25018c2ecf20Sopenharmony_ci if (ifnum != UEA_INTR_IFACE_NO) 25028c2ecf20Sopenharmony_ci return -ENODEV; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci usbatm->flags = (sync_wait[modem_index] ? 0 : UDSL_SKIP_HEAVY_INIT); 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci /* interface 1 is for outbound traffic */ 25078c2ecf20Sopenharmony_ci ret = claim_interface(usb, usbatm, UEA_US_IFACE_NO); 25088c2ecf20Sopenharmony_ci if (ret < 0) 25098c2ecf20Sopenharmony_ci return ret; 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci /* ADI930 has only 2 interfaces and inbound traffic is on interface 1 */ 25128c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(id) != ADI930) { 25138c2ecf20Sopenharmony_ci /* interface 2 is for inbound traffic */ 25148c2ecf20Sopenharmony_ci ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO); 25158c2ecf20Sopenharmony_ci if (ret < 0) 25168c2ecf20Sopenharmony_ci return ret; 25178c2ecf20Sopenharmony_ci } 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL); 25208c2ecf20Sopenharmony_ci if (!sc) 25218c2ecf20Sopenharmony_ci return -ENOMEM; 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci sc->usb_dev = usb; 25248c2ecf20Sopenharmony_ci usbatm->driver_data = sc; 25258c2ecf20Sopenharmony_ci sc->usbatm = usbatm; 25268c2ecf20Sopenharmony_ci sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0; 25278c2ecf20Sopenharmony_ci sc->driver_info = id->driver_info; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci /* first try to use module parameter */ 25308c2ecf20Sopenharmony_ci if (annex[sc->modem_index] == 1) 25318c2ecf20Sopenharmony_ci sc->annex = ANNEXA; 25328c2ecf20Sopenharmony_ci else if (annex[sc->modem_index] == 2) 25338c2ecf20Sopenharmony_ci sc->annex = ANNEXB; 25348c2ecf20Sopenharmony_ci /* try to autodetect annex */ 25358c2ecf20Sopenharmony_ci else if (sc->driver_info & AUTO_ANNEX_A) 25368c2ecf20Sopenharmony_ci sc->annex = ANNEXA; 25378c2ecf20Sopenharmony_ci else if (sc->driver_info & AUTO_ANNEX_B) 25388c2ecf20Sopenharmony_ci sc->annex = ANNEXB; 25398c2ecf20Sopenharmony_ci else 25408c2ecf20Sopenharmony_ci sc->annex = (le16_to_cpu 25418c2ecf20Sopenharmony_ci (sc->usb_dev->descriptor.bcdDevice) & 0x80) ? ANNEXB : ANNEXA; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci alt = altsetting[sc->modem_index]; 25448c2ecf20Sopenharmony_ci /* ADI930 don't support iso */ 25458c2ecf20Sopenharmony_ci if (UEA_CHIP_VERSION(id) != ADI930 && alt > 0) { 25468c2ecf20Sopenharmony_ci if (alt <= 8 && 25478c2ecf20Sopenharmony_ci usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) { 25488c2ecf20Sopenharmony_ci uea_dbg(usb, "set alternate %u for 2 interface\n", alt); 25498c2ecf20Sopenharmony_ci uea_info(usb, "using iso mode\n"); 25508c2ecf20Sopenharmony_ci usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ; 25518c2ecf20Sopenharmony_ci } else { 25528c2ecf20Sopenharmony_ci uea_err(usb, "setting alternate %u failed for " 25538c2ecf20Sopenharmony_ci "2 interface, using bulk mode\n", alt); 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci ret = uea_boot(sc, intf); 25588c2ecf20Sopenharmony_ci if (ret < 0) 25598c2ecf20Sopenharmony_ci goto error; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci return 0; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_cierror: 25648c2ecf20Sopenharmony_ci kfree(sc); 25658c2ecf20Sopenharmony_ci return ret; 25668c2ecf20Sopenharmony_ci} 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_cistatic void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) 25698c2ecf20Sopenharmony_ci{ 25708c2ecf20Sopenharmony_ci struct uea_softc *sc = usbatm->driver_data; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci uea_stop(sc); 25738c2ecf20Sopenharmony_ci kfree(sc); 25748c2ecf20Sopenharmony_ci} 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_cistatic struct usbatm_driver uea_usbatm_driver = { 25778c2ecf20Sopenharmony_ci .driver_name = "ueagle-atm", 25788c2ecf20Sopenharmony_ci .bind = uea_bind, 25798c2ecf20Sopenharmony_ci .atm_start = uea_atm_open, 25808c2ecf20Sopenharmony_ci .unbind = uea_unbind, 25818c2ecf20Sopenharmony_ci .heavy_init = uea_heavy, 25828c2ecf20Sopenharmony_ci .bulk_in = UEA_BULK_DATA_PIPE, 25838c2ecf20Sopenharmony_ci .bulk_out = UEA_BULK_DATA_PIPE, 25848c2ecf20Sopenharmony_ci .isoc_in = UEA_ISO_DATA_PIPE, 25858c2ecf20Sopenharmony_ci}; 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) 25888c2ecf20Sopenharmony_ci{ 25898c2ecf20Sopenharmony_ci struct usb_device *usb = interface_to_usbdev(intf); 25908c2ecf20Sopenharmony_ci int ret; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci uea_enters(usb); 25938c2ecf20Sopenharmony_ci uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n", 25948c2ecf20Sopenharmony_ci le16_to_cpu(usb->descriptor.idVendor), 25958c2ecf20Sopenharmony_ci le16_to_cpu(usb->descriptor.idProduct), 25968c2ecf20Sopenharmony_ci le16_to_cpu(usb->descriptor.bcdDevice), 25978c2ecf20Sopenharmony_ci chip_name[UEA_CHIP_VERSION(id)]); 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci usb_reset_device(usb); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci if (UEA_IS_PREFIRM(id)) 26028c2ecf20Sopenharmony_ci return uea_load_firmware(usb, UEA_CHIP_VERSION(id)); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver); 26058c2ecf20Sopenharmony_ci if (ret == 0) { 26068c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = usb_get_intfdata(intf); 26078c2ecf20Sopenharmony_ci struct uea_softc *sc = usbatm->driver_data; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci /* Ensure carrier is initialized to off as early as possible */ 26108c2ecf20Sopenharmony_ci UPDATE_ATM_SIGNAL(ATM_PHY_SIG_LOST); 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci /* Only start the worker thread when all init is done */ 26138c2ecf20Sopenharmony_ci wake_up_process(sc->kthread); 26148c2ecf20Sopenharmony_ci } 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci return ret; 26178c2ecf20Sopenharmony_ci} 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_cistatic void uea_disconnect(struct usb_interface *intf) 26208c2ecf20Sopenharmony_ci{ 26218c2ecf20Sopenharmony_ci struct usb_device *usb = interface_to_usbdev(intf); 26228c2ecf20Sopenharmony_ci int ifnum = intf->altsetting->desc.bInterfaceNumber; 26238c2ecf20Sopenharmony_ci uea_enters(usb); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci /* ADI930 has 2 interfaces and eagle 3 interfaces. 26268c2ecf20Sopenharmony_ci * Pre-firmware device has one interface 26278c2ecf20Sopenharmony_ci */ 26288c2ecf20Sopenharmony_ci if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) { 26298c2ecf20Sopenharmony_ci mutex_lock(&uea_mutex); 26308c2ecf20Sopenharmony_ci usbatm_usb_disconnect(intf); 26318c2ecf20Sopenharmony_ci mutex_unlock(&uea_mutex); 26328c2ecf20Sopenharmony_ci uea_info(usb, "ADSL device removed\n"); 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci uea_leaves(usb); 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci/* 26398c2ecf20Sopenharmony_ci * List of supported VID/PID 26408c2ecf20Sopenharmony_ci */ 26418c2ecf20Sopenharmony_cistatic const struct usb_device_id uea_ids[] = { 26428c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM), 26438c2ecf20Sopenharmony_ci .driver_info = ADI930 | PREFIRM}, 26448c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM), 26458c2ecf20Sopenharmony_ci .driver_info = ADI930 | PSTFIRM}, 26468c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM), 26478c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 26488c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM), 26498c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM}, 26508c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM), 26518c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PREFIRM}, 26528c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM), 26538c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PSTFIRM}, 26548c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM), 26558c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PREFIRM}, 26568c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM), 26578c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PSTFIRM}, 26588c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM), 26598c2ecf20Sopenharmony_ci .driver_info = EAGLE_III | PREFIRM}, 26608c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM), 26618c2ecf20Sopenharmony_ci .driver_info = EAGLE_III | PSTFIRM}, 26628c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM), 26638c2ecf20Sopenharmony_ci .driver_info = EAGLE_IV | PREFIRM}, 26648c2ecf20Sopenharmony_ci {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM), 26658c2ecf20Sopenharmony_ci .driver_info = EAGLE_IV | PSTFIRM}, 26668c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM), 26678c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 26688c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM), 26698c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, 26708c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM), 26718c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 26728c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM), 26738c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, 26748c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM), 26758c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PREFIRM}, 26768c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM), 26778c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A}, 26788c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM), 26798c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PREFIRM}, 26808c2ecf20Sopenharmony_ci {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM), 26818c2ecf20Sopenharmony_ci .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B}, 26828c2ecf20Sopenharmony_ci {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), 26838c2ecf20Sopenharmony_ci .driver_info = ADI930 | PREFIRM}, 26848c2ecf20Sopenharmony_ci {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), 26858c2ecf20Sopenharmony_ci .driver_info = ADI930 | PSTFIRM}, 26868c2ecf20Sopenharmony_ci {USB_DEVICE(ELSA_VID, ELSA_PID_A_PREFIRM), 26878c2ecf20Sopenharmony_ci .driver_info = ADI930 | PREFIRM}, 26888c2ecf20Sopenharmony_ci {USB_DEVICE(ELSA_VID, ELSA_PID_A_PSTFIRM), 26898c2ecf20Sopenharmony_ci .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A}, 26908c2ecf20Sopenharmony_ci {USB_DEVICE(ELSA_VID, ELSA_PID_B_PREFIRM), 26918c2ecf20Sopenharmony_ci .driver_info = ADI930 | PREFIRM}, 26928c2ecf20Sopenharmony_ci {USB_DEVICE(ELSA_VID, ELSA_PID_B_PSTFIRM), 26938c2ecf20Sopenharmony_ci .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B}, 26948c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), 26958c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 26968c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), 26978c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, 26988c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), 26998c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 27008c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), 27018c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, 27028c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM), 27038c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 27048c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM), 27058c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A}, 27068c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM), 27078c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PREFIRM}, 27088c2ecf20Sopenharmony_ci {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM), 27098c2ecf20Sopenharmony_ci .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B}, 27108c2ecf20Sopenharmony_ci {} 27118c2ecf20Sopenharmony_ci}; 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci/* 27148c2ecf20Sopenharmony_ci * USB driver descriptor 27158c2ecf20Sopenharmony_ci */ 27168c2ecf20Sopenharmony_cistatic struct usb_driver uea_driver = { 27178c2ecf20Sopenharmony_ci .name = "ueagle-atm", 27188c2ecf20Sopenharmony_ci .id_table = uea_ids, 27198c2ecf20Sopenharmony_ci .probe = uea_probe, 27208c2ecf20Sopenharmony_ci .disconnect = uea_disconnect, 27218c2ecf20Sopenharmony_ci .dev_groups = uea_groups, 27228c2ecf20Sopenharmony_ci}; 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, uea_ids); 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_cimodule_usb_driver(uea_driver); 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka"); 27298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver"); 27308c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 27318c2ecf20Sopenharmony_ciMODULE_FIRMWARE(EAGLE_FIRMWARE); 27328c2ecf20Sopenharmony_ciMODULE_FIRMWARE(ADI930_FIRMWARE); 27338c2ecf20Sopenharmony_ciMODULE_FIRMWARE(EAGLE_I_FIRMWARE); 27348c2ecf20Sopenharmony_ciMODULE_FIRMWARE(EAGLE_II_FIRMWARE); 27358c2ecf20Sopenharmony_ciMODULE_FIRMWARE(EAGLE_III_FIRMWARE); 27368c2ecf20Sopenharmony_ciMODULE_FIRMWARE(EAGLE_IV_FIRMWARE); 27378c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DSP4I_FIRMWARE); 27388c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DSP4P_FIRMWARE); 27398c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DSP9I_FIRMWARE); 27408c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DSP9P_FIRMWARE); 27418c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DSPEI_FIRMWARE); 27428c2ecf20Sopenharmony_ciMODULE_FIRMWARE(DSPEP_FIRMWARE); 27438c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FPGA930_FIRMWARE); 27448c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV4P_FIRMWARE); 27458c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV4PV2_FIRMWARE); 27468c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV4I_FIRMWARE); 27478c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV4IV2_FIRMWARE); 27488c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV9P_FIRMWARE); 27498c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV9PV2_FIRMWARE); 27508c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV9I_FIRMWARE); 27518c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMV9IV2_FIRMWARE); 27528c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMVEP_FIRMWARE); 27538c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMVEPV2_FIRMWARE); 27548c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMVEI_FIRMWARE); 27558c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CMVEIV2_FIRMWARE); 2756