#include #include //#include #include #include #include "../test_lkm_tolapai_gpio/gpio.h" #include "xhfc24sucd.h" #include "xhfc.h" #include "xhfc_leb.h" #include "xhfc_test_hdlc.h" static const char xhfc_rev[] = "$Revision: 1.22 $"; static int card_cnt = 0; // debug level defines #define LOG_ALL 0xffffffff #define LOG_MIN 0x00000001 #define LOG_MEDIUM 0x00000002 // number of st ports #define NR_ST_PORT 4 MODULE_LICENSE("GPL"); uint debug = 0; static uint port = 3; static int nt = 0; static int pcm = 0; static uint fifo = 7; static uint slot = 23; static uint reset_gpio = 33; int softconf = 0; static uint lineterm_gpio = 27; static uint ntte_gpio = 25; static uint lineterm_conf = 1; static uint ntte_conf = 1; module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(port, uint, S_IRUGO); module_param(nt, bool, S_IRUGO); module_param(pcm, bool, S_IRUGO); module_param(fifo, uint, S_IRUGO); module_param(slot, uint, S_IRUGO); module_param(softconf, bool, S_IRUGO); module_param(lineterm_gpio, uint, S_IRUGO); module_param(ntte_gpio, uint, S_IRUGO); module_param(lineterm_conf, uint, S_IRUGO); module_param(ntte_conf, uint, S_IRUGO); MODULE_PARM_DESC(pcm, "Use the PCM bus instead of the S/T interface."); MODULE_PARM_DESC(softconf, "Configure the S/T port line interface in software."); MODULE_PARM_DESC(reset_gpio, "Reset the XHFC using this GPIO"); MODULE_PARM_DESC(lineterm_gpio, "(Only if softconf) Configure the line termination on the S/T port using this GPIO"); MODULE_PARM_DESC(ntte_gpio, "(Only if softconf) Configure the the S/T port as NT/TE using this GPIO"); MODULE_PARM_DESC(lineterm_conf, "(Only if softconf) 1=line termination on."); MODULE_PARM_DESC(ntte_conf, "(Only if softconf) 0=TE mode, no power feeding; 1=NT mode, power feeding"); MODULE_PARM_DESC(nt, "(Only for S/T) 1 if the interface is NT, 0 if it is TE"); MODULE_PARM_DESC(port, "(Only for S/T) XHFC S/T port number"); MODULE_PARM_DESC(fifo, "(Only for PCM) XHFC's host FIFO number."); MODULE_PARM_DESC(slot, "(Only for PCM) PCM slot."); uint trigger = 0; /* common xhfc operations */ int xhfc_chipid(struct xhfc * xhfc) { __u8 chip_id; int result = TEST_FAILED; chip_id = read_xhfc(xhfc, R_CHIP_ID); switch (chip_id) { case CHIP_ID_1SU : case CHIP_ID_2SU : case CHIP_ID_2S4U : case CHIP_ID_4SU : result = TEST_PASSED; break; } return(result); } void xhfc_waitbusy(struct xhfc * xhfc) { while (read_xhfc(xhfc, R_STATUS) & M_BUSY) cpu_relax(); } void xhfc_selfifo(struct xhfc * xhfc, int fifo, int receive) { u8 r_fifo = 0x00; SET_V_FIFO_NUM(r_fifo, fifo); SET_V_FIFO_DIR(r_fifo, receive); write_xhfc(xhfc, R_FIFO, r_fifo); xhfc_waitbusy(xhfc); } void xhfc_inc_f(struct xhfc * xhfc) { write_xhfc(xhfc, A_INC_RES_FIFO, M_INC_F); xhfc_waitbusy(xhfc); } static inline void xhfc_resetfifo(struct xhfc * xhfc) { write_xhfc(xhfc, A_INC_RES_FIFO, M_RES_FIFO | M_RES_FIFO_ERR); xhfc_waitbusy(xhfc); } int xhfc_reset(struct xhfc * xhfc) { int timeout = 0x2000; /* software reset to enable R_FIFO_MD setting */ write_xhfc(xhfc, R_CIRM, M_SRES); udelay(5); write_xhfc(xhfc, R_CIRM, 0); while ((read_xhfc(xhfc, R_STATUS) & (M_BUSY | M_PCM_INIT)) && (timeout)) timeout--; if (!(timeout)) { printk(KERN_ERR "%s %s: initialization sequence could not finish\n", xhfc->pi->name, __FUNCTION__); return (-ENODEV); } return(0); } /* --------------------------------------------------------- xhfc_cfg_pcm_loop(struct xhfc * xhfc, optional paramters) configures PCM master mode, and loop backs on some time slots ----------------------------------------------------------- */ int xhfc_cfg_pcm_loop1(struct xhfc * xhfc, __u8 pcm) { static char msg[1000]; msg[0] = 0; // prepare access to R_PCM_MD1 write_xhfc(xhfc, R_PCM_MD0, 0x90); // write settings for R_PCM_MD1 // use slow PCM clock adjust speed write_xhfc(xhfc, R_PCM_MD1, M_PLL_ADJ | M_PCM_OD); // prepare access to R_SH0L write_xhfc(xhfc, R_PCM_MD0, 0xc0); // write settings for R_SH0L // define frame sync shape0 low byte write_xhfc(xhfc, R_SH0L, 0); // prepare access to R_SH0H write_xhfc(xhfc, R_PCM_MD0, 0xd0); // write settings for R_SH0H // define frame sync shape0 high byte write_xhfc(xhfc, R_SH0H, 0x0f); // prepare access to R_SL_SEL0 write_xhfc(xhfc, R_PCM_MD0, 0x00); // write settings for R_SL_SEL0 // enable frame signal with shape0 timing // for F1_0 pin on time slot 0 for 2MBit PCM write_xhfc(xhfc, R_SL_SEL0, 0x1f); if (pcm == XHFC_PCM_MASTER) { // prepare access to R_PCM_MD2 write_xhfc(xhfc, R_PCM_MD0, 0xa0); // write settings for R_PCM_MD2 // enable PCM bit clock for C2O pin write_xhfc(xhfc, R_PCM_MD2, M_C2O_EN); trigger = 1; write_xhfc(xhfc, R_PCM_MD0, 0x01 | M_F0_LEN); trigger = 0; printk(KERN_INFO "%s: PCM master enabled, bitclock at C2O pin, 2Mbit/s\n",__FUNCTION__); printk(KERN_INFO "%s: additional F1_0 frame sync signal enabled with user defined timing\n",__FUNCTION__); printk(KERN_INFO "%s: configuration finished\n",__FUNCTION__); } else { // prepare access to R_PCM_MD2 write_xhfc(xhfc, R_PCM_MD0, 0xa0); // write settings for R_PCM_MD2 write_xhfc(xhfc, R_PCM_MD2, M_C2I_EN ); write_xhfc(xhfc, R_PCM_MD0, 0x00 | M_F0_LEN); printk(KERN_INFO "%s: PCM slave enabled, M_C2I_EN, 2Mbit/s\n",__FUNCTION__); printk(KERN_INFO "%s: additional F1_0 frame sync signal enabled with user defined timing\n",__FUNCTION__); printk(KERN_INFO "%s: PCM TS0_STIO1 rx -> PCM_TS0_STIO2 tx\n",__FUNCTION__); printk(KERN_INFO "%s: PCM TS1_STIO1 rx -> PCM_TS1_STIO2 tx\n",__FUNCTION__); printk(KERN_INFO "%s: PCM TS2_STIO1 rx -> PCM_TS2_STIO2 tx\n",__FUNCTION__); printk(KERN_INFO "%s: PCM TS3_STIO1 rx -> PCM_TS3_STIO2 tx\n",__FUNCTION__); printk(KERN_INFO "%s: configuration finished\n",__FUNCTION__); } // port 0 as sysnc source for PCM // with port 0 in NT mode or as deactivated TE // PCM clock becomes free running write_xhfc(xhfc, R_SU_SYNC, 0x8); return(0); } /*******************************************/ /* Interrupt handler, just handle TimerIRQ */ /******************************************/ irqreturn_t xhfc_interrupt(int irq, void *dev_id, struct pt_regs* ptregs) { /* (void) ptregs; */ struct xhfc_pi * pi = dev_id; struct xhfc * xhfc = NULL; u8 misc_irq; xhfc = &pi->xhfc; /* ommit IRQs for other HW with shared IRQ */ if (!(read_xhfc(xhfc, R_IRQ_OVIEW))) { /* printk(KERN_INFO "%s %s NOT M_GLOB_IRQ_EN or R_IRQ_OVIEW \n", xhfc->name, __FUNCTION__); */ return IRQ_NONE; } /* reset IRQ source */ misc_irq = read_xhfc(xhfc, R_MISC_IRQ); /* check for Timer IRQ */ if (misc_irq & M_TI_IRQMSK) xhfc->irq_cnt++; xhfc_hdlc_interrupt(xhfc); return IRQ_HANDLED; } /*****************************************************/ /* disable all interrupts by disabling M_GLOB_IRQ_EN */ /*****************************************************/ static void disable_interrupts(struct xhfc * xhfc) { printk(KERN_INFO "%s %s\n", xhfc->name, __FUNCTION__); write_xhfc(xhfc, R_IRQ_CTRL, 0); } /******************************************/ /* start interrupt and set interrupt mask */ /******************************************/ static void enable_interrupts(struct xhfc * xhfc) { printk(KERN_INFO "%s %s\n", xhfc->name, __FUNCTION__); /* set PCM master mode */ write_xhfc(xhfc, R_PCM_MD0, M_PCM_MD | M_F0_LEN); // Setting M_F0_LEN here so it's not overwritten write_xhfc(xhfc, R_TI_WD, 0x02); write_xhfc(xhfc, R_MISC_IRQMSK, M_TI_IRQMSK); /* clear all pending interrupts bits */ read_xhfc(xhfc, R_MISC_IRQ); read_xhfc(xhfc, R_SU_IRQ); read_xhfc(xhfc, R_FIFO_BL0_IRQ); read_xhfc(xhfc, R_FIFO_BL1_IRQ); read_xhfc(xhfc, R_FIFO_BL2_IRQ); read_xhfc(xhfc, R_FIFO_BL3_IRQ); /* enable global interrupts */ write_xhfc(xhfc, R_IRQ_CTRL, M_GLOB_IRQ_EN | M_FIFO_IRQ_EN); } /***********************************/ /* initialise the XHFC ISDN Chip */ /* return 0 on success. */ /***********************************/ int xhfc_collect_chip_id(struct xhfc * xhfc) { int err = 0; __u8 chip_id; chip_id = read_xhfc(xhfc, R_CHIP_ID); switch (chip_id) { case CHIP_ID_1SU: xhfc->num_ports = 1; xhfc->max_fifo = 4; xhfc->max_z = 0xFF; break; case CHIP_ID_2SU: xhfc->num_ports = 2; xhfc->max_fifo = 8; xhfc->max_z = 0x7F; break; case CHIP_ID_2S4U: case CHIP_ID_4SU: xhfc->num_ports = 4; xhfc->max_fifo = 16; xhfc->max_z = 0x3F; break; default: err = -ENODEV; } if (err) { printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n", xhfc->name, __FUNCTION__, chip_id); return (err); } else { printk(KERN_INFO "%s ChipID: 0x%x\n", xhfc->name, chip_id); } return 0; } void xhfc_hard_reset(void) { gpio_set_direction(reset_gpio, GPIO_OUTPUT); gpio_set_to_gpio(reset_gpio); gpio_set_level(reset_gpio, GPIO_LOW); msleep(1); gpio_set_level(reset_gpio, GPIO_HIGH); } void configure_interface(void) { gpio_set_direction(lineterm_gpio, GPIO_OUTPUT); gpio_set_to_gpio(lineterm_gpio); gpio_set_direction(ntte_gpio, GPIO_OUTPUT); gpio_set_to_gpio(ntte_gpio); gpio_set_level(lineterm_gpio, lineterm_conf); gpio_set_level(ntte_gpio, ntte_conf); } /************************/ /* release single card */ /************************/ void release_card_irq(struct xhfc_pi * pi) { disable_interrupts(&pi->xhfc); free_irq(pi->irq, pi); /* wait for pending tasklet to finish */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ } static const struct pci_device_id tlp_leb_pci_tbl[] = { { 0x8086, 0x503d, PCI_ANY_ID, PCI_ANY_ID, }, /* Intel Tolapai LEB controler */ { } }; MODULE_DEVICE_TABLE(pci, tlp_leb_pci_tbl); static struct pci_driver xhfc_driver = { .name = DRV_NAME, .id_table = tlp_leb_pci_tbl, .probe = xhfc_init_one, .remove = __devexit_p(xhfc_remove_one), .shutdown = xhfc_shutdown, }; int __devinit xhfc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct xhfc_pi * pi; unsigned long base, size; int rc = -ENOMEM; printk(KERN_DEBUG "entering tlp_leb_init_one.\n"); pi = kmalloc(sizeof(struct xhfc_pi), GFP_KERNEL); if (pi == NULL) goto err_alloc; memset(pi, 0, sizeof(struct xhfc_pi)); sprintf(pi->name, "%s", DRIVER_NAME); /* ? */ printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x\n", pi->name, __FUNCTION__, pi->name, pdev->bus->number, pdev->devfn); rc = pci_enable_device(pdev); if (rc) goto err_enable_device; printk(KERN_DEBUG "pci_enable_device succeeded.\n"); rc = pci_request_regions(pdev, DRV_NAME); if (rc) goto err_request_regions; rc = -ENOMEM; base = pci_resource_start(pdev, LEB_CSRBAR); size = pci_resource_len(pdev, LEB_CSRBAR); pi->regs = ioremap(base, size); if (!pi->regs) goto err_ioremap_csrbar; base = pci_resource_start(pdev, LEB_MMBAR); size = pci_resource_len(pdev, LEB_MMBAR); pi->cs_n0 = ioremap(base, size); if (!pi->cs_n0) goto err_ioremap_mmbar; pci_set_drvdata(pdev, pi); pi->xhfc.pi = pi; /* only adress one xhfc per PI */ pi->xhfc.chipidx = 0; pi->xhfc.name = pi->name; /* init interrupt engine */ rc = pi->irq = acpi_register_gsi(IRQ_TLP_GPIO_30, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW); if(rc < 0) goto err_acpi_register; leb_init(pi); if(softconf) configure_interface(); xhfc_hard_reset(); rc = xhfc_collect_chip_id(&pi->xhfc); if (rc) goto err_collect_chip_id; card_cnt++; // XXX no module argument checking done! rc = xhfc_hdlc_init_test(&pi->xhfc); if (rc < 0) goto err_init_test; if(pcm) xhfc_hdlc_init_test_pcm(&pi->xhfc, fifo, slot); else xhfc_hdlc_init_test_st(&pi->xhfc, port, nt); if (request_irq(pi->irq, &xhfc_interrupt, IRQF_SHARED, "XHFC_HWTD", pi)) { printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n", pi->name, __FUNCTION__, pi->irq); rc = -EIO; goto err_request_irq; } enable_interrupts(&pi->xhfc); return 0; err_request_irq: err_init_test: err_collect_chip_id: //acpi_unregister_gsi(IRQ_TLP_GPIO_30); /* symbol doesn't exist??? */ err_acpi_register: iounmap(pi->cs_n0); err_ioremap_mmbar: iounmap(pi->regs); err_ioremap_csrbar: pci_release_regions(pdev); err_request_regions: pci_disable_device(pdev); err_enable_device: kfree(pi); err_alloc: return rc; } void __devexit xhfc_remove_one(struct pci_dev *pdev) { struct xhfc_pi *pi; printk(KERN_DEBUG "entering tlp_leb_remove_one.\n"); pi = pci_get_drvdata(pdev); printk(KERN_INFO "%s %s: removing card\n", pi->name, __FUNCTION__); release_card_irq(pi); xhfc_hdlc_end_test(&pi->xhfc); card_cnt--; iounmap(pi->regs); pi->regs = NULL; iounmap(pi->cs_n0); pi->cs_n0 = NULL; pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); kfree(pi); printk(KERN_DEBUG "%s: bye-bye.\n", __FUNCTION__); } void xhfc_shutdown(struct pci_dev *pdev) { printk(KERN_DEBUG "%s: Au revoir.\n", __FUNCTION__); } /***************/ /* Module init */ /***************/ static int __init xhfc_init(void) { int err; printk(KERN_INFO DRIVER_NAME " HDLC test %s driver Rev. %s \n", __FUNCTION__, xhfc_rev); err = pci_register_driver(&xhfc_driver); if (err < 0) goto out; printk(KERN_INFO DRIVER_NAME " HDLC test %d cards installed\n", card_cnt); #if !defined(CONFIG_HOTPLUG) if (err == 0) { err = -ENODEV; pci_unregister_driver(&xhfc_driver); goto out; } #endif return 0; out: return (err); } static void __exit xhfc_cleanup(void) { pci_unregister_driver(&xhfc_driver); printk(KERN_INFO "%s: driver removed\n", __FUNCTION__); } module_init(xhfc_init); module_exit(xhfc_cleanup);