18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* sb1000.c: A General Instruments SB1000 driver for linux. */ 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci Written 1998 by Franco Venturi. 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Copyright 1998 by Franco Venturi. 78c2ecf20Sopenharmony_ci Copyright 1994,1995 by Donald Becker. 88c2ecf20Sopenharmony_ci Copyright 1993 United States Government as represented by the 98c2ecf20Sopenharmony_ci Director, National Security Agency. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci This driver is for the General Instruments SB1000 (internal SURFboard) 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci The author may be reached as fventuri@mediaone.net 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci Changes: 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 981115 Steven Hirsch <shirsch@adelphia.net> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci Linus changed the timer interface. Should work on all recent 218c2ecf20Sopenharmony_ci development kernels. 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 980608 Steven Hirsch <shirsch@adelphia.net> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci Small changes to make it work with 2.1.x kernels. Hopefully, 268c2ecf20Sopenharmony_ci nothing major will change before official release of Linux 2.2. 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci Merged with 2.2 - Alan Cox 298c2ecf20Sopenharmony_ci*/ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic char version[] = "sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)\n"; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/sched.h> 368c2ecf20Sopenharmony_ci#include <linux/string.h> 378c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 388c2ecf20Sopenharmony_ci#include <linux/errno.h> 398c2ecf20Sopenharmony_ci#include <linux/if_cablemodem.h> /* for SIOGCM/SIOSCM stuff */ 408c2ecf20Sopenharmony_ci#include <linux/in.h> 418c2ecf20Sopenharmony_ci#include <linux/ioport.h> 428c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 438c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 448c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 458c2ecf20Sopenharmony_ci#include <linux/delay.h> /* for udelay() */ 468c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 478c2ecf20Sopenharmony_ci#include <linux/pnp.h> 488c2ecf20Sopenharmony_ci#include <linux/init.h> 498c2ecf20Sopenharmony_ci#include <linux/bitops.h> 508c2ecf20Sopenharmony_ci#include <linux/gfp.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <asm/io.h> 538c2ecf20Sopenharmony_ci#include <asm/processor.h> 548c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#ifdef SB1000_DEBUG 578c2ecf20Sopenharmony_cistatic int sb1000_debug = SB1000_DEBUG; 588c2ecf20Sopenharmony_ci#else 598c2ecf20Sopenharmony_cistatic const int sb1000_debug = 1; 608c2ecf20Sopenharmony_ci#endif 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const int SB1000_IO_EXTENT = 8; 638c2ecf20Sopenharmony_ci/* SB1000 Maximum Receive Unit */ 648c2ecf20Sopenharmony_cistatic const int SB1000_MRU = 1500; /* octects */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define NPIDS 4 678c2ecf20Sopenharmony_cistruct sb1000_private { 688c2ecf20Sopenharmony_ci struct sk_buff *rx_skb[NPIDS]; 698c2ecf20Sopenharmony_ci short rx_dlen[NPIDS]; 708c2ecf20Sopenharmony_ci unsigned int rx_frames; 718c2ecf20Sopenharmony_ci short rx_error_count; 728c2ecf20Sopenharmony_ci short rx_error_dpc_count; 738c2ecf20Sopenharmony_ci unsigned char rx_session_id[NPIDS]; 748c2ecf20Sopenharmony_ci unsigned char rx_frame_id[NPIDS]; 758c2ecf20Sopenharmony_ci unsigned char rx_pkt_type[NPIDS]; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* prototypes for Linux interface */ 798c2ecf20Sopenharmony_ciextern int sb1000_probe(struct net_device *dev); 808c2ecf20Sopenharmony_cistatic int sb1000_open(struct net_device *dev); 818c2ecf20Sopenharmony_cistatic int sb1000_dev_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd); 828c2ecf20Sopenharmony_cistatic netdev_tx_t sb1000_start_xmit(struct sk_buff *skb, 838c2ecf20Sopenharmony_ci struct net_device *dev); 848c2ecf20Sopenharmony_cistatic irqreturn_t sb1000_interrupt(int irq, void *dev_id); 858c2ecf20Sopenharmony_cistatic int sb1000_close(struct net_device *dev); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* SB1000 hardware routines to be used during open/configuration phases */ 898c2ecf20Sopenharmony_cistatic int card_wait_for_busy_clear(const int ioaddr[], 908c2ecf20Sopenharmony_ci const char* name); 918c2ecf20Sopenharmony_cistatic int card_wait_for_ready(const int ioaddr[], const char* name, 928c2ecf20Sopenharmony_ci unsigned char in[]); 938c2ecf20Sopenharmony_cistatic int card_send_command(const int ioaddr[], const char* name, 948c2ecf20Sopenharmony_ci const unsigned char out[], unsigned char in[]); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* SB1000 hardware routines to be used during frame rx interrupt */ 978c2ecf20Sopenharmony_cistatic int sb1000_wait_for_ready(const int ioaddr[], const char* name); 988c2ecf20Sopenharmony_cistatic int sb1000_wait_for_ready_clear(const int ioaddr[], 998c2ecf20Sopenharmony_ci const char* name); 1008c2ecf20Sopenharmony_cistatic void sb1000_send_command(const int ioaddr[], const char* name, 1018c2ecf20Sopenharmony_ci const unsigned char out[]); 1028c2ecf20Sopenharmony_cistatic void sb1000_read_status(const int ioaddr[], unsigned char in[]); 1038c2ecf20Sopenharmony_cistatic void sb1000_issue_read_command(const int ioaddr[], 1048c2ecf20Sopenharmony_ci const char* name); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* SB1000 commands for open/configuration */ 1078c2ecf20Sopenharmony_cistatic int sb1000_reset(const int ioaddr[], const char* name); 1088c2ecf20Sopenharmony_cistatic int sb1000_check_CRC(const int ioaddr[], const char* name); 1098c2ecf20Sopenharmony_cistatic inline int sb1000_start_get_set_command(const int ioaddr[], 1108c2ecf20Sopenharmony_ci const char* name); 1118c2ecf20Sopenharmony_cistatic int sb1000_end_get_set_command(const int ioaddr[], 1128c2ecf20Sopenharmony_ci const char* name); 1138c2ecf20Sopenharmony_cistatic int sb1000_activate(const int ioaddr[], const char* name); 1148c2ecf20Sopenharmony_cistatic int sb1000_get_firmware_version(const int ioaddr[], 1158c2ecf20Sopenharmony_ci const char* name, unsigned char version[], int do_end); 1168c2ecf20Sopenharmony_cistatic int sb1000_get_frequency(const int ioaddr[], const char* name, 1178c2ecf20Sopenharmony_ci int* frequency); 1188c2ecf20Sopenharmony_cistatic int sb1000_set_frequency(const int ioaddr[], const char* name, 1198c2ecf20Sopenharmony_ci int frequency); 1208c2ecf20Sopenharmony_cistatic int sb1000_get_PIDs(const int ioaddr[], const char* name, 1218c2ecf20Sopenharmony_ci short PID[]); 1228c2ecf20Sopenharmony_cistatic int sb1000_set_PIDs(const int ioaddr[], const char* name, 1238c2ecf20Sopenharmony_ci const short PID[]); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* SB1000 commands for frame rx interrupt */ 1268c2ecf20Sopenharmony_cistatic int sb1000_rx(struct net_device *dev); 1278c2ecf20Sopenharmony_cistatic void sb1000_error_dpc(struct net_device *dev); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct pnp_device_id sb1000_pnp_ids[] = { 1308c2ecf20Sopenharmony_ci { "GIC1000", 0 }, 1318c2ecf20Sopenharmony_ci { "", 0 } 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, sb1000_pnp_ids); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic const struct net_device_ops sb1000_netdev_ops = { 1368c2ecf20Sopenharmony_ci .ndo_open = sb1000_open, 1378c2ecf20Sopenharmony_ci .ndo_start_xmit = sb1000_start_xmit, 1388c2ecf20Sopenharmony_ci .ndo_do_ioctl = sb1000_dev_ioctl, 1398c2ecf20Sopenharmony_ci .ndo_stop = sb1000_close, 1408c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 1418c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int 1458c2ecf20Sopenharmony_cisb1000_probe_one(struct pnp_dev *pdev, const struct pnp_device_id *id) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct net_device *dev; 1488c2ecf20Sopenharmony_ci unsigned short ioaddr[2], irq; 1498c2ecf20Sopenharmony_ci unsigned int serial_number; 1508c2ecf20Sopenharmony_ci int error = -ENODEV; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (pnp_device_attach(pdev) < 0) 1538c2ecf20Sopenharmony_ci return -ENODEV; 1548c2ecf20Sopenharmony_ci if (pnp_activate_dev(pdev) < 0) 1558c2ecf20Sopenharmony_ci goto out_detach; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!pnp_port_valid(pdev, 0) || !pnp_port_valid(pdev, 1)) 1588c2ecf20Sopenharmony_ci goto out_disable; 1598c2ecf20Sopenharmony_ci if (!pnp_irq_valid(pdev, 0)) 1608c2ecf20Sopenharmony_ci goto out_disable; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci serial_number = pdev->card->serial; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ioaddr[0] = pnp_port_start(pdev, 0); 1658c2ecf20Sopenharmony_ci ioaddr[1] = pnp_port_start(pdev, 0); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci irq = pnp_irq(pdev, 0); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!request_region(ioaddr[0], 16, "sb1000")) 1708c2ecf20Sopenharmony_ci goto out_disable; 1718c2ecf20Sopenharmony_ci if (!request_region(ioaddr[1], 16, "sb1000")) 1728c2ecf20Sopenharmony_ci goto out_release_region0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct sb1000_private)); 1758c2ecf20Sopenharmony_ci if (!dev) { 1768c2ecf20Sopenharmony_ci error = -ENOMEM; 1778c2ecf20Sopenharmony_ci goto out_release_regions; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dev->base_addr = ioaddr[0]; 1828c2ecf20Sopenharmony_ci /* mem_start holds the second I/O address */ 1838c2ecf20Sopenharmony_ci dev->mem_start = ioaddr[1]; 1848c2ecf20Sopenharmony_ci dev->irq = irq; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (sb1000_debug > 0) 1878c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s: sb1000 at (%#3.3lx,%#3.3lx), " 1888c2ecf20Sopenharmony_ci "S/N %#8.8x, IRQ %d.\n", dev->name, dev->base_addr, 1898c2ecf20Sopenharmony_ci dev->mem_start, serial_number, dev->irq); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * The SB1000 is an rx-only cable modem device. The uplink is a modem 1938c2ecf20Sopenharmony_ci * and we do not want to arp on it. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci dev->flags = IFF_POINTOPOINT|IFF_NOARP; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (sb1000_debug > 0) 2008c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s", version); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci dev->netdev_ops = &sb1000_netdev_ops; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* hardware address is 0:0:serial_number */ 2058c2ecf20Sopenharmony_ci dev->dev_addr[2] = serial_number >> 24 & 0xff; 2068c2ecf20Sopenharmony_ci dev->dev_addr[3] = serial_number >> 16 & 0xff; 2078c2ecf20Sopenharmony_ci dev->dev_addr[4] = serial_number >> 8 & 0xff; 2088c2ecf20Sopenharmony_ci dev->dev_addr[5] = serial_number >> 0 & 0xff; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci pnp_set_drvdata(pdev, dev); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci error = register_netdev(dev); 2138c2ecf20Sopenharmony_ci if (error) 2148c2ecf20Sopenharmony_ci goto out_free_netdev; 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci out_free_netdev: 2188c2ecf20Sopenharmony_ci free_netdev(dev); 2198c2ecf20Sopenharmony_ci out_release_regions: 2208c2ecf20Sopenharmony_ci release_region(ioaddr[1], 16); 2218c2ecf20Sopenharmony_ci out_release_region0: 2228c2ecf20Sopenharmony_ci release_region(ioaddr[0], 16); 2238c2ecf20Sopenharmony_ci out_disable: 2248c2ecf20Sopenharmony_ci pnp_disable_dev(pdev); 2258c2ecf20Sopenharmony_ci out_detach: 2268c2ecf20Sopenharmony_ci pnp_device_detach(pdev); 2278c2ecf20Sopenharmony_ci return error; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void 2318c2ecf20Sopenharmony_cisb1000_remove_one(struct pnp_dev *pdev) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct net_device *dev = pnp_get_drvdata(pdev); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci unregister_netdev(dev); 2368c2ecf20Sopenharmony_ci release_region(dev->base_addr, 16); 2378c2ecf20Sopenharmony_ci release_region(dev->mem_start, 16); 2388c2ecf20Sopenharmony_ci free_netdev(dev); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic struct pnp_driver sb1000_driver = { 2428c2ecf20Sopenharmony_ci .name = "sb1000", 2438c2ecf20Sopenharmony_ci .id_table = sb1000_pnp_ids, 2448c2ecf20Sopenharmony_ci .probe = sb1000_probe_one, 2458c2ecf20Sopenharmony_ci .remove = sb1000_remove_one, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * SB1000 hardware routines to be used during open/configuration phases 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic const int TimeOutJiffies = (875 * HZ) / 100; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* Card Wait For Busy Clear (cannot be used during an interrupt) */ 2568c2ecf20Sopenharmony_cistatic int 2578c2ecf20Sopenharmony_cicard_wait_for_busy_clear(const int ioaddr[], const char* name) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci unsigned char a; 2608c2ecf20Sopenharmony_ci unsigned long timeout; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci a = inb(ioaddr[0] + 7); 2638c2ecf20Sopenharmony_ci timeout = jiffies + TimeOutJiffies; 2648c2ecf20Sopenharmony_ci while (a & 0x80 || a & 0x40) { 2658c2ecf20Sopenharmony_ci /* a little sleep */ 2668c2ecf20Sopenharmony_ci yield(); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci a = inb(ioaddr[0] + 7); 2698c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) { 2708c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: card_wait_for_busy_clear timeout\n", 2718c2ecf20Sopenharmony_ci name); 2728c2ecf20Sopenharmony_ci return -ETIME; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* Card Wait For Ready (cannot be used during an interrupt) */ 2808c2ecf20Sopenharmony_cistatic int 2818c2ecf20Sopenharmony_cicard_wait_for_ready(const int ioaddr[], const char* name, unsigned char in[]) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci unsigned char a; 2848c2ecf20Sopenharmony_ci unsigned long timeout; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci a = inb(ioaddr[1] + 6); 2878c2ecf20Sopenharmony_ci timeout = jiffies + TimeOutJiffies; 2888c2ecf20Sopenharmony_ci while (a & 0x80 || !(a & 0x40)) { 2898c2ecf20Sopenharmony_ci /* a little sleep */ 2908c2ecf20Sopenharmony_ci yield(); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci a = inb(ioaddr[1] + 6); 2938c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) { 2948c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: card_wait_for_ready timeout\n", 2958c2ecf20Sopenharmony_ci name); 2968c2ecf20Sopenharmony_ci return -ETIME; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci in[1] = inb(ioaddr[0] + 1); 3018c2ecf20Sopenharmony_ci in[2] = inb(ioaddr[0] + 2); 3028c2ecf20Sopenharmony_ci in[3] = inb(ioaddr[0] + 3); 3038c2ecf20Sopenharmony_ci in[4] = inb(ioaddr[0] + 4); 3048c2ecf20Sopenharmony_ci in[0] = inb(ioaddr[0] + 5); 3058c2ecf20Sopenharmony_ci in[6] = inb(ioaddr[0] + 6); 3068c2ecf20Sopenharmony_ci in[5] = inb(ioaddr[1] + 6); 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* Card Send Command (cannot be used during an interrupt) */ 3118c2ecf20Sopenharmony_cistatic int 3128c2ecf20Sopenharmony_cicard_send_command(const int ioaddr[], const char* name, 3138c2ecf20Sopenharmony_ci const unsigned char out[], unsigned char in[]) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci int status; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if ((status = card_wait_for_busy_clear(ioaddr, name))) 3188c2ecf20Sopenharmony_ci return status; 3198c2ecf20Sopenharmony_ci outb(0xa0, ioaddr[0] + 6); 3208c2ecf20Sopenharmony_ci outb(out[2], ioaddr[0] + 1); 3218c2ecf20Sopenharmony_ci outb(out[3], ioaddr[0] + 2); 3228c2ecf20Sopenharmony_ci outb(out[4], ioaddr[0] + 3); 3238c2ecf20Sopenharmony_ci outb(out[5], ioaddr[0] + 4); 3248c2ecf20Sopenharmony_ci outb(out[1], ioaddr[0] + 5); 3258c2ecf20Sopenharmony_ci outb(0xa0, ioaddr[0] + 6); 3268c2ecf20Sopenharmony_ci outb(out[0], ioaddr[0] + 7); 3278c2ecf20Sopenharmony_ci if (out[0] != 0x20 && out[0] != 0x30) { 3288c2ecf20Sopenharmony_ci if ((status = card_wait_for_ready(ioaddr, name, in))) 3298c2ecf20Sopenharmony_ci return status; 3308c2ecf20Sopenharmony_ci inb(ioaddr[0] + 7); 3318c2ecf20Sopenharmony_ci if (sb1000_debug > 3) 3328c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: card_send_command " 3338c2ecf20Sopenharmony_ci "out: %02x%02x%02x%02x%02x%02x " 3348c2ecf20Sopenharmony_ci "in: %02x%02x%02x%02x%02x%02x%02x\n", name, 3358c2ecf20Sopenharmony_ci out[0], out[1], out[2], out[3], out[4], out[5], 3368c2ecf20Sopenharmony_ci in[0], in[1], in[2], in[3], in[4], in[5], in[6]); 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci if (sb1000_debug > 3) 3398c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: card_send_command " 3408c2ecf20Sopenharmony_ci "out: %02x%02x%02x%02x%02x%02x\n", name, 3418c2ecf20Sopenharmony_ci out[0], out[1], out[2], out[3], out[4], out[5]); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (out[1] != 0x1b) { 3458c2ecf20Sopenharmony_ci if (out[0] >= 0x80 && in[0] != (out[1] | 0x80)) 3468c2ecf20Sopenharmony_ci return -EIO; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* 3538c2ecf20Sopenharmony_ci * SB1000 hardware routines to be used during frame rx interrupt 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_cistatic const int Sb1000TimeOutJiffies = 7 * HZ; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* Card Wait For Ready (to be used during frame rx) */ 3588c2ecf20Sopenharmony_cistatic int 3598c2ecf20Sopenharmony_cisb1000_wait_for_ready(const int ioaddr[], const char* name) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci unsigned long timeout; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci timeout = jiffies + Sb1000TimeOutJiffies; 3648c2ecf20Sopenharmony_ci while (inb(ioaddr[1] + 6) & 0x80) { 3658c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) { 3668c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: sb1000_wait_for_ready timeout\n", 3678c2ecf20Sopenharmony_ci name); 3688c2ecf20Sopenharmony_ci return -ETIME; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci timeout = jiffies + Sb1000TimeOutJiffies; 3728c2ecf20Sopenharmony_ci while (!(inb(ioaddr[1] + 6) & 0x40)) { 3738c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) { 3748c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: sb1000_wait_for_ready timeout\n", 3758c2ecf20Sopenharmony_ci name); 3768c2ecf20Sopenharmony_ci return -ETIME; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci inb(ioaddr[0] + 7); 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* Card Wait For Ready Clear (to be used during frame rx) */ 3848c2ecf20Sopenharmony_cistatic int 3858c2ecf20Sopenharmony_cisb1000_wait_for_ready_clear(const int ioaddr[], const char* name) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci unsigned long timeout; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci timeout = jiffies + Sb1000TimeOutJiffies; 3908c2ecf20Sopenharmony_ci while (inb(ioaddr[1] + 6) & 0x80) { 3918c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) { 3928c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: sb1000_wait_for_ready_clear timeout\n", 3938c2ecf20Sopenharmony_ci name); 3948c2ecf20Sopenharmony_ci return -ETIME; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci timeout = jiffies + Sb1000TimeOutJiffies; 3988c2ecf20Sopenharmony_ci while (inb(ioaddr[1] + 6) & 0x40) { 3998c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) { 4008c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: sb1000_wait_for_ready_clear timeout\n", 4018c2ecf20Sopenharmony_ci name); 4028c2ecf20Sopenharmony_ci return -ETIME; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* Card Send Command (to be used during frame rx) */ 4098c2ecf20Sopenharmony_cistatic void 4108c2ecf20Sopenharmony_cisb1000_send_command(const int ioaddr[], const char* name, 4118c2ecf20Sopenharmony_ci const unsigned char out[]) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci outb(out[2], ioaddr[0] + 1); 4148c2ecf20Sopenharmony_ci outb(out[3], ioaddr[0] + 2); 4158c2ecf20Sopenharmony_ci outb(out[4], ioaddr[0] + 3); 4168c2ecf20Sopenharmony_ci outb(out[5], ioaddr[0] + 4); 4178c2ecf20Sopenharmony_ci outb(out[1], ioaddr[0] + 5); 4188c2ecf20Sopenharmony_ci outb(out[0], ioaddr[0] + 7); 4198c2ecf20Sopenharmony_ci if (sb1000_debug > 3) 4208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: sb1000_send_command out: %02x%02x%02x%02x" 4218c2ecf20Sopenharmony_ci "%02x%02x\n", name, out[0], out[1], out[2], out[3], out[4], out[5]); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/* Card Read Status (to be used during frame rx) */ 4258c2ecf20Sopenharmony_cistatic void 4268c2ecf20Sopenharmony_cisb1000_read_status(const int ioaddr[], unsigned char in[]) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci in[1] = inb(ioaddr[0] + 1); 4298c2ecf20Sopenharmony_ci in[2] = inb(ioaddr[0] + 2); 4308c2ecf20Sopenharmony_ci in[3] = inb(ioaddr[0] + 3); 4318c2ecf20Sopenharmony_ci in[4] = inb(ioaddr[0] + 4); 4328c2ecf20Sopenharmony_ci in[0] = inb(ioaddr[0] + 5); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/* Issue Read Command (to be used during frame rx) */ 4368c2ecf20Sopenharmony_cistatic void 4378c2ecf20Sopenharmony_cisb1000_issue_read_command(const int ioaddr[], const char* name) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x20, 0x00, 0x00, 0x01, 0x00, 0x00}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci sb1000_wait_for_ready_clear(ioaddr, name); 4428c2ecf20Sopenharmony_ci outb(0xa0, ioaddr[0] + 6); 4438c2ecf20Sopenharmony_ci sb1000_send_command(ioaddr, name, Command0); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * SB1000 commands for open/configuration 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci/* reset SB1000 card */ 4518c2ecf20Sopenharmony_cistatic int 4528c2ecf20Sopenharmony_cisb1000_reset(const int ioaddr[], const char* name) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x16, 0x00, 0x00, 0x00, 0x00}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci unsigned char st[7]; 4578c2ecf20Sopenharmony_ci int port, status; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci port = ioaddr[1] + 6; 4608c2ecf20Sopenharmony_ci outb(0x4, port); 4618c2ecf20Sopenharmony_ci inb(port); 4628c2ecf20Sopenharmony_ci udelay(1000); 4638c2ecf20Sopenharmony_ci outb(0x0, port); 4648c2ecf20Sopenharmony_ci inb(port); 4658c2ecf20Sopenharmony_ci ssleep(1); 4668c2ecf20Sopenharmony_ci outb(0x4, port); 4678c2ecf20Sopenharmony_ci inb(port); 4688c2ecf20Sopenharmony_ci udelay(1000); 4698c2ecf20Sopenharmony_ci outb(0x0, port); 4708c2ecf20Sopenharmony_ci inb(port); 4718c2ecf20Sopenharmony_ci udelay(0); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 4748c2ecf20Sopenharmony_ci return status; 4758c2ecf20Sopenharmony_ci if (st[3] != 0xf0) 4768c2ecf20Sopenharmony_ci return -EIO; 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* check SB1000 firmware CRC */ 4818c2ecf20Sopenharmony_cistatic int 4828c2ecf20Sopenharmony_cisb1000_check_CRC(const int ioaddr[], const char* name) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x1f, 0x00, 0x00, 0x00, 0x00}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci unsigned char st[7]; 4878c2ecf20Sopenharmony_ci int status; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* check CRC */ 4908c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 4918c2ecf20Sopenharmony_ci return status; 4928c2ecf20Sopenharmony_ci if (st[1] != st[3] || st[2] != st[4]) 4938c2ecf20Sopenharmony_ci return -EIO; 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic inline int 4988c2ecf20Sopenharmony_cisb1000_start_get_set_command(const int ioaddr[], const char* name) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x1b, 0x00, 0x00, 0x00, 0x00}; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci unsigned char st[7]; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return card_send_command(ioaddr, name, Command0, st); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int 5088c2ecf20Sopenharmony_cisb1000_end_get_set_command(const int ioaddr[], const char* name) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x1b, 0x02, 0x00, 0x00, 0x00}; 5118c2ecf20Sopenharmony_ci static const unsigned char Command1[6] = {0x20, 0x00, 0x00, 0x00, 0x00, 0x00}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci unsigned char st[7]; 5148c2ecf20Sopenharmony_ci int status; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 5178c2ecf20Sopenharmony_ci return status; 5188c2ecf20Sopenharmony_ci return card_send_command(ioaddr, name, Command1, st); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int 5228c2ecf20Sopenharmony_cisb1000_activate(const int ioaddr[], const char* name) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x11, 0x00, 0x00, 0x00, 0x00}; 5258c2ecf20Sopenharmony_ci static const unsigned char Command1[6] = {0x80, 0x16, 0x00, 0x00, 0x00, 0x00}; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci unsigned char st[7]; 5288c2ecf20Sopenharmony_ci int status; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ssleep(1); 5318c2ecf20Sopenharmony_ci status = card_send_command(ioaddr, name, Command0, st); 5328c2ecf20Sopenharmony_ci if (status) 5338c2ecf20Sopenharmony_ci return status; 5348c2ecf20Sopenharmony_ci status = card_send_command(ioaddr, name, Command1, st); 5358c2ecf20Sopenharmony_ci if (status) 5368c2ecf20Sopenharmony_ci return status; 5378c2ecf20Sopenharmony_ci if (st[3] != 0xf1) { 5388c2ecf20Sopenharmony_ci status = sb1000_start_get_set_command(ioaddr, name); 5398c2ecf20Sopenharmony_ci if (status) 5408c2ecf20Sopenharmony_ci return status; 5418c2ecf20Sopenharmony_ci return -EIO; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci udelay(1000); 5448c2ecf20Sopenharmony_ci return sb1000_start_get_set_command(ioaddr, name); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* get SB1000 firmware version */ 5488c2ecf20Sopenharmony_cistatic int 5498c2ecf20Sopenharmony_cisb1000_get_firmware_version(const int ioaddr[], const char* name, 5508c2ecf20Sopenharmony_ci unsigned char version[], int do_end) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x23, 0x00, 0x00, 0x00, 0x00}; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci unsigned char st[7]; 5558c2ecf20Sopenharmony_ci int status; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if ((status = sb1000_start_get_set_command(ioaddr, name))) 5588c2ecf20Sopenharmony_ci return status; 5598c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 5608c2ecf20Sopenharmony_ci return status; 5618c2ecf20Sopenharmony_ci if (st[0] != 0xa3) 5628c2ecf20Sopenharmony_ci return -EIO; 5638c2ecf20Sopenharmony_ci version[0] = st[1]; 5648c2ecf20Sopenharmony_ci version[1] = st[2]; 5658c2ecf20Sopenharmony_ci if (do_end) 5668c2ecf20Sopenharmony_ci return sb1000_end_get_set_command(ioaddr, name); 5678c2ecf20Sopenharmony_ci else 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/* get SB1000 frequency */ 5728c2ecf20Sopenharmony_cistatic int 5738c2ecf20Sopenharmony_cisb1000_get_frequency(const int ioaddr[], const char* name, int* frequency) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x44, 0x00, 0x00, 0x00, 0x00}; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci unsigned char st[7]; 5788c2ecf20Sopenharmony_ci int status; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci udelay(1000); 5818c2ecf20Sopenharmony_ci if ((status = sb1000_start_get_set_command(ioaddr, name))) 5828c2ecf20Sopenharmony_ci return status; 5838c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 5848c2ecf20Sopenharmony_ci return status; 5858c2ecf20Sopenharmony_ci *frequency = ((st[1] << 8 | st[2]) << 8 | st[3]) << 8 | st[4]; 5868c2ecf20Sopenharmony_ci return sb1000_end_get_set_command(ioaddr, name); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/* set SB1000 frequency */ 5908c2ecf20Sopenharmony_cistatic int 5918c2ecf20Sopenharmony_cisb1000_set_frequency(const int ioaddr[], const char* name, int frequency) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci unsigned char st[7]; 5948c2ecf20Sopenharmony_ci int status; 5958c2ecf20Sopenharmony_ci unsigned char Command0[6] = {0x80, 0x29, 0x00, 0x00, 0x00, 0x00}; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci const int FrequencyLowerLimit = 57000; 5988c2ecf20Sopenharmony_ci const int FrequencyUpperLimit = 804000; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (frequency < FrequencyLowerLimit || frequency > FrequencyUpperLimit) { 6018c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: frequency chosen (%d kHz) is not in the range " 6028c2ecf20Sopenharmony_ci "[%d,%d] kHz\n", name, frequency, FrequencyLowerLimit, 6038c2ecf20Sopenharmony_ci FrequencyUpperLimit); 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci udelay(1000); 6078c2ecf20Sopenharmony_ci if ((status = sb1000_start_get_set_command(ioaddr, name))) 6088c2ecf20Sopenharmony_ci return status; 6098c2ecf20Sopenharmony_ci Command0[5] = frequency & 0xff; 6108c2ecf20Sopenharmony_ci frequency >>= 8; 6118c2ecf20Sopenharmony_ci Command0[4] = frequency & 0xff; 6128c2ecf20Sopenharmony_ci frequency >>= 8; 6138c2ecf20Sopenharmony_ci Command0[3] = frequency & 0xff; 6148c2ecf20Sopenharmony_ci frequency >>= 8; 6158c2ecf20Sopenharmony_ci Command0[2] = frequency & 0xff; 6168c2ecf20Sopenharmony_ci return card_send_command(ioaddr, name, Command0, st); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/* get SB1000 PIDs */ 6208c2ecf20Sopenharmony_cistatic int 6218c2ecf20Sopenharmony_cisb1000_get_PIDs(const int ioaddr[], const char* name, short PID[]) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x40, 0x00, 0x00, 0x00, 0x00}; 6248c2ecf20Sopenharmony_ci static const unsigned char Command1[6] = {0x80, 0x41, 0x00, 0x00, 0x00, 0x00}; 6258c2ecf20Sopenharmony_ci static const unsigned char Command2[6] = {0x80, 0x42, 0x00, 0x00, 0x00, 0x00}; 6268c2ecf20Sopenharmony_ci static const unsigned char Command3[6] = {0x80, 0x43, 0x00, 0x00, 0x00, 0x00}; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci unsigned char st[7]; 6298c2ecf20Sopenharmony_ci int status; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci udelay(1000); 6328c2ecf20Sopenharmony_ci if ((status = sb1000_start_get_set_command(ioaddr, name))) 6338c2ecf20Sopenharmony_ci return status; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 6368c2ecf20Sopenharmony_ci return status; 6378c2ecf20Sopenharmony_ci PID[0] = st[1] << 8 | st[2]; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command1, st))) 6408c2ecf20Sopenharmony_ci return status; 6418c2ecf20Sopenharmony_ci PID[1] = st[1] << 8 | st[2]; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command2, st))) 6448c2ecf20Sopenharmony_ci return status; 6458c2ecf20Sopenharmony_ci PID[2] = st[1] << 8 | st[2]; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command3, st))) 6488c2ecf20Sopenharmony_ci return status; 6498c2ecf20Sopenharmony_ci PID[3] = st[1] << 8 | st[2]; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return sb1000_end_get_set_command(ioaddr, name); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* set SB1000 PIDs */ 6558c2ecf20Sopenharmony_cistatic int 6568c2ecf20Sopenharmony_cisb1000_set_PIDs(const int ioaddr[], const char* name, const short PID[]) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci static const unsigned char Command4[6] = {0x80, 0x2e, 0x00, 0x00, 0x00, 0x00}; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci unsigned char st[7]; 6618c2ecf20Sopenharmony_ci short p; 6628c2ecf20Sopenharmony_ci int status; 6638c2ecf20Sopenharmony_ci unsigned char Command0[6] = {0x80, 0x31, 0x00, 0x00, 0x00, 0x00}; 6648c2ecf20Sopenharmony_ci unsigned char Command1[6] = {0x80, 0x32, 0x00, 0x00, 0x00, 0x00}; 6658c2ecf20Sopenharmony_ci unsigned char Command2[6] = {0x80, 0x33, 0x00, 0x00, 0x00, 0x00}; 6668c2ecf20Sopenharmony_ci unsigned char Command3[6] = {0x80, 0x34, 0x00, 0x00, 0x00, 0x00}; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci udelay(1000); 6698c2ecf20Sopenharmony_ci if ((status = sb1000_start_get_set_command(ioaddr, name))) 6708c2ecf20Sopenharmony_ci return status; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci p = PID[0]; 6738c2ecf20Sopenharmony_ci Command0[3] = p & 0xff; 6748c2ecf20Sopenharmony_ci p >>= 8; 6758c2ecf20Sopenharmony_ci Command0[2] = p & 0xff; 6768c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command0, st))) 6778c2ecf20Sopenharmony_ci return status; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci p = PID[1]; 6808c2ecf20Sopenharmony_ci Command1[3] = p & 0xff; 6818c2ecf20Sopenharmony_ci p >>= 8; 6828c2ecf20Sopenharmony_ci Command1[2] = p & 0xff; 6838c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command1, st))) 6848c2ecf20Sopenharmony_ci return status; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci p = PID[2]; 6878c2ecf20Sopenharmony_ci Command2[3] = p & 0xff; 6888c2ecf20Sopenharmony_ci p >>= 8; 6898c2ecf20Sopenharmony_ci Command2[2] = p & 0xff; 6908c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command2, st))) 6918c2ecf20Sopenharmony_ci return status; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci p = PID[3]; 6948c2ecf20Sopenharmony_ci Command3[3] = p & 0xff; 6958c2ecf20Sopenharmony_ci p >>= 8; 6968c2ecf20Sopenharmony_ci Command3[2] = p & 0xff; 6978c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command3, st))) 6988c2ecf20Sopenharmony_ci return status; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if ((status = card_send_command(ioaddr, name, Command4, st))) 7018c2ecf20Sopenharmony_ci return status; 7028c2ecf20Sopenharmony_ci return sb1000_end_get_set_command(ioaddr, name); 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic void 7078c2ecf20Sopenharmony_cisb1000_print_status_buffer(const char* name, unsigned char st[], 7088c2ecf20Sopenharmony_ci unsigned char buffer[], int size) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci int i, j, k; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: status: %02x %02x\n", name, st[0], st[1]); 7138c2ecf20Sopenharmony_ci if (buffer[24] == 0x08 && buffer[25] == 0x00 && buffer[26] == 0x45) { 7148c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: length: %d protocol: %d from: %d.%d.%d.%d:%d " 7158c2ecf20Sopenharmony_ci "to %d.%d.%d.%d:%d\n", name, buffer[28] << 8 | buffer[29], 7168c2ecf20Sopenharmony_ci buffer[35], buffer[38], buffer[39], buffer[40], buffer[41], 7178c2ecf20Sopenharmony_ci buffer[46] << 8 | buffer[47], 7188c2ecf20Sopenharmony_ci buffer[42], buffer[43], buffer[44], buffer[45], 7198c2ecf20Sopenharmony_ci buffer[48] << 8 | buffer[49]); 7208c2ecf20Sopenharmony_ci } else { 7218c2ecf20Sopenharmony_ci for (i = 0, k = 0; i < (size + 7) / 8; i++) { 7228c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: %s", name, i ? " " : "buffer:"); 7238c2ecf20Sopenharmony_ci for (j = 0; j < 8 && k < size; j++, k++) 7248c2ecf20Sopenharmony_ci printk(" %02x", buffer[k]); 7258c2ecf20Sopenharmony_ci printk("\n"); 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/* 7318c2ecf20Sopenharmony_ci * SB1000 commands for frame rx interrupt 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ci/* receive a single frame and assemble datagram 7348c2ecf20Sopenharmony_ci * (this is the heart of the interrupt routine) 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_cistatic int 7378c2ecf20Sopenharmony_cisb1000_rx(struct net_device *dev) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci#define FRAMESIZE 184 7418c2ecf20Sopenharmony_ci unsigned char st[2], buffer[FRAMESIZE], session_id, frame_id; 7428c2ecf20Sopenharmony_ci short dlen; 7438c2ecf20Sopenharmony_ci int ioaddr, ns; 7448c2ecf20Sopenharmony_ci unsigned int skbsize; 7458c2ecf20Sopenharmony_ci struct sk_buff *skb; 7468c2ecf20Sopenharmony_ci struct sb1000_private *lp = netdev_priv(dev); 7478c2ecf20Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* SB1000 frame constants */ 7508c2ecf20Sopenharmony_ci const int FrameSize = FRAMESIZE; 7518c2ecf20Sopenharmony_ci const int NewDatagramHeaderSkip = 8; 7528c2ecf20Sopenharmony_ci const int NewDatagramHeaderSize = NewDatagramHeaderSkip + 18; 7538c2ecf20Sopenharmony_ci const int NewDatagramDataSize = FrameSize - NewDatagramHeaderSize; 7548c2ecf20Sopenharmony_ci const int ContDatagramHeaderSkip = 7; 7558c2ecf20Sopenharmony_ci const int ContDatagramHeaderSize = ContDatagramHeaderSkip + 1; 7568c2ecf20Sopenharmony_ci const int ContDatagramDataSize = FrameSize - ContDatagramHeaderSize; 7578c2ecf20Sopenharmony_ci const int TrailerSize = 4; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci ioaddr = dev->base_addr; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci insw(ioaddr, (unsigned short*) st, 1); 7628c2ecf20Sopenharmony_ci#ifdef XXXDEBUG 7638c2ecf20Sopenharmony_ciprintk("cm0: received: %02x %02x\n", st[0], st[1]); 7648c2ecf20Sopenharmony_ci#endif /* XXXDEBUG */ 7658c2ecf20Sopenharmony_ci lp->rx_frames++; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* decide if it is a good or bad frame */ 7688c2ecf20Sopenharmony_ci for (ns = 0; ns < NPIDS; ns++) { 7698c2ecf20Sopenharmony_ci session_id = lp->rx_session_id[ns]; 7708c2ecf20Sopenharmony_ci frame_id = lp->rx_frame_id[ns]; 7718c2ecf20Sopenharmony_ci if (st[0] == session_id) { 7728c2ecf20Sopenharmony_ci if (st[1] == frame_id || (!frame_id && (st[1] & 0xf0) == 0x30)) { 7738c2ecf20Sopenharmony_ci goto good_frame; 7748c2ecf20Sopenharmony_ci } else if ((st[1] & 0xf0) == 0x30 && (st[0] & 0x40)) { 7758c2ecf20Sopenharmony_ci goto skipped_frame; 7768c2ecf20Sopenharmony_ci } else { 7778c2ecf20Sopenharmony_ci goto bad_frame; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci } else if (st[0] == (session_id | 0x40)) { 7808c2ecf20Sopenharmony_ci if ((st[1] & 0xf0) == 0x30) { 7818c2ecf20Sopenharmony_ci goto skipped_frame; 7828c2ecf20Sopenharmony_ci } else { 7838c2ecf20Sopenharmony_ci goto bad_frame; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci goto bad_frame; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ciskipped_frame: 7908c2ecf20Sopenharmony_ci stats->rx_frame_errors++; 7918c2ecf20Sopenharmony_ci skb = lp->rx_skb[ns]; 7928c2ecf20Sopenharmony_ci if (sb1000_debug > 1) 7938c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: missing frame(s): got %02x %02x " 7948c2ecf20Sopenharmony_ci "expecting %02x %02x\n", dev->name, st[0], st[1], 7958c2ecf20Sopenharmony_ci skb ? session_id : session_id | 0x40, frame_id); 7968c2ecf20Sopenharmony_ci if (skb) { 7978c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 7988c2ecf20Sopenharmony_ci skb = NULL; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cigood_frame: 8028c2ecf20Sopenharmony_ci lp->rx_frame_id[ns] = 0x30 | ((st[1] + 1) & 0x0f); 8038c2ecf20Sopenharmony_ci /* new datagram */ 8048c2ecf20Sopenharmony_ci if (st[0] & 0x40) { 8058c2ecf20Sopenharmony_ci /* get data length */ 8068c2ecf20Sopenharmony_ci insw(ioaddr, buffer, NewDatagramHeaderSize / 2); 8078c2ecf20Sopenharmony_ci#ifdef XXXDEBUG 8088c2ecf20Sopenharmony_ciprintk("cm0: IP identification: %02x%02x fragment offset: %02x%02x\n", buffer[30], buffer[31], buffer[32], buffer[33]); 8098c2ecf20Sopenharmony_ci#endif /* XXXDEBUG */ 8108c2ecf20Sopenharmony_ci if (buffer[0] != NewDatagramHeaderSkip) { 8118c2ecf20Sopenharmony_ci if (sb1000_debug > 1) 8128c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: new datagram header skip error: " 8138c2ecf20Sopenharmony_ci "got %02x expecting %02x\n", dev->name, buffer[0], 8148c2ecf20Sopenharmony_ci NewDatagramHeaderSkip); 8158c2ecf20Sopenharmony_ci stats->rx_length_errors++; 8168c2ecf20Sopenharmony_ci insw(ioaddr, buffer, NewDatagramDataSize / 2); 8178c2ecf20Sopenharmony_ci goto bad_frame_next; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci dlen = ((buffer[NewDatagramHeaderSkip + 3] & 0x0f) << 8 | 8208c2ecf20Sopenharmony_ci buffer[NewDatagramHeaderSkip + 4]) - 17; 8218c2ecf20Sopenharmony_ci if (dlen > SB1000_MRU) { 8228c2ecf20Sopenharmony_ci if (sb1000_debug > 1) 8238c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: datagram length (%d) greater " 8248c2ecf20Sopenharmony_ci "than MRU (%d)\n", dev->name, dlen, SB1000_MRU); 8258c2ecf20Sopenharmony_ci stats->rx_length_errors++; 8268c2ecf20Sopenharmony_ci insw(ioaddr, buffer, NewDatagramDataSize / 2); 8278c2ecf20Sopenharmony_ci goto bad_frame_next; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci lp->rx_dlen[ns] = dlen; 8308c2ecf20Sopenharmony_ci /* compute size to allocate for datagram */ 8318c2ecf20Sopenharmony_ci skbsize = dlen + FrameSize; 8328c2ecf20Sopenharmony_ci if ((skb = alloc_skb(skbsize, GFP_ATOMIC)) == NULL) { 8338c2ecf20Sopenharmony_ci if (sb1000_debug > 1) 8348c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: can't allocate %d bytes long " 8358c2ecf20Sopenharmony_ci "skbuff\n", dev->name, skbsize); 8368c2ecf20Sopenharmony_ci stats->rx_dropped++; 8378c2ecf20Sopenharmony_ci insw(ioaddr, buffer, NewDatagramDataSize / 2); 8388c2ecf20Sopenharmony_ci goto dropped_frame; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci skb->dev = dev; 8418c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 8428c2ecf20Sopenharmony_ci skb->protocol = (unsigned short) buffer[NewDatagramHeaderSkip + 16]; 8438c2ecf20Sopenharmony_ci insw(ioaddr, skb_put(skb, NewDatagramDataSize), 8448c2ecf20Sopenharmony_ci NewDatagramDataSize / 2); 8458c2ecf20Sopenharmony_ci lp->rx_skb[ns] = skb; 8468c2ecf20Sopenharmony_ci } else { 8478c2ecf20Sopenharmony_ci /* continuation of previous datagram */ 8488c2ecf20Sopenharmony_ci insw(ioaddr, buffer, ContDatagramHeaderSize / 2); 8498c2ecf20Sopenharmony_ci if (buffer[0] != ContDatagramHeaderSkip) { 8508c2ecf20Sopenharmony_ci if (sb1000_debug > 1) 8518c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: cont datagram header skip error: " 8528c2ecf20Sopenharmony_ci "got %02x expecting %02x\n", dev->name, buffer[0], 8538c2ecf20Sopenharmony_ci ContDatagramHeaderSkip); 8548c2ecf20Sopenharmony_ci stats->rx_length_errors++; 8558c2ecf20Sopenharmony_ci insw(ioaddr, buffer, ContDatagramDataSize / 2); 8568c2ecf20Sopenharmony_ci goto bad_frame_next; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci skb = lp->rx_skb[ns]; 8598c2ecf20Sopenharmony_ci insw(ioaddr, skb_put(skb, ContDatagramDataSize), 8608c2ecf20Sopenharmony_ci ContDatagramDataSize / 2); 8618c2ecf20Sopenharmony_ci dlen = lp->rx_dlen[ns]; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci if (skb->len < dlen + TrailerSize) { 8648c2ecf20Sopenharmony_ci lp->rx_session_id[ns] &= ~0x40; 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* datagram completed: send to upper level */ 8698c2ecf20Sopenharmony_ci skb_trim(skb, dlen); 8708c2ecf20Sopenharmony_ci netif_rx(skb); 8718c2ecf20Sopenharmony_ci stats->rx_bytes+=dlen; 8728c2ecf20Sopenharmony_ci stats->rx_packets++; 8738c2ecf20Sopenharmony_ci lp->rx_skb[ns] = NULL; 8748c2ecf20Sopenharmony_ci lp->rx_session_id[ns] |= 0x40; 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cibad_frame: 8788c2ecf20Sopenharmony_ci insw(ioaddr, buffer, FrameSize / 2); 8798c2ecf20Sopenharmony_ci if (sb1000_debug > 1) 8808c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: frame error: got %02x %02x\n", 8818c2ecf20Sopenharmony_ci dev->name, st[0], st[1]); 8828c2ecf20Sopenharmony_ci stats->rx_frame_errors++; 8838c2ecf20Sopenharmony_cibad_frame_next: 8848c2ecf20Sopenharmony_ci if (sb1000_debug > 2) 8858c2ecf20Sopenharmony_ci sb1000_print_status_buffer(dev->name, st, buffer, FrameSize); 8868c2ecf20Sopenharmony_cidropped_frame: 8878c2ecf20Sopenharmony_ci stats->rx_errors++; 8888c2ecf20Sopenharmony_ci if (ns < NPIDS) { 8898c2ecf20Sopenharmony_ci if ((skb = lp->rx_skb[ns])) { 8908c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8918c2ecf20Sopenharmony_ci lp->rx_skb[ns] = NULL; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci lp->rx_session_id[ns] |= 0x40; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci return -1; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic void 8998c2ecf20Sopenharmony_cisb1000_error_dpc(struct net_device *dev) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x26, 0x00, 0x00, 0x00, 0x00}; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci char *name; 9048c2ecf20Sopenharmony_ci unsigned char st[5]; 9058c2ecf20Sopenharmony_ci int ioaddr[2]; 9068c2ecf20Sopenharmony_ci struct sb1000_private *lp = netdev_priv(dev); 9078c2ecf20Sopenharmony_ci const int ErrorDpcCounterInitialize = 200; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci ioaddr[0] = dev->base_addr; 9108c2ecf20Sopenharmony_ci /* mem_start holds the second I/O address */ 9118c2ecf20Sopenharmony_ci ioaddr[1] = dev->mem_start; 9128c2ecf20Sopenharmony_ci name = dev->name; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci sb1000_wait_for_ready_clear(ioaddr, name); 9158c2ecf20Sopenharmony_ci sb1000_send_command(ioaddr, name, Command0); 9168c2ecf20Sopenharmony_ci sb1000_wait_for_ready(ioaddr, name); 9178c2ecf20Sopenharmony_ci sb1000_read_status(ioaddr, st); 9188c2ecf20Sopenharmony_ci if (st[1] & 0x10) 9198c2ecf20Sopenharmony_ci lp->rx_error_dpc_count = ErrorDpcCounterInitialize; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci/* 9248c2ecf20Sopenharmony_ci * Linux interface functions 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_cistatic int 9278c2ecf20Sopenharmony_cisb1000_open(struct net_device *dev) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci char *name; 9308c2ecf20Sopenharmony_ci int ioaddr[2], status; 9318c2ecf20Sopenharmony_ci struct sb1000_private *lp = netdev_priv(dev); 9328c2ecf20Sopenharmony_ci const unsigned short FirmwareVersion[] = {0x01, 0x01}; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ioaddr[0] = dev->base_addr; 9358c2ecf20Sopenharmony_ci /* mem_start holds the second I/O address */ 9368c2ecf20Sopenharmony_ci ioaddr[1] = dev->mem_start; 9378c2ecf20Sopenharmony_ci name = dev->name; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* initialize sb1000 */ 9408c2ecf20Sopenharmony_ci if ((status = sb1000_reset(ioaddr, name))) 9418c2ecf20Sopenharmony_ci return status; 9428c2ecf20Sopenharmony_ci ssleep(1); 9438c2ecf20Sopenharmony_ci if ((status = sb1000_check_CRC(ioaddr, name))) 9448c2ecf20Sopenharmony_ci return status; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* initialize private data before board can catch interrupts */ 9478c2ecf20Sopenharmony_ci lp->rx_skb[0] = NULL; 9488c2ecf20Sopenharmony_ci lp->rx_skb[1] = NULL; 9498c2ecf20Sopenharmony_ci lp->rx_skb[2] = NULL; 9508c2ecf20Sopenharmony_ci lp->rx_skb[3] = NULL; 9518c2ecf20Sopenharmony_ci lp->rx_dlen[0] = 0; 9528c2ecf20Sopenharmony_ci lp->rx_dlen[1] = 0; 9538c2ecf20Sopenharmony_ci lp->rx_dlen[2] = 0; 9548c2ecf20Sopenharmony_ci lp->rx_dlen[3] = 0; 9558c2ecf20Sopenharmony_ci lp->rx_frames = 0; 9568c2ecf20Sopenharmony_ci lp->rx_error_count = 0; 9578c2ecf20Sopenharmony_ci lp->rx_error_dpc_count = 0; 9588c2ecf20Sopenharmony_ci lp->rx_session_id[0] = 0x50; 9598c2ecf20Sopenharmony_ci lp->rx_session_id[1] = 0x48; 9608c2ecf20Sopenharmony_ci lp->rx_session_id[2] = 0x44; 9618c2ecf20Sopenharmony_ci lp->rx_session_id[3] = 0x42; 9628c2ecf20Sopenharmony_ci lp->rx_frame_id[0] = 0; 9638c2ecf20Sopenharmony_ci lp->rx_frame_id[1] = 0; 9648c2ecf20Sopenharmony_ci lp->rx_frame_id[2] = 0; 9658c2ecf20Sopenharmony_ci lp->rx_frame_id[3] = 0; 9668c2ecf20Sopenharmony_ci if (request_irq(dev->irq, sb1000_interrupt, 0, "sb1000", dev)) { 9678c2ecf20Sopenharmony_ci return -EAGAIN; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (sb1000_debug > 2) 9718c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Opening, IRQ %d\n", name, dev->irq); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* Activate board and check firmware version */ 9748c2ecf20Sopenharmony_ci udelay(1000); 9758c2ecf20Sopenharmony_ci if ((status = sb1000_activate(ioaddr, name))) 9768c2ecf20Sopenharmony_ci return status; 9778c2ecf20Sopenharmony_ci udelay(0); 9788c2ecf20Sopenharmony_ci if ((status = sb1000_get_firmware_version(ioaddr, name, version, 0))) 9798c2ecf20Sopenharmony_ci return status; 9808c2ecf20Sopenharmony_ci if (version[0] != FirmwareVersion[0] || version[1] != FirmwareVersion[1]) 9818c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: found firmware version %x.%02x " 9828c2ecf20Sopenharmony_ci "(should be %x.%02x)\n", name, version[0], version[1], 9838c2ecf20Sopenharmony_ci FirmwareVersion[0], FirmwareVersion[1]); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci netif_start_queue(dev); 9878c2ecf20Sopenharmony_ci return 0; /* Always succeed */ 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci char* name; 9938c2ecf20Sopenharmony_ci unsigned char version[2]; 9948c2ecf20Sopenharmony_ci short PID[4]; 9958c2ecf20Sopenharmony_ci int ioaddr[2], status, frequency; 9968c2ecf20Sopenharmony_ci unsigned int stats[5]; 9978c2ecf20Sopenharmony_ci struct sb1000_private *lp = netdev_priv(dev); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (!(dev && dev->flags & IFF_UP)) 10008c2ecf20Sopenharmony_ci return -ENODEV; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci ioaddr[0] = dev->base_addr; 10038c2ecf20Sopenharmony_ci /* mem_start holds the second I/O address */ 10048c2ecf20Sopenharmony_ci ioaddr[1] = dev->mem_start; 10058c2ecf20Sopenharmony_ci name = dev->name; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci switch (cmd) { 10088c2ecf20Sopenharmony_ci case SIOCGCMSTATS: /* get statistics */ 10098c2ecf20Sopenharmony_ci stats[0] = dev->stats.rx_bytes; 10108c2ecf20Sopenharmony_ci stats[1] = lp->rx_frames; 10118c2ecf20Sopenharmony_ci stats[2] = dev->stats.rx_packets; 10128c2ecf20Sopenharmony_ci stats[3] = dev->stats.rx_errors; 10138c2ecf20Sopenharmony_ci stats[4] = dev->stats.rx_dropped; 10148c2ecf20Sopenharmony_ci if(copy_to_user(ifr->ifr_data, stats, sizeof(stats))) 10158c2ecf20Sopenharmony_ci return -EFAULT; 10168c2ecf20Sopenharmony_ci status = 0; 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci case SIOCGCMFIRMWARE: /* get firmware version */ 10208c2ecf20Sopenharmony_ci if ((status = sb1000_get_firmware_version(ioaddr, name, version, 1))) 10218c2ecf20Sopenharmony_ci return status; 10228c2ecf20Sopenharmony_ci if(copy_to_user(ifr->ifr_data, version, sizeof(version))) 10238c2ecf20Sopenharmony_ci return -EFAULT; 10248c2ecf20Sopenharmony_ci break; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci case SIOCGCMFREQUENCY: /* get frequency */ 10278c2ecf20Sopenharmony_ci if ((status = sb1000_get_frequency(ioaddr, name, &frequency))) 10288c2ecf20Sopenharmony_ci return status; 10298c2ecf20Sopenharmony_ci if(put_user(frequency, (int __user *) ifr->ifr_data)) 10308c2ecf20Sopenharmony_ci return -EFAULT; 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci case SIOCSCMFREQUENCY: /* set frequency */ 10348c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 10358c2ecf20Sopenharmony_ci return -EPERM; 10368c2ecf20Sopenharmony_ci if(get_user(frequency, (int __user *) ifr->ifr_data)) 10378c2ecf20Sopenharmony_ci return -EFAULT; 10388c2ecf20Sopenharmony_ci if ((status = sb1000_set_frequency(ioaddr, name, frequency))) 10398c2ecf20Sopenharmony_ci return status; 10408c2ecf20Sopenharmony_ci break; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci case SIOCGCMPIDS: /* get PIDs */ 10438c2ecf20Sopenharmony_ci if ((status = sb1000_get_PIDs(ioaddr, name, PID))) 10448c2ecf20Sopenharmony_ci return status; 10458c2ecf20Sopenharmony_ci if(copy_to_user(ifr->ifr_data, PID, sizeof(PID))) 10468c2ecf20Sopenharmony_ci return -EFAULT; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci case SIOCSCMPIDS: /* set PIDs */ 10508c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 10518c2ecf20Sopenharmony_ci return -EPERM; 10528c2ecf20Sopenharmony_ci if(copy_from_user(PID, ifr->ifr_data, sizeof(PID))) 10538c2ecf20Sopenharmony_ci return -EFAULT; 10548c2ecf20Sopenharmony_ci if ((status = sb1000_set_PIDs(ioaddr, name, PID))) 10558c2ecf20Sopenharmony_ci return status; 10568c2ecf20Sopenharmony_ci /* set session_id, frame_id and pkt_type too */ 10578c2ecf20Sopenharmony_ci lp->rx_session_id[0] = 0x50 | (PID[0] & 0x0f); 10588c2ecf20Sopenharmony_ci lp->rx_session_id[1] = 0x48; 10598c2ecf20Sopenharmony_ci lp->rx_session_id[2] = 0x44; 10608c2ecf20Sopenharmony_ci lp->rx_session_id[3] = 0x42; 10618c2ecf20Sopenharmony_ci lp->rx_frame_id[0] = 0; 10628c2ecf20Sopenharmony_ci lp->rx_frame_id[1] = 0; 10638c2ecf20Sopenharmony_ci lp->rx_frame_id[2] = 0; 10648c2ecf20Sopenharmony_ci lp->rx_frame_id[3] = 0; 10658c2ecf20Sopenharmony_ci break; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci default: 10688c2ecf20Sopenharmony_ci status = -EINVAL; 10698c2ecf20Sopenharmony_ci break; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci return status; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci/* transmit function: do nothing since SB1000 can't send anything out */ 10758c2ecf20Sopenharmony_cistatic netdev_tx_t 10768c2ecf20Sopenharmony_cisb1000_start_xmit(struct sk_buff *skb, struct net_device *dev) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: trying to transmit!!!\n", dev->name); 10798c2ecf20Sopenharmony_ci /* sb1000 can't xmit datagrams */ 10808c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 10818c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/* SB1000 interrupt handler. */ 10858c2ecf20Sopenharmony_cistatic irqreturn_t sb1000_interrupt(int irq, void *dev_id) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci static const unsigned char Command0[6] = {0x80, 0x2c, 0x00, 0x00, 0x00, 0x00}; 10888c2ecf20Sopenharmony_ci static const unsigned char Command1[6] = {0x80, 0x2e, 0x00, 0x00, 0x00, 0x00}; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci char *name; 10918c2ecf20Sopenharmony_ci unsigned char st; 10928c2ecf20Sopenharmony_ci int ioaddr[2]; 10938c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 10948c2ecf20Sopenharmony_ci struct sb1000_private *lp = netdev_priv(dev); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci const int MaxRxErrorCount = 6; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci ioaddr[0] = dev->base_addr; 10998c2ecf20Sopenharmony_ci /* mem_start holds the second I/O address */ 11008c2ecf20Sopenharmony_ci ioaddr[1] = dev->mem_start; 11018c2ecf20Sopenharmony_ci name = dev->name; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* is it a good interrupt? */ 11048c2ecf20Sopenharmony_ci st = inb(ioaddr[1] + 6); 11058c2ecf20Sopenharmony_ci if (!(st & 0x08 && st & 0x20)) { 11068c2ecf20Sopenharmony_ci return IRQ_NONE; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (sb1000_debug > 3) 11108c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: entering interrupt\n", dev->name); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci st = inb(ioaddr[0] + 7); 11138c2ecf20Sopenharmony_ci if (sb1000_rx(dev)) 11148c2ecf20Sopenharmony_ci lp->rx_error_count++; 11158c2ecf20Sopenharmony_ci#ifdef SB1000_DELAY 11168c2ecf20Sopenharmony_ci udelay(SB1000_DELAY); 11178c2ecf20Sopenharmony_ci#endif /* SB1000_DELAY */ 11188c2ecf20Sopenharmony_ci sb1000_issue_read_command(ioaddr, name); 11198c2ecf20Sopenharmony_ci if (st & 0x01) { 11208c2ecf20Sopenharmony_ci sb1000_error_dpc(dev); 11218c2ecf20Sopenharmony_ci sb1000_issue_read_command(ioaddr, name); 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci if (lp->rx_error_dpc_count && !(--lp->rx_error_dpc_count)) { 11248c2ecf20Sopenharmony_ci sb1000_wait_for_ready_clear(ioaddr, name); 11258c2ecf20Sopenharmony_ci sb1000_send_command(ioaddr, name, Command0); 11268c2ecf20Sopenharmony_ci sb1000_wait_for_ready(ioaddr, name); 11278c2ecf20Sopenharmony_ci sb1000_issue_read_command(ioaddr, name); 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci if (lp->rx_error_count >= MaxRxErrorCount) { 11308c2ecf20Sopenharmony_ci sb1000_wait_for_ready_clear(ioaddr, name); 11318c2ecf20Sopenharmony_ci sb1000_send_command(ioaddr, name, Command1); 11328c2ecf20Sopenharmony_ci sb1000_wait_for_ready(ioaddr, name); 11338c2ecf20Sopenharmony_ci sb1000_issue_read_command(ioaddr, name); 11348c2ecf20Sopenharmony_ci lp->rx_error_count = 0; 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic int sb1000_close(struct net_device *dev) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci int i; 11438c2ecf20Sopenharmony_ci int ioaddr[2]; 11448c2ecf20Sopenharmony_ci struct sb1000_private *lp = netdev_priv(dev); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (sb1000_debug > 2) 11478c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Shutting down sb1000.\n", dev->name); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci netif_stop_queue(dev); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci ioaddr[0] = dev->base_addr; 11528c2ecf20Sopenharmony_ci /* mem_start holds the second I/O address */ 11538c2ecf20Sopenharmony_ci ioaddr[1] = dev->mem_start; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 11568c2ecf20Sopenharmony_ci /* If we don't do this, we can't re-insmod it later. */ 11578c2ecf20Sopenharmony_ci release_region(ioaddr[1], SB1000_IO_EXTENT); 11588c2ecf20Sopenharmony_ci release_region(ioaddr[0], SB1000_IO_EXTENT); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci /* free rx_skb's if needed */ 11618c2ecf20Sopenharmony_ci for (i=0; i<4; i++) { 11628c2ecf20Sopenharmony_ci if (lp->rx_skb[i]) { 11638c2ecf20Sopenharmony_ci dev_kfree_skb(lp->rx_skb[i]); 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci return 0; 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Franco Venturi <fventuri@mediaone.net>"); 11708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("General Instruments SB1000 driver"); 11718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cimodule_pnp_driver(sb1000_driver); 1174