Resent-Date: Tue, 10 Nov 1998 10:45:01 +0100 (MET)
From: zippel@fh-brandenburg.de (Roman Zippel)
Subject: Re: MFC3 serial overruns
To: alan@cymru.net (Alan Cox)
Date: Tue, 10 Nov 1998 10:45:19 +0100 (MET)
Cc: zippel@fh-brandenburg.de, alan@cymru.net, dorchain@wirbel.com,
        linux-m68k@phil.uni-sb.de
In-Reply-To: <199811062031.UAA08840@snowcrash.cymru.net> from "Alan Cox" at Nov 6, 98 08:31:53 pm
Resent-From: linux-m68k@phil.uni-sb.de

Hi,

> Ok so for m68k we need to implement some minimal hard real time for high
> interrupt numbers. Atomic operations, a hard rt safe mark_bh() and
> fixing cli to not lock all IRQ's out (ie have cli and very_cli())..

Ok, here is a basic framework to implement nonrealtime interrupts on m68k
machines. If someone wants to try it on the amiga (e.g. to get better serial
perfomance), I can help him. On the amiga the only problem are shared
interrupts, which cannot be realtime afterwards.
The interrupt function will look like this for nonrealtime interrupts:

	if ( rt_intenable ) {
		ScsiRtRequest();
	} else
		rt_setrequest( RT_SCSI );

ScsiRtRequest() is an example for the real interrupt routine, if the
interrupts are not allowed, a request is set and the interrupt will
be executed, when interrupts are enabled again.
The only problem is the number of interrupts is limited to 32 ( no
problem on the amiga, but AFAIK has more int sources, as they use
int vectors, so they need a dynamic allocation scheme).
Another problem is the cas instruction below should be protected
with CONFIG_RMW_INSNS (like xchg()).

Finally I have a minor patch for 2.0, although we don't support
SMP, the should make sure atomic operations are done in memory
and not in a register. (2.1 needs a similiar patch.)


--- linux-2.0/include/asm-m68k/atomic.h.org	Wed Aug 26 15:25:30 1998
+++ linux-2.0/include/asm-m68k/atomic.h	Tue Nov 10 10:30:49 1998
@@ -7,35 +7,38 @@
  */
 
 /*
- * We do not have SMP m68k systems, so we don't have to deal with that.
+ * Make sure gcc doesn't try to be clever and move things around
+ * on us. We need to use _exactly_ the address the user gave us,
+ * not some alias that contains the same information.
  */
+#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x)
 
 typedef int atomic_t;
 
 static __inline__ void atomic_add(atomic_t i, atomic_t *v)
 {
-	__asm__ __volatile__("addl %1,%0" : : "m" (*v), "id" (i));
+	__asm__ __volatile__("addl %1,%0" : : "m" (__atomic_fool_gcc(v)), "id" (i));
 }
 
 static __inline__ void atomic_sub(atomic_t i, atomic_t *v)
 {
-	__asm__ __volatile__("subl %1,%0" : : "m" (*v), "id" (i));
+	__asm__ __volatile__("subl %1,%0" : : "m" (__atomic_fool_gcc(v)), "id" (i));
 }
 
 static __inline__ void atomic_inc(atomic_t *v)
 {
-	__asm__ __volatile__("addql #1,%0" : : "m" (*v));
+	__asm__ __volatile__("addql #1,%0" : : "m" (__atomic_fool_gcc(v)));
 }
 
 static __inline__ void atomic_dec(atomic_t *v)
 {
-	__asm__ __volatile__("subql #1,%0" : : "m" (*v));
+	__asm__ __volatile__("subql #1,%0" : : "m" (__atomic_fool_gcc(v)));
 }
 
 static __inline__ int atomic_dec_and_test(atomic_t *v)
 {
 	char c;
-	__asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c) : "m" (*v));
+	__asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c) : "m" (__atomic_fool_gcc(v)));
 	return c != 0;
 }
 


/***** rt sources (example) *****/

#define RT_SERIAL		0
#define RT_SCSI			1
#define RT_LANCE		20

/************ system.h ****************/

extern volatile u32 rt_intenable;
extern volatile u32 rt_intrequest;
extern void do_rt_request(void);

static inline void sti( void )
{
	do_rt_request();
	rt_enable = 1;
	__sti();
}

static inline void cli( void )
{
	rt_intenable = 0;
}

#define save_flags( x )			({(x) = rt_intenable;})

static inline void restore_flags( unsigned long enable )
{
#ifdef DEBUG
	if ( enable != 0 && enable != 1 )
		printk( "\nrestore_flags: flags != 0/1!!! (%lx,%p)\n", enable, __builtin_return_address(0) );
#endif
	if ( (rt_intenable = enable) )
		do_rt_request();
}

struct __rt_dummy { unsigned long a[100]; };
#define __rt(x) ((volatile struct __rt_dummy *)(x))

/* -------> initialize the data from a memory location */
#define rt_init( ptr , data )	((data) = *(ptr))

/* -------> reserve the data from a memory location (dummy here) */
#define rt_reserve( ptr, data )

/* -------> check if the data is still the same, 
	    if yes update data otherwise don't change data */
#define rt_update( ptr, data, new )						\
({										\
	u8 res;									\
	asm volatile ( "cas.l %2,%3,%4; seq %0" : "=d" (res), "=&d" (data) :	\
		       "1" (data), "d" ((u32)(new)), "m" (*__rt(ptr)));		\
	res != 0;								\
})

#define rt_exchange( ptr, old, new )						\
({										\
	old = *(ptr);								\
	asm volatile ( "1:": "=&d" (old) : "0" (old) );				\
	asm volatile ( "cas.l %1,%2,%3; jne 1b" : "=&d" (old) : "0" (old),	\
		       "d" ((u32)(new)), "m" (*__rt(ptr)) );			\
})


/* -------> clear the intrequest mask and return the old value */
extern inline u32 rt_clrrequest( void )
{
	u32 oldval;

	rt_exchange( &rt_intrequest, oldval, 0 );
	return oldval;
}

/* -------> set an intrequest, this has to be done atomic! */
#define rt_setrequest( bit ) ({					       \
	card8 __oldval;						       \
								       \
	asm volatile ( "bfset %2{%1,#1}; sne %0"		       \
		: "=d" (__oldval) : "id" (bit), "m" (rt_intrequest) ); \
	__oldval;						       \
})

#define rt_getrequest( val ) ({				      \
	u32 __bit;					      \
							      \
	asm volatile ( "bfffo %1{#0,#32},%0; bfclr %1{%0,#1}" \
		: "=d" (__bit), "=d" (val) : "1" (val) );     \
	__bit;						      \
})


/******************* int.c ****************/

volatile u32 rt_intenable = 0;
volatile u32 rt_intrequest = 0;

void do_rt_request( void )
{
	u32 request, bit;

	/* -------> clear the global request mask and return the old one */
	request = rt_clrrequest();
	while ( request ) {
		/* -------> get the next request bit and clear it in the mask */
		switch ( bit = rt_getrequest(request) ) {
		case RT_SERIAL:
			SerialRtRequest();
			break;
		case RT_SCSI:
			ScsiRtRequest();
			break;
		case RT_LANCE:
			LanceRtRequest();
			break;
		default:
			printk("\nUnknown rt request %d\n", bit);
		}
	}
}

