static void cheetah_xcall_deliver()

in kernel/smp_64.c [483:624]


static void cheetah_xcall_deliver(struct trap_per_cpu *tb, int cnt)
{
	int nack_busy_id, is_jbus, need_more;
	u64 *mondo, pstate, ver, busy_mask;
	u16 *cpu_list;

	cpu_list = __va(tb->cpu_list_pa);
	mondo = __va(tb->cpu_mondo_block_pa);

	/* Unfortunately, someone at Sun had the brilliant idea to make the
	 * busy/nack fields hard-coded by ITID number for this Ultra-III
	 * derivative processor.
	 */
	__asm__ ("rdpr %%ver, %0" : "=r" (ver));
	is_jbus = ((ver >> 32) == __JALAPENO_ID ||
		   (ver >> 32) == __SERRANO_ID);

	__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));

retry:
	need_more = 0;
	__asm__ __volatile__("wrpr %0, %1, %%pstate\n\t"
			     : : "r" (pstate), "i" (PSTATE_IE));

	/* Setup the dispatch data registers. */
	__asm__ __volatile__("stxa	%0, [%3] %6\n\t"
			     "stxa	%1, [%4] %6\n\t"
			     "stxa	%2, [%5] %6\n\t"
			     "membar	#Sync\n\t"
			     : /* no outputs */
			     : "r" (mondo[0]), "r" (mondo[1]), "r" (mondo[2]),
			       "r" (0x40), "r" (0x50), "r" (0x60),
			       "i" (ASI_INTR_W));

	nack_busy_id = 0;
	busy_mask = 0;
	{
		int i;

		for (i = 0; i < cnt; i++) {
			u64 target, nr;

			nr = cpu_list[i];
			if (nr == 0xffff)
				continue;

			target = (nr << 14) | 0x70;
			if (is_jbus) {
				busy_mask |= (0x1UL << (nr * 2));
			} else {
				target |= (nack_busy_id << 24);
				busy_mask |= (0x1UL <<
					      (nack_busy_id * 2));
			}
			__asm__ __volatile__(
				"stxa	%%g0, [%0] %1\n\t"
				"membar	#Sync\n\t"
				: /* no outputs */
				: "r" (target), "i" (ASI_INTR_W));
			nack_busy_id++;
			if (nack_busy_id == 32) {
				need_more = 1;
				break;
			}
		}
	}

	/* Now, poll for completion. */
	{
		u64 dispatch_stat, nack_mask;
		long stuck;

		stuck = 100000 * nack_busy_id;
		nack_mask = busy_mask << 1;
		do {
			__asm__ __volatile__("ldxa	[%%g0] %1, %0"
					     : "=r" (dispatch_stat)
					     : "i" (ASI_INTR_DISPATCH_STAT));
			if (!(dispatch_stat & (busy_mask | nack_mask))) {
				__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
						     : : "r" (pstate));
				if (unlikely(need_more)) {
					int i, this_cnt = 0;
					for (i = 0; i < cnt; i++) {
						if (cpu_list[i] == 0xffff)
							continue;
						cpu_list[i] = 0xffff;
						this_cnt++;
						if (this_cnt == 32)
							break;
					}
					goto retry;
				}
				return;
			}
			if (!--stuck)
				break;
		} while (dispatch_stat & busy_mask);

		__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
				     : : "r" (pstate));

		if (dispatch_stat & busy_mask) {
			/* Busy bits will not clear, continue instead
			 * of freezing up on this cpu.
			 */
			printk("CPU[%d]: mondo stuckage result[%016llx]\n",
			       smp_processor_id(), dispatch_stat);
		} else {
			int i, this_busy_nack = 0;

			/* Delay some random time with interrupts enabled
			 * to prevent deadlock.
			 */
			udelay(2 * nack_busy_id);

			/* Clear out the mask bits for cpus which did not
			 * NACK us.
			 */
			for (i = 0; i < cnt; i++) {
				u64 check_mask, nr;

				nr = cpu_list[i];
				if (nr == 0xffff)
					continue;

				if (is_jbus)
					check_mask = (0x2UL << (2*nr));
				else
					check_mask = (0x2UL <<
						      this_busy_nack);
				if ((dispatch_stat & check_mask) == 0)
					cpu_list[i] = 0xffff;
				this_busy_nack += 2;
				if (this_busy_nack == 64)
					break;
			}

			goto retry;
		}
	}
}