aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/scsi/sun3x_esp.c
blob: 80fb3f88af2edd937ebdf2df51e8f4efcca67ee3 (plain) (tree)
























                                                              























                                                                             
                                                     




                                  
                                             










































                                                                                 
                                                          









































































































































































































































                                                                                                 
                                                                                    


                                                                         
                                                                     









                                                                         
                                                                      

                        
                                               





                                          
                                                                       








                                                                         
                                                    





















                                                         
/* sun3x_esp.c:  EnhancedScsiProcessor Sun3x SCSI driver code.
 *
 * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
 *
 * Based on David S. Miller's esp driver
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/delay.h>
#include <linux/interrupt.h>

#include "scsi.h"
#include <scsi/scsi_host.h>
#include "NCR53C9x.h"

#include <asm/sun3x.h>
#include <asm/dvma.h>
#include <asm/irq.h>

static void dma_barrier(struct NCR_ESP *esp);
static int  dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
static int  dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
static void dma_drain(struct NCR_ESP *esp);
static void dma_invalidate(struct NCR_ESP *esp);
static void dma_dump_state(struct NCR_ESP *esp);
static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length);
static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length);
static void dma_ints_off(struct NCR_ESP *esp);
static void dma_ints_on(struct NCR_ESP *esp);
static int  dma_irq_p(struct NCR_ESP *esp);
static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr);
static int  dma_ports_p(struct NCR_ESP *esp);
static void dma_reset(struct NCR_ESP *esp);
static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp);
static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp);
static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp);
static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp);
static void dma_advance_sg (Scsi_Cmnd *sp);

/* Detecting ESP chips on the machine.  This is the simple and easy
 * version.
 */
int sun3x_esp_detect(struct scsi_host_template *tpnt)
{
	struct NCR_ESP *esp;
	struct ConfigDev *esp_dev;

	esp_dev = 0;
	esp = esp_allocate(tpnt, esp_dev, 0);

	/* Do command transfer with DMA */
	esp->do_pio_cmds = 0;

	/* Required functions */
	esp->dma_bytes_sent = &dma_bytes_sent;
	esp->dma_can_transfer = &dma_can_transfer;
	esp->dma_dump_state = &dma_dump_state;
	esp->dma_init_read = &dma_init_read;
	esp->dma_init_write = &dma_init_write;
	esp->dma_ints_off = &dma_ints_off;
	esp->dma_ints_on = &dma_ints_on;
	esp->dma_irq_p = &dma_irq_p;
	esp->dma_ports_p = &dma_ports_p;
	esp->dma_setup = &dma_setup;

	/* Optional functions */
	esp->dma_barrier = &dma_barrier;
	esp->dma_invalidate = &dma_invalidate;
	esp->dma_drain = &dma_drain;
	esp->dma_irq_entry = 0;
	esp->dma_irq_exit = 0;
	esp->dma_led_on = 0;
	esp->dma_led_off = 0;
	esp->dma_poll = &dma_poll;
	esp->dma_reset = &dma_reset;

        /* virtual DMA functions */
        esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
        esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
        esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one;
        esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl;
        esp->dma_advance_sg = &dma_advance_sg;
	    
	/* SCSI chip speed */
	esp->cfreq = 20000000;
	esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE);
	esp->dregs = (void *)SUN3X_ESP_DMA;

	esp->esp_command = (volatile unsigned char *)dvma_malloc(DVMA_PAGE_SIZE);
	esp->esp_command_dvma = dvma_vtob((unsigned long)esp->esp_command);

	esp->irq = 2;
	if (request_irq(esp->irq, esp_intr, IRQF_DISABLED,
			"SUN3X SCSI", esp->ehost)) {
		esp_deallocate(esp);
		return 0;
	}

	esp->scsi_id = 7;
	esp->diff = 0;

	esp_initialize(esp);

 	/* for reasons beyond my knowledge (and which should likely be fixed)
 	   sync mode doesn't work on a 3/80 at 5mhz.  but it does at 4. */
 	esp->sync_defp = 0x3f;

	printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,
	       esps_in_use);
	esps_running = esps_in_use;
	return esps_in_use;
}

static void dma_do_drain(struct NCR_ESP *esp)
{
 	struct sparc_dma_registers *dregs =
 		(struct sparc_dma_registers *) esp->dregs;
 	
 	int count = 500000;
 
 	while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) 
 		udelay(1);
 
 	if(!count) {
 		printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
 	}
 
 	dregs->cond_reg |= DMA_FIFO_STDRAIN;
 	
 	count = 500000;
 
 	while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) 
 		udelay(1);
 
 	if(!count) {
 		printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
 	}
 
}
 
static void dma_barrier(struct NCR_ESP *esp)
{
  	struct sparc_dma_registers *dregs =
  		(struct sparc_dma_registers *) esp->dregs;
 	int count = 500000;
  
 	while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0))
  		udelay(1);
 
 	if(!count) {
 		printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
 	}
 
  	dregs->cond_reg &= ~(DMA_ENABLE);
}

/* This uses various DMA csr fields and the fifo flags count value to
 * determine how many bytes were successfully sent/received by the ESP.
 */
static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
{
	struct sparc_dma_registers *dregs = 
		(struct sparc_dma_registers *) esp->dregs;

	int rval = dregs->st_addr - esp->esp_command_dvma;

	return rval - fifo_count;
}

static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
{
	return sp->SCp.this_residual;
}

static void dma_drain(struct NCR_ESP *esp)
{
	struct sparc_dma_registers *dregs =
		(struct sparc_dma_registers *) esp->dregs;
	int count = 500000;

	if(dregs->cond_reg & DMA_FIFO_ISDRAIN) {
		dregs->cond_reg |= DMA_FIFO_STDRAIN;
		while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0))
			udelay(1);
		if(!count) {
			printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
		}

	}
}

static void dma_invalidate(struct NCR_ESP *esp)
{
	struct sparc_dma_registers *dregs =
		(struct sparc_dma_registers *) esp->dregs;

	__u32 tmp;
	int count = 500000;

	while(((tmp = dregs->cond_reg) & DMA_PEND_READ) && (--count > 0)) 
		udelay(1);

	if(!count) {
		printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
	}

	dregs->cond_reg = tmp | DMA_FIFO_INV;
	dregs->cond_reg &= ~DMA_FIFO_INV;

}

static void dma_dump_state(struct NCR_ESP *esp)
{
	struct sparc_dma_registers *dregs =
		(struct sparc_dma_registers *) esp->dregs;

	ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%08lx>\n",
		esp->esp_id, dregs->cond_reg, dregs->st_addr));
}

static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length)
{
	struct sparc_dma_registers *dregs = 
		(struct sparc_dma_registers *) esp->dregs;

	dregs->st_addr = vaddress;
	dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE);
}

static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length)
{
	struct sparc_dma_registers *dregs = 
		(struct sparc_dma_registers *) esp->dregs;

	/* Set up the DMA counters */

	dregs->st_addr = vaddress;
	dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE);
}

static void dma_ints_off(struct NCR_ESP *esp)
{
	DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs);
}

static void dma_ints_on(struct NCR_ESP *esp)
{
	DMA_INTSON((struct sparc_dma_registers *) esp->dregs);
}

static int dma_irq_p(struct NCR_ESP *esp)
{
	return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs);
}

static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr)
{
	int count = 50;
	dma_do_drain(esp);

	/* Wait till the first bits settle. */
	while((*(volatile unsigned char *)vaddr == 0xff) && (--count > 0))
		udelay(1);

	if(!count) {
//		printk("%s:%d timeout expire (data %02x)\n", __FILE__, __LINE__,
//		       esp_read(esp->eregs->esp_fdata));
		//mach_halt();
		vaddr[0] = esp_read(esp->eregs->esp_fdata);
		vaddr[1] = esp_read(esp->eregs->esp_fdata);
	}

}	

static int dma_ports_p(struct NCR_ESP *esp)
{
	return (((struct sparc_dma_registers *) esp->dregs)->cond_reg 
			& DMA_INT_ENAB);
}

/* Resetting various pieces of the ESP scsi driver chipset/buses. */
static void dma_reset(struct NCR_ESP *esp)
{
	struct sparc_dma_registers *dregs =
		(struct sparc_dma_registers *)esp->dregs;

	/* Punt the DVMA into a known state. */
	dregs->cond_reg |= DMA_RST_SCSI;
	dregs->cond_reg &= ~(DMA_RST_SCSI);
	DMA_INTSON(dregs);
}

static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
{
	struct sparc_dma_registers *dregs = 
		(struct sparc_dma_registers *) esp->dregs;
	unsigned long nreg = dregs->cond_reg;

//	printk("dma_setup %c addr %08x cnt %08x\n",
//	       write ? 'W' : 'R', addr, count);

	dma_do_drain(esp);

	if(write)
		nreg |= DMA_ST_WRITE;
	else {
		nreg &= ~(DMA_ST_WRITE);
	}
		
	nreg |= DMA_ENABLE;
	dregs->cond_reg = nreg;
	dregs->st_addr = addr;
}

static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp)
{
    sp->SCp.have_data_in = dvma_map((unsigned long)sp->SCp.buffer,
				       sp->SCp.this_residual);
    sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in);
}

static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp)
{
    int sz = sp->SCp.buffers_residual;
    struct scatterlist *sg = sp->SCp.buffer;

    while (sz >= 0) {
	    sg[sz].dma_address = dvma_map((unsigned long)page_address(sg[sz].page) +
					   sg[sz].offset, sg[sz].length);
	    sz--;
    }
    sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dma_address);
}

static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp)
{
    dvma_unmap((char *)sp->SCp.have_data_in);
}

static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp)
{
    int sz = sp->use_sg - 1;
    struct scatterlist *sg = (struct scatterlist *)sp->request_buffer;
                        
    while(sz >= 0) {
        dvma_unmap((char *)sg[sz].dma_address);
        sz--;
    }
}

static void dma_advance_sg (Scsi_Cmnd *sp)
{
    sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dma_address);
}

static int sun3x_esp_release(struct Scsi_Host *instance)
{
	/* this code does not support being compiled as a module */	 
	return 1;

}

static struct scsi_host_template driver_template = {
	.proc_name		= "sun3x_esp",
	.proc_info		= &esp_proc_info,
	.name			= "Sun ESP 100/100a/200",
	.detect			= sun3x_esp_detect,
	.release                = sun3x_esp_release,
	.slave_alloc		= esp_slave_alloc,
	.slave_destroy		= esp_slave_destroy,
	.info			= esp_info,
	.queuecommand		= esp_queue,
	.eh_abort_handler	= esp_abort,
	.eh_bus_reset_handler	= esp_reset,
	.can_queue		= 7,
	.this_id		= 7,
	.sg_tablesize		= SG_ALL,
	.cmd_per_lun		= 1,
	.use_clustering		= DISABLE_CLUSTERING,
};


#include "scsi_module.c"

MODULE_LICENSE("GPL");
pan class="hl kwb">int len_packet(const uint8_t *packet) { return (int)((struct hvsi_header *)packet)->len; } static inline int is_header(const uint8_t *packet) { struct hvsi_header *header = (struct hvsi_header *)packet; return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; } static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) { if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) return 0; /* don't even have the packet header */ if (hp->inbuf_end < (packet + len_packet(packet))) return 0; /* don't have the rest of the packet */ return 1; } /* shift remaining bytes in packetbuf down */ static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) { int remaining = (int)(hp->inbuf_end - read_to); pr_debug("%s: %i chars remain\n", __FUNCTION__, remaining); if (read_to != hp->inbuf) memmove(hp->inbuf, read_to, remaining); hp->inbuf_end = hp->inbuf + remaining; } #ifdef DEBUG #define dbg_dump_packet(packet) dump_packet(packet) #define dbg_dump_hex(data, len) dump_hex(data, len) #else #define dbg_dump_packet(packet) do { } while (0) #define dbg_dump_hex(data, len) do { } while (0) #endif static void dump_hex(const uint8_t *data, int len) { int i; printk(" "); for (i=0; i < len; i++) printk("%.2x", data[i]); printk("\n "); for (i=0; i < len; i++) { if (isprint(data[i])) printk("%c", data[i]); else printk("."); } printk("\n"); } static void dump_packet(uint8_t *packet) { struct hvsi_header *header = (struct hvsi_header *)packet; printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, header->seqno); dump_hex(packet, header->len); } static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) { unsigned long got; got = hvc_get_chars(hp->vtermno, buf, count); return got; } static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) { struct hvsi_control *header = (struct hvsi_control *)packet; switch (header->verb) { case VSV_MODEM_CTL_UPDATE: if ((header->word & HVSI_TSCD) == 0) { /* CD went away; no more connection */ pr_debug("hvsi%i: CD dropped\n", hp->index); hp->mctrl &= TIOCM_CD; if (!(hp->tty->flags & CLOCAL)) *to_hangup = hp->tty; } break; case VSV_CLOSE_PROTOCOL: pr_debug("hvsi%i: service processor came back\n", hp->index); if (hp->state != HVSI_CLOSED) { *to_handshake = hp; } break; default: printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", hp->index); dump_packet(packet); break; } } static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) { struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; switch (hp->state) { case HVSI_WAIT_FOR_VER_RESPONSE: __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); break; case HVSI_WAIT_FOR_MCTRL_RESPONSE: hp->mctrl = 0; if (resp->u.mctrl_word & HVSI_TSDTR) hp->mctrl |= TIOCM_DTR; if (resp->u.mctrl_word & HVSI_TSCD) hp->mctrl |= TIOCM_CD; __set_state(hp, HVSI_OPEN); break; default: printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); dump_packet(packet); break; } } /* respond to service processor's version query */ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) { struct hvsi_query_response packet __ALIGNED__; int wrote; packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; packet.len = sizeof(struct hvsi_query_response); packet.seqno = atomic_inc_return(&hp->seqno); packet.verb = VSV_SEND_VERSION_NUMBER; packet.u.version = HVSI_VERSION; packet.query_seqno = query_seqno+1; pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); if (wrote != packet.len) { printk(KERN_ERR "hvsi%i: couldn't send query response!\n", hp->index); return -EIO; } return 0; } static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) { struct hvsi_query *query = (struct hvsi_query *)packet; switch (hp->state) { case HVSI_WAIT_FOR_VER_QUERY: hvsi_version_respond(hp, query->seqno); __set_state(hp, HVSI_OPEN); break; default: printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); dump_packet(packet); break; } } static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) { int i; for (i=0; i < len; i++) { char c = buf[i]; #ifdef CONFIG_MAGIC_SYSRQ if (c == '\0') { hp->sysrq = 1; continue; } else if (hp->sysrq) { handle_sysrq(c, NULL, hp->tty); hp->sysrq = 0; continue; } #endif /* CONFIG_MAGIC_SYSRQ */ tty_insert_flip_char(hp->tty, c, 0); } } /* * We could get 252 bytes of data at once here. But the tty layer only * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow * it. Accordingly we won't send more than 128 bytes at a time to the flip * buffer, which will give the tty buffer a chance to throttle us. Should the * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be * revisited. */ #define TTY_THRESHOLD_THROTTLE 128 static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet) { const struct hvsi_header *header = (const struct hvsi_header *)packet; const uint8_t *data = packet + sizeof(struct hvsi_header); int datalen = header->len - sizeof(struct hvsi_header); int overflow = datalen - TTY_THRESHOLD_THROTTLE; pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); if (datalen == 0) return NULL; if (overflow > 0) { pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __FUNCTION__); datalen = TTY_THRESHOLD_THROTTLE; } hvsi_insert_chars(hp, data, datalen); if (overflow > 0) { /* * we still have more data to deliver, so we need to save off the * overflow and send it later */ pr_debug("%s: deferring overflow\n", __FUNCTION__); memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); hp->n_throttle = overflow; } return hp->tty; } /* * Returns true/false indicating data successfully read from hypervisor. * Used both to get packets for tty connections and to advance the state * machine during console handshaking (in which case tty = NULL and we ignore * incoming data). */ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, struct tty_struct **hangup, struct hvsi_struct **handshake) { uint8_t *packet = hp->inbuf; int chunklen; *flip = NULL; *hangup = NULL; *handshake = NULL; chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); if (chunklen == 0) { pr_debug("%s: 0-length read\n", __FUNCTION__); return 0; } pr_debug("%s: got %i bytes\n", __FUNCTION__, chunklen); dbg_dump_hex(hp->inbuf_end, chunklen); hp->inbuf_end += chunklen; /* handle all completed packets */ while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { struct hvsi_header *header = (struct hvsi_header *)packet; if (!is_header(packet)) { printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); /* skip bytes until we find a header or run out of data */ while ((packet < hp->inbuf_end) && (!is_header(packet))) packet++; continue; } pr_debug("%s: handling %i-byte packet\n", __FUNCTION__, len_packet(packet)); dbg_dump_packet(packet); switch (header->type) { case VS_DATA_PACKET_HEADER: if (!is_open(hp)) break; if (hp->tty == NULL) break; /* no tty buffer to put data in */ *flip = hvsi_recv_data(hp, packet); break; case VS_CONTROL_PACKET_HEADER: hvsi_recv_control(hp, packet, hangup, handshake); break; case VS_QUERY_RESPONSE_PACKET_HEADER: hvsi_recv_response(hp, packet); break; case VS_QUERY_PACKET_HEADER: hvsi_recv_query(hp, packet); break; default: printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", hp->index, header->type); dump_packet(packet); break; } packet += len_packet(packet); if (*hangup || *handshake) { pr_debug("%s: hangup or handshake\n", __FUNCTION__); /* * we need to send the hangup now before receiving any more data. * If we get "data, hangup, data", we can't deliver the second * data before the hangup. */ break; } } compact_inbuf(hp, packet); return 1; } static void hvsi_send_overflow(struct hvsi_struct *hp) { pr_debug("%s: delivering %i bytes overflow\n", __FUNCTION__, hp->n_throttle); hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); hp->n_throttle = 0; } /* * must get all pending data because we only get an irq on empty->non-empty * transition */ static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs) { struct hvsi_struct *hp = (struct hvsi_struct *)arg; struct tty_struct *flip; struct tty_struct *hangup; struct hvsi_struct *handshake; unsigned long flags; int again = 1; pr_debug("%s\n", __FUNCTION__); while (again) { spin_lock_irqsave(&hp->lock, flags); again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); spin_unlock_irqrestore(&hp->lock, flags); /* * we have to call tty_flip_buffer_push() and tty_hangup() outside our * spinlock. But we also have to keep going until we've read all the * available data. */ if (flip) { /* there was data put in the tty flip buffer */ tty_flip_buffer_push(flip); flip = NULL; } if (hangup) { tty_hangup(hangup); } if (handshake) { pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); schedule_work(&handshake->handshaker); } } spin_lock_irqsave(&hp->lock, flags); if (hp->tty && hp->n_throttle && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { /* we weren't hung up and we weren't throttled, so we can deliver the * rest now */ flip = hp->tty; hvsi_send_overflow(hp); } spin_unlock_irqrestore(&hp->lock, flags); if (flip) { tty_flip_buffer_push(flip); } return IRQ_HANDLED; } /* for boot console, before the irq handler is running */ static int __init poll_for_state(struct hvsi_struct *hp, int state) { unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; for (;;) { hvsi_interrupt(hp->virq, (void *)hp, NULL); /* get pending data */ if (hp->state == state) return 0; mdelay(5); if (time_after(jiffies, end_jiffies)) return -EIO; } } /* wait for irq handler to change our state */ static int wait_for_state(struct hvsi_struct *hp, int state) { int ret = 0; if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) ret = -EIO; return ret; } static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) { struct hvsi_query packet __ALIGNED__; int wrote; packet.type = VS_QUERY_PACKET_HEADER; packet.len = sizeof(struct hvsi_query); packet.seqno = atomic_inc_return(&hp->seqno); packet.verb = verb; pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); if (wrote != packet.len) { printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, wrote); return -EIO; } return 0; } static int hvsi_get_mctrl(struct hvsi_struct *hp) { int ret; set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); ret = hvsi_wait(hp, HVSI_OPEN); if (ret < 0) { printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); set_state(hp, HVSI_OPEN); return ret; } pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, hp->mctrl); return 0; } /* note that we can only set DTR */ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) { struct hvsi_control packet __ALIGNED__; int wrote; packet.type = VS_CONTROL_PACKET_HEADER, packet.seqno = atomic_inc_return(&hp->seqno); packet.len = sizeof(struct hvsi_control); packet.verb = VSV_SET_MODEM_CTL; packet.mask = HVSI_TSDTR; if (mctrl & TIOCM_DTR) packet.word = HVSI_TSDTR; pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); if (wrote != packet.len) { printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); return -EIO; } return 0; } static void hvsi_drain_input(struct hvsi_struct *hp) { uint8_t buf[HVSI_MAX_READ] __ALIGNED__; unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; while (time_before(end_jiffies, jiffies)) if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) break; } static int hvsi_handshake(struct hvsi_struct *hp) { int ret; /* * We could have a CLOSE or other data waiting for us before we even try * to open; try to throw it all away so we don't get confused. (CLOSE * is the first message sent up the pipe when the FSP comes online. We * need to distinguish between "it came up a while ago and we're the first * user" and "it was just reset before it saw our handshake packet".) */ hvsi_drain_input(hp); set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); if (ret < 0) { printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); return ret; } ret = hvsi_wait(hp, HVSI_OPEN); if (ret < 0) return ret; return 0; } static void hvsi_handshaker(void *arg) { struct hvsi_struct *hp = (struct hvsi_struct *)arg; if (hvsi_handshake(hp) >= 0) return; printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); if (is_console(hp)) { /* * ttys will re-attempt the handshake via hvsi_open, but * the console will not. */ printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); } } static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) { struct hvsi_data packet __ALIGNED__; int ret; BUG_ON(count > HVSI_MAX_OUTGOING_DATA); packet.type = VS_DATA_PACKET_HEADER; packet.seqno = atomic_inc_return(&hp->seqno); packet.len = count + sizeof(struct hvsi_header); memcpy(&packet.data, buf, count); ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); if (ret == packet.len) { /* return the number of chars written, not the packet length */ return count; } return ret; /* return any errors */ } static void hvsi_close_protocol(struct hvsi_struct *hp) { struct hvsi_control packet __ALIGNED__; packet.type = VS_CONTROL_PACKET_HEADER; packet.seqno = atomic_inc_return(&hp->seqno); packet.len = 6; packet.verb = VSV_CLOSE_PROTOCOL; pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); dbg_dump_hex((uint8_t*)&packet, packet.len); hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); } static int hvsi_open(struct tty_struct *tty, struct file *filp) { struct hvsi_struct *hp; unsigned long flags; int line = tty->index; int ret; pr_debug("%s\n", __FUNCTION__); if (line < 0 || line >= hvsi_count) return -ENODEV; hp = &hvsi_ports[line]; tty->driver_data = hp; tty->low_latency = 1; /* avoid throttle/tty_flip_buffer_push race */ mb(); if (hp->state == HVSI_FSP_DIED) return -EIO; spin_lock_irqsave(&hp->lock, flags); hp->tty = tty; hp->count++; atomic_set(&hp->seqno, 0); h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); spin_unlock_irqrestore(&hp->lock, flags); if (is_console(hp)) return 0; /* this has already been handshaked as the console */ ret = hvsi_handshake(hp); if (ret < 0) { printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); return ret; } ret = hvsi_get_mctrl(hp); if (ret < 0) { printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); return ret; } ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); if (ret < 0) { printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); return ret; } return 0; } /* wait for hvsi_write_worker to empty hp->outbuf */ static void hvsi_flush_output(struct hvsi_struct *hp) { wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ cancel_delayed_work(&hp->writer); flush_scheduled_work(); /* * it's also possible that our timeout expired and hvsi_write_worker * didn't manage to push outbuf. poof. */ hp->n_outbuf = 0; } static void hvsi_close(struct tty_struct *tty, struct file *filp) { struct hvsi_struct *hp = tty->driver_data; unsigned long flags; pr_debug("%s\n", __FUNCTION__); if (tty_hung_up_p(filp)) return; spin_lock_irqsave(&hp->lock, flags); if (--hp->count == 0) { hp->tty = NULL; hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ /* only close down connection if it is not the console */ if (!is_console(hp)) { h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ __set_state(hp, HVSI_CLOSED); /* * any data delivered to the tty layer after this will be * discarded (except for XON/XOFF) */ tty->closing = 1; spin_unlock_irqrestore(&hp->lock, flags); /* let any existing irq handlers finish. no more will start. */ synchronize_irq(hp->virq); /* hvsi_write_worker will re-schedule until outbuf is empty. */ hvsi_flush_output(hp); /* tell FSP to stop sending data */ hvsi_close_protocol(hp); /* * drain anything FSP is still in the middle of sending, and let * hvsi_handshake drain the rest on the next open. */ hvsi_drain_input(hp); spin_lock_irqsave(&hp->lock, flags); } } else if (hp->count < 0) printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", hp - hvsi_ports, hp->count); spin_unlock_irqrestore(&hp->lock, flags); } static void hvsi_hangup(struct tty_struct *tty) { struct hvsi_struct *hp = tty->driver_data; unsigned long flags; pr_debug("%s\n", __FUNCTION__); spin_lock_irqsave(&hp->lock, flags); hp->count = 0; hp->n_outbuf = 0; hp->tty = NULL; spin_unlock_irqrestore(&hp->lock, flags); } /* called with hp->lock held */ static void hvsi_push(struct hvsi_struct *hp) { int n; if (hp->n_outbuf <= 0) return; n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); if (n > 0) { /* success */ pr_debug("%s: wrote %i chars\n", __FUNCTION__, n); hp->n_outbuf = 0; } else if (n == -EIO) { __set_state(hp, HVSI_FSP_DIED); printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); } } /* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ static void hvsi_write_worker(void *arg) { struct hvsi_struct *hp = (struct hvsi_struct *)arg; unsigned long flags; #ifdef DEBUG static long start_j = 0; if (start_j == 0) start_j = jiffies; #endif /* DEBUG */ spin_lock_irqsave(&hp->lock, flags); pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); if (!is_open(hp)) { /* * We could have a non-open connection if the service processor died * while we were busily scheduling ourselves. In that case, it could * be minutes before the service processor comes back, so only try * again once a second. */ schedule_delayed_work(&hp->writer, HZ); goto out; } hvsi_push(hp); if (hp->n_outbuf > 0) schedule_delayed_work(&hp->writer, 10); else { #ifdef DEBUG pr_debug("%s: outbuf emptied after %li jiffies\n", __FUNCTION__, jiffies - start_j); start_j = 0; #endif /* DEBUG */ wake_up_all(&hp->emptyq); if (test_bit(TTY_DO_WRITE_WAKEUP, &hp->tty->flags) && hp->tty->ldisc.write_wakeup) hp->tty->ldisc.write_wakeup(hp->tty); wake_up_interruptible(&hp->tty->write_wait); } out: spin_unlock_irqrestore(&hp->lock, flags); } static int hvsi_write_room(struct tty_struct *tty) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; return N_OUTBUF - hp->n_outbuf; } static int hvsi_chars_in_buffer(struct tty_struct *tty) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; return hp->n_outbuf; } static int hvsi_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct hvsi_struct *hp = tty->driver_data; const char *source = buf; unsigned long flags; int total = 0; int origcount = count; spin_lock_irqsave(&hp->lock, flags); pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); if (!is_open(hp)) { /* we're either closing or not yet open; don't accept data */ pr_debug("%s: not open\n", __FUNCTION__); goto out; } /* * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls * will see there is no room in outbuf and return. */ while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { int chunksize = min(count, hvsi_write_room(hp->tty)); BUG_ON(hp->n_outbuf < 0); memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); hp->n_outbuf += chunksize; total += chunksize; source += chunksize; count -= chunksize; hvsi_push(hp); } if (hp->n_outbuf > 0) { /* * we weren't able to write it all to the hypervisor. * schedule another push attempt. */ schedule_delayed_work(&hp->writer, 10); } out: spin_unlock_irqrestore(&hp->lock, flags); if (total != origcount) pr_debug("%s: wanted %i, only wrote %i\n", __FUNCTION__, origcount, total); return total; } /* * I have never seen throttle or unthrottle called, so this little throttle * buffering scheme may or may not work. */ static void hvsi_throttle(struct tty_struct *tty) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; pr_debug("%s\n", __FUNCTION__); h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); } static void hvsi_unthrottle(struct tty_struct *tty) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; unsigned long flags; int shouldflip = 0; pr_debug("%s\n", __FUNCTION__); spin_lock_irqsave(&hp->lock, flags); if (hp->n_throttle) { hvsi_send_overflow(hp); shouldflip = 1; } spin_unlock_irqrestore(&hp->lock, flags); if (shouldflip) tty_flip_buffer_push(hp->tty); h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); } static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; hvsi_get_mctrl(hp); return hp->mctrl; } static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; unsigned long flags; uint16_t new_mctrl; /* we can only alter DTR */ clear &= TIOCM_DTR; set &= TIOCM_DTR; spin_lock_irqsave(&hp->lock, flags); new_mctrl = (hp->mctrl & ~clear) | set; if (hp->mctrl != new_mctrl) { hvsi_set_mctrl(hp, new_mctrl); hp->mctrl = new_mctrl; } spin_unlock_irqrestore(&hp->lock, flags); return 0; } static struct tty_operations hvsi_ops = { .open = hvsi_open, .close = hvsi_close, .write = hvsi_write, .hangup = hvsi_hangup, .write_room = hvsi_write_room, .chars_in_buffer = hvsi_chars_in_buffer, .throttle = hvsi_throttle, .unthrottle = hvsi_unthrottle, .tiocmget = hvsi_tiocmget, .tiocmset = hvsi_tiocmset, }; static int __init hvsi_init(void) { int i; hvsi_driver = alloc_tty_driver(hvsi_count); if (!hvsi_driver) return -ENOMEM; hvsi_driver->owner = THIS_MODULE; hvsi_driver->devfs_name = "hvsi/"; hvsi_driver->driver_name = "hvsi"; hvsi_driver->name = "hvsi"; hvsi_driver->major = HVSI_MAJOR; hvsi_driver->minor_start = HVSI_MINOR; hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; hvsi_driver->init_termios = tty_std_termios; hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; hvsi_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(hvsi_driver, &hvsi_ops); for (i=0; i < hvsi_count; i++) { struct hvsi_struct *hp = &hvsi_ports[i]; int ret = 1; ret = request_irq(hp->virq, hvsi_interrupt, SA_INTERRUPT, "hvsi", hp); if (ret) printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", hp->virq, ret); } hvsi_wait = wait_for_state; /* irqs active now */ if (tty_register_driver(hvsi_driver)) panic("Couldn't register hvsi console driver\n"); printk(KERN_INFO "HVSI: registered %i devices\n", hvsi_count); return 0; } device_initcall(hvsi_init); /***** console (not tty) code: *****/ static void hvsi_console_print(struct console *console, const char *buf, unsigned int count) { struct hvsi_struct *hp = &hvsi_ports[console->index]; char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; unsigned int i = 0, n = 0; int ret, donecr = 0; mb(); if (!is_open(hp)) return; /* * ugh, we have to translate LF -> CRLF ourselves, in place. * copied from hvc_console.c: */ while (count > 0 || i > 0) { if (count > 0 && i < sizeof(c)) { if (buf[n] == '\n' && !donecr) { c[i++] = '\r'; donecr = 1; } else { c[i++] = buf[n++]; donecr = 0; --count; } } else { ret = hvsi_put_chars(hp, c, i); if (ret < 0) i = 0; i -= ret; } } } static struct tty_driver *hvsi_console_device(struct console *console, int *index) { *index = console->index; return hvsi_driver; } static int __init hvsi_console_setup(struct console *console, char *options) { struct hvsi_struct *hp = &hvsi_ports[console->index]; int ret; if (console->index < 0 || console->index >= hvsi_count) return -1; /* give the FSP a chance to change the baud rate when we re-open */ hvsi_close_protocol(hp); ret = hvsi_handshake(hp); if (ret < 0) return ret; ret = hvsi_get_mctrl(hp); if (ret < 0) return ret; ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); if (ret < 0) return ret; hp->flags |= HVSI_CONSOLE; return 0; } static struct console hvsi_con_driver = { .name = "hvsi", .write = hvsi_console_print, .device = hvsi_console_device, .setup = hvsi_console_setup, .flags = CON_PRINTBUFFER, .index = -1, }; static int __init hvsi_console_init(void) { struct device_node *vty; hvsi_wait = poll_for_state; /* no irqs yet; must poll */ /* search device tree for vty nodes */ for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); vty != NULL; vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { struct hvsi_struct *hp; uint32_t *vtermno; uint32_t *irq; vtermno = (uint32_t *)get_property(vty, "reg", NULL); irq = (uint32_t *)get_property(vty, "interrupts", NULL); if (!vtermno || !irq) continue; if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { of_node_put(vty); break; } hp = &hvsi_ports[hvsi_count]; INIT_WORK(&hp->writer, hvsi_write_worker, hp); INIT_WORK(&hp->handshaker, hvsi_handshaker, hp); init_waitqueue_head(&hp->emptyq); init_waitqueue_head(&hp->stateq); spin_lock_init(&hp->lock); hp->index = hvsi_count; hp->inbuf_end = hp->inbuf; hp->state = HVSI_CLOSED; hp->vtermno = *vtermno; hp->virq = virt_irq_create_mapping(irq[0]); if (hp->virq == NO_IRQ) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", __FUNCTION__, hp->virq); continue; } else hp->virq = irq_offset_up(hp->virq); hvsi_count++; } if (hvsi_count) register_console(&hvsi_con_driver); return 0; } console_initcall(hvsi_console_init);