📄 s390io.c
字号:
/* * arch/s390/kernel/s390io.c * S/390 common I/O routines * * S390 version * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Ingo Adlung (adlung@de.ibm.com) */#include <linux/config.h>#include <linux/errno.h>#include <linux/kernel_stat.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/malloc.h>#include <linux/string.h>#include <linux/smp.h>#include <linux/tasks.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <linux/bootmem.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/smp.h>#include <asm/pgtable.h>#include <asm/delay.h>#include <asm/processor.h>#include <asm/lowcore.h>#include <asm/s390io.h>#include <asm/s390dyn.h>#include <asm/s390mach.h>#ifndef TRUE#define TRUE 1#define FALSE 0#endif#undef CONFIG_DEBUG_IO#define REIPL_DEVID_MAGIC 0x87654321struct irqaction init_IRQ_action;unsigned int highest_subchannel;ioinfo_t *ioinfo_head = NULL;ioinfo_t *ioinfo_tail = NULL;ioinfo_t *ioinfo[__MAX_SUBCHANNELS] = { [0 ... (__MAX_SUBCHANNELS-1)] = INVALID_STORAGE_AREA};static spinlock_t sync_isc; // synchronous irq processing lockstatic psw_t io_sync_wait; // wait PSW for sync IO, prot. by sync_iscstatic psw_t io_new_psw; // save I/O new PSW, prot. by sync_iscstatic int cons_dev = -1; // identify console devicestatic int init_IRQ_complete = 0;static schib_t init_schib;static __u64 irq_IPL_TOD;/* * Dummy controller type for unused interrupts */int do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;}int enable_none(unsigned int irq) { return(-ENODEV); }int disable_none(unsigned int irq) { return(-ENODEV); }struct hw_interrupt_type no_irq_type = { "none", do_none, enable_none, disable_none};static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs);static int s390_setup_irq(unsigned int irq, struct irqaction * new);static void s390_process_subchannels( void);static void s390_device_recognition( void);static int s390_validate_subchannel( int irq);static int s390_SenseID( int irq, senseid_t *sid);static int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid);static int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid);static int s390_process_IRQ( unsigned int irq );static int s390_DevicePathVerification( int irq );extern int do_none(unsigned int irq, int cpu, struct pt_regs * regs);extern int enable_none(unsigned int irq);extern int disable_none(unsigned int irq);extern void tod_wait(unsigned long usecs);asmlinkage void do_IRQ( struct pt_regs regs, unsigned int irq, __u32 s390_intparm );void s390_displayhex(char *str,void *ptr,s32 cnt);void s390_displayhex(char *str,void *ptr,s32 cnt){ s32 cnt1,cnt2,maxcnt2; u32 *currptr=(__u32 *)ptr; printk("\n%s\n",str); for(cnt1=0;cnt1<cnt;cnt1+=16) { printk("%08lX ",(unsigned long)currptr); maxcnt2=cnt-cnt1; if(maxcnt2>16) maxcnt2=16; for(cnt2=0;cnt2<maxcnt2;cnt2+=4) printk("%08X ",*currptr++); printk("\n"); }}int s390_request_irq( unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id){ int retval; struct irqaction *action; if (irq >= __MAX_SUBCHANNELS) return -EINVAL; if ( !handler || !dev_id ) return -EINVAL; /* * during init_IRQ() processing we don't have memory * management yet, thus need to use a statically * allocated irqaction control block */ if ( init_IRQ_complete ) { action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL); } else { action = &init_IRQ_action; } /* endif */ if (!action) { return -ENOMEM; } /* endif */ action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->next = NULL; action->dev_id = dev_id; retval = s390_setup_irq( irq, action); if ( !retval ) { retval = s390_DevicePathVerification( irq ); } else if ( retval && init_IRQ_complete ) { kfree(action); } /* endif */ return retval;}void s390_free_irq(unsigned int irq, void *dev_id){ unsigned int flags; int ret; unsigned int count = 0; if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA ) { return; } /* endif */ s390irq_spin_lock_irqsave( irq, flags);#ifdef CONFIG_KERNEL_DEBUG if ( irq != cons_dev ) { printk("Trying to free IRQ%d\n",irq); } /* endif */#endif /* * disable the device and reset all IRQ info if * the IRQ is actually owned by the handler ... */ if ( ioinfo[irq]->irq_desc.action ) { if ( (dev_id == ioinfo[irq]->irq_desc.action->dev_id ) || (dev_id == (devstat_t *)REIPL_DEVID_MAGIC) ) { /* start deregister */ ioinfo[irq]->ui.flags.unready = 1; do { ret = ioinfo[irq]->irq_desc.handler->disable(irq); count++; if ( ret == -EBUSY ) { int iret; /* * kill it ! * ... we first try sync and eventually * try terminating the current I/O by * an async request, twice halt, then * clear. */ if ( count < 3 ) { iret = halt_IO( irq, 0xC8C1D3E3, DOIO_WAIT_FOR_INTERRUPT ); if ( iret == -EBUSY ) { halt_IO( irq, 0xC8C1D3E3, 0); s390irq_spin_unlock_irqrestore( irq, flags); tod_wait( 200000 ); /* 200 ms */ s390irq_spin_lock_irqsave( irq, flags); } /* endif */ } else { iret = clear_IO( irq, 0x40C3D3D9, DOIO_WAIT_FOR_INTERRUPT ); if ( iret == -EBUSY ) { clear_IO( irq, 0xC8C1D3E3, 0); s390irq_spin_unlock_irqrestore( irq, flags); tod_wait( 1000000 ); /* 1000 ms */ s390irq_spin_lock_irqsave( irq, flags); } /* endif */ } /* endif */ if ( count == 3 ) { /* give it a very last try ... */ ioinfo[irq]->irq_desc.handler->disable(irq); if ( ioinfo[irq]->ui.flags.busy ) { printk( KERN_CRIT"free_irq(%04X) " "- device %04X busy, retry " "count exceeded\n", irq, ioinfo[irq]->devstat.devno); } /* endif */ break; /* sigh, let's give up ... */ } /* endif */ } /* endif */ } while ( ret == -EBUSY ); if ( init_IRQ_complete ) kfree( ioinfo[irq]->irq_desc.action ); ioinfo[irq]->irq_desc.action = NULL; ioinfo[irq]->ui.flags.ready = 0; ioinfo[irq]->irq_desc.handler->enable = &enable_none; ioinfo[irq]->irq_desc.handler->disable = &disable_none; ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */ s390irq_spin_unlock_irqrestore( irq, flags); } else { s390irq_spin_unlock_irqrestore( irq, flags); printk("free_irq() : error, dev_id does not match !"); } /* endif */ } else { s390irq_spin_unlock_irqrestore( irq, flags); printk("free_irq() : error, no action block ... !"); } /* endif */}/* * Generic enable/disable code */int disable_irq(unsigned int irq){ unsigned long flags; int ret; if ( ioinfo[irq] == INVALID_STORAGE_AREA ) return( -ENODEV); s390irq_spin_lock_irqsave(irq, flags); /* * At this point we may actually have a pending interrupt being active * on another CPU. So don't touch the IRQ_INPROGRESS bit.. */ ioinfo[irq]->irq_desc.status |= IRQ_DISABLED; ret = ioinfo[irq]->irq_desc.handler->disable(irq); s390irq_spin_unlock_irqrestore(irq, flags); synchronize_irq(); return( ret);}int enable_irq(unsigned int irq){ unsigned long flags; int ret; if ( ioinfo[irq] == INVALID_STORAGE_AREA ) return( -ENODEV); s390irq_spin_lock_irqsave(irq, flags); ioinfo[irq]->irq_desc.status = 0; ret = ioinfo[irq]->irq_desc.handler->enable(irq); s390irq_spin_unlock_irqrestore(irq, flags); return(ret);}/* * Enable IRQ by modifying the subchannel */static int enable_subchannel( unsigned int irq){ int ret; int ccode; int retry = 5; if ( irq > highest_subchannel || irq < 0 ) { return( -ENODEV ); } /* endif */ if ( ioinfo[irq] == INVALID_STORAGE_AREA ) return( -ENODEV); /* * If a previous disable request is pending we reset it. However, this * status implies that the device may (still) be not-operational. */ if ( ioinfo[irq]->ui.flags.d_disable ) { ioinfo[irq]->ui.flags.d_disable = 0; ret = 0; } else { ccode = stsch(irq, &(ioinfo[irq]->schib) ); if ( ccode ) { ret = -ENODEV; } else { ioinfo[irq]->schib.pmcw.ena = 1; do { ccode = msch( irq, &(ioinfo[irq]->schib) ); switch (ccode) { case 0: ret = 0; break; case 1: /* * very bad, requires interrupt alike * processing, where "rbh" is a dummy * parameter for interface compatibility * only. Bottom-half handling cannot be * required as this must be an * unsolicited interrupt (!busy). */ ioinfo[irq]->ui.flags.s_pend = 1; s390_process_IRQ( irq ); ioinfo[irq]->ui.flags.s_pend = 0; ret = -EIO; /* might be overwritten */ /* ... on re-driving */ /* ... the msch() */ retry--; break; case 3: ioinfo[irq]->ui.flags.oper = 0; ret = -ENODEV; break; default: printk( KERN_CRIT"enable_subchannel(%04X) " " : ccode 2 on msch() for device " "%04X received !\n", irq, ioinfo[irq]->devstat.devno); ret = -ENODEV; // never reached } } while ( (ccode == 1) && retry ); } /* endif */ } /* endif */ return( ret );}/* * Disable IRQ by modifying the subchannel */static int disable_subchannel( unsigned int irq){ int cc; /* condition code */ int ret; /* function return value */ int retry = 5; if ( irq > highest_subchannel ) { ret = -ENODEV; } if ( ioinfo[irq] == INVALID_STORAGE_AREA ) { return( -ENODEV); } else if ( ioinfo[irq]->ui.flags.busy ) { /* * the disable function must not be called while there are * requests pending for completion ! */ ret = -EBUSY; } else { /* * If device isn't operational we have to perform delayed * disabling when the next interrupt occurs - unless the * irq is re-requested prior to the interrupt to occur. */ cc = stsch(irq, &(ioinfo[irq]->schib) ); if ( cc == 3 ) { ioinfo[irq]->ui.flags.oper = 0; ioinfo[irq]->ui.flags.d_disable = 1; ret = 0; } else // cc == 0 { ioinfo[irq]->schib.pmcw.ena = 0; do { cc = msch( irq, &(ioinfo[irq]->schib) ); switch (cc) { case 0 : ret = 0; /* done */ break; case 1 : /* * very bad, requires interrupt alike * processing, where "rbh" is a dummy * parm for interface compatibility * only. Bottom-half handling cannot * be required as this must be an * unsolicited interrupt (!busy). */ ioinfo[irq]->ui.flags.s_pend = 1; s390_process_IRQ( irq ); ioinfo[irq]->ui.flags.s_pend = 0; ret = -EBUSY; /* might be overwritten */ /* ... on re-driving the */ /* ... msch() call */ retry--; break; case 2 : /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -