18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SCSI low-level driver for the 53c94 SCSI bus adaptor found 48c2ecf20Sopenharmony_ci * on Power Macintosh computers, controlling the external SCSI chain. 58c2ecf20Sopenharmony_ci * We assume the 53c94 is connected to a DBDMA (descriptor-based DMA) 68c2ecf20Sopenharmony_ci * controller. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Paul Mackerras, August 1996. 98c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 178c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 188c2ecf20Sopenharmony_ci#include <linux/stat.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/pci.h> 238c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 248c2ecf20Sopenharmony_ci#include <asm/dbdma.h> 258c2ecf20Sopenharmony_ci#include <asm/io.h> 268c2ecf20Sopenharmony_ci#include <asm/prom.h> 278c2ecf20Sopenharmony_ci#include <asm/macio.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 308c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 318c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 328c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "mac53c94.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cienum fsc_phase { 378c2ecf20Sopenharmony_ci idle, 388c2ecf20Sopenharmony_ci selecting, 398c2ecf20Sopenharmony_ci dataing, 408c2ecf20Sopenharmony_ci completing, 418c2ecf20Sopenharmony_ci busfreeing, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct fsc_state { 458c2ecf20Sopenharmony_ci struct mac53c94_regs __iomem *regs; 468c2ecf20Sopenharmony_ci int intr; 478c2ecf20Sopenharmony_ci struct dbdma_regs __iomem *dma; 488c2ecf20Sopenharmony_ci int dmaintr; 498c2ecf20Sopenharmony_ci int clk_freq; 508c2ecf20Sopenharmony_ci struct Scsi_Host *host; 518c2ecf20Sopenharmony_ci struct scsi_cmnd *request_q; 528c2ecf20Sopenharmony_ci struct scsi_cmnd *request_qtail; 538c2ecf20Sopenharmony_ci struct scsi_cmnd *current_req; /* req we're currently working on */ 548c2ecf20Sopenharmony_ci enum fsc_phase phase; /* what we're currently trying to do */ 558c2ecf20Sopenharmony_ci struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */ 568c2ecf20Sopenharmony_ci void *dma_cmd_space; 578c2ecf20Sopenharmony_ci struct pci_dev *pdev; 588c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 598c2ecf20Sopenharmony_ci struct macio_dev *mdev; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void mac53c94_init(struct fsc_state *); 638c2ecf20Sopenharmony_cistatic void mac53c94_start(struct fsc_state *); 648c2ecf20Sopenharmony_cistatic void mac53c94_interrupt(int, void *); 658c2ecf20Sopenharmony_cistatic irqreturn_t do_mac53c94_interrupt(int, void *); 668c2ecf20Sopenharmony_cistatic void cmd_done(struct fsc_state *, int result); 678c2ecf20Sopenharmony_cistatic void set_dma_cmds(struct fsc_state *, struct scsi_cmnd *); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int mac53c94_queue_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct fsc_state *state; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#if 0 758c2ecf20Sopenharmony_ci if (cmd->sc_data_direction == DMA_TO_DEVICE) { 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci printk(KERN_DEBUG "mac53c94_queue %p: command is", cmd); 788c2ecf20Sopenharmony_ci for (i = 0; i < cmd->cmd_len; ++i) 798c2ecf20Sopenharmony_ci printk(KERN_CONT " %.2x", cmd->cmnd[i]); 808c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 818c2ecf20Sopenharmony_ci printk(KERN_DEBUG "use_sg=%d request_bufflen=%d request_buffer=%p\n", 828c2ecf20Sopenharmony_ci scsi_sg_count(cmd), scsi_bufflen(cmd), scsi_sglist(cmd)); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci#endif 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci cmd->scsi_done = done; 878c2ecf20Sopenharmony_ci cmd->host_scribble = NULL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci state = (struct fsc_state *) cmd->device->host->hostdata; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (state->request_q == NULL) 928c2ecf20Sopenharmony_ci state->request_q = cmd; 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci state->request_qtail->host_scribble = (void *) cmd; 958c2ecf20Sopenharmony_ci state->request_qtail = cmd; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (state->phase == idle) 988c2ecf20Sopenharmony_ci mac53c94_start(state); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(mac53c94_queue) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int mac53c94_host_reset(struct scsi_cmnd *cmd) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct fsc_state *state = (struct fsc_state *) cmd->device->host->hostdata; 1088c2ecf20Sopenharmony_ci struct mac53c94_regs __iomem *regs = state->regs; 1098c2ecf20Sopenharmony_ci struct dbdma_regs __iomem *dma = state->dma; 1108c2ecf20Sopenharmony_ci unsigned long flags; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci spin_lock_irqsave(cmd->device->host->host_lock, flags); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci writel((RUN|PAUSE|FLUSH|WAKE) << 16, &dma->control); 1158c2ecf20Sopenharmony_ci writeb(CMD_SCSI_RESET, ®s->command); /* assert RST */ 1168c2ecf20Sopenharmony_ci udelay(100); /* leave it on for a while (>= 25us) */ 1178c2ecf20Sopenharmony_ci writeb(CMD_RESET, ®s->command); 1188c2ecf20Sopenharmony_ci udelay(20); 1198c2ecf20Sopenharmony_ci mac53c94_init(state); 1208c2ecf20Sopenharmony_ci writeb(CMD_NOP, ®s->command); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cmd->device->host->host_lock, flags); 1238c2ecf20Sopenharmony_ci return SUCCESS; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void mac53c94_init(struct fsc_state *state) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct mac53c94_regs __iomem *regs = state->regs; 1298c2ecf20Sopenharmony_ci struct dbdma_regs __iomem *dma = state->dma; 1308c2ecf20Sopenharmony_ci int x; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci writeb(state->host->this_id | CF1_PAR_ENABLE, ®s->config1); 1338c2ecf20Sopenharmony_ci writeb(TIMO_VAL(250), ®s->sel_timeout); /* 250ms */ 1348c2ecf20Sopenharmony_ci writeb(CLKF_VAL(state->clk_freq), ®s->clk_factor); 1358c2ecf20Sopenharmony_ci writeb(CF2_FEATURE_EN, ®s->config2); 1368c2ecf20Sopenharmony_ci writeb(0, ®s->config3); 1378c2ecf20Sopenharmony_ci writeb(0, ®s->sync_period); 1388c2ecf20Sopenharmony_ci writeb(0, ®s->sync_offset); 1398c2ecf20Sopenharmony_ci x = readb(®s->interrupt); 1408c2ecf20Sopenharmony_ci writel((RUN|PAUSE|FLUSH|WAKE) << 16, &dma->control); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Start the next command for a 53C94. 1458c2ecf20Sopenharmony_ci * Should be called with interrupts disabled. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic void mac53c94_start(struct fsc_state *state) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd; 1508c2ecf20Sopenharmony_ci struct mac53c94_regs __iomem *regs = state->regs; 1518c2ecf20Sopenharmony_ci int i; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (state->phase != idle || state->current_req != NULL) 1548c2ecf20Sopenharmony_ci panic("inappropriate mac53c94_start (state=%p)", state); 1558c2ecf20Sopenharmony_ci if (state->request_q == NULL) 1568c2ecf20Sopenharmony_ci return; 1578c2ecf20Sopenharmony_ci state->current_req = cmd = state->request_q; 1588c2ecf20Sopenharmony_ci state->request_q = (struct scsi_cmnd *) cmd->host_scribble; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Off we go */ 1618c2ecf20Sopenharmony_ci writeb(0, ®s->count_lo); 1628c2ecf20Sopenharmony_ci writeb(0, ®s->count_mid); 1638c2ecf20Sopenharmony_ci writeb(0, ®s->count_hi); 1648c2ecf20Sopenharmony_ci writeb(CMD_NOP + CMD_DMA_MODE, ®s->command); 1658c2ecf20Sopenharmony_ci udelay(1); 1668c2ecf20Sopenharmony_ci writeb(CMD_FLUSH, ®s->command); 1678c2ecf20Sopenharmony_ci udelay(1); 1688c2ecf20Sopenharmony_ci writeb(cmd->device->id, ®s->dest_id); 1698c2ecf20Sopenharmony_ci writeb(0, ®s->sync_period); 1708c2ecf20Sopenharmony_ci writeb(0, ®s->sync_offset); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* load the command into the FIFO */ 1738c2ecf20Sopenharmony_ci for (i = 0; i < cmd->cmd_len; ++i) 1748c2ecf20Sopenharmony_ci writeb(cmd->cmnd[i], ®s->fifo); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* do select without ATN XXX */ 1778c2ecf20Sopenharmony_ci writeb(CMD_SELECT, ®s->command); 1788c2ecf20Sopenharmony_ci state->phase = selecting; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci set_dma_cmds(state, cmd); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic irqreturn_t do_mac53c94_interrupt(int irq, void *dev_id) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci unsigned long flags; 1868c2ecf20Sopenharmony_ci struct Scsi_Host *dev = ((struct fsc_state *) dev_id)->current_req->device->host; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci spin_lock_irqsave(dev->host_lock, flags); 1898c2ecf20Sopenharmony_ci mac53c94_interrupt(irq, dev_id); 1908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(dev->host_lock, flags); 1918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void mac53c94_interrupt(int irq, void *dev_id) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct fsc_state *state = (struct fsc_state *) dev_id; 1978c2ecf20Sopenharmony_ci struct mac53c94_regs __iomem *regs = state->regs; 1988c2ecf20Sopenharmony_ci struct dbdma_regs __iomem *dma = state->dma; 1998c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd = state->current_req; 2008c2ecf20Sopenharmony_ci int nb, stat, seq, intr; 2018c2ecf20Sopenharmony_ci static int mac53c94_errors; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Apparently, reading the interrupt register unlatches 2058c2ecf20Sopenharmony_ci * the status and sequence step registers. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci seq = readb(®s->seqstep); 2088c2ecf20Sopenharmony_ci stat = readb(®s->status); 2098c2ecf20Sopenharmony_ci intr = readb(®s->interrupt); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci#if 0 2128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "mac53c94_intr, intr=%x stat=%x seq=%x phase=%d\n", 2138c2ecf20Sopenharmony_ci intr, stat, seq, state->phase); 2148c2ecf20Sopenharmony_ci#endif 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (intr & INTR_RESET) { 2178c2ecf20Sopenharmony_ci /* SCSI bus was reset */ 2188c2ecf20Sopenharmony_ci printk(KERN_INFO "external SCSI bus reset detected\n"); 2198c2ecf20Sopenharmony_ci writeb(CMD_NOP, ®s->command); 2208c2ecf20Sopenharmony_ci writel(RUN << 16, &dma->control); /* stop dma */ 2218c2ecf20Sopenharmony_ci cmd_done(state, DID_RESET << 16); 2228c2ecf20Sopenharmony_ci return; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci if (intr & INTR_ILL_CMD) { 2258c2ecf20Sopenharmony_ci printk(KERN_ERR "53c94: invalid cmd, intr=%x stat=%x seq=%x phase=%d\n", 2268c2ecf20Sopenharmony_ci intr, stat, seq, state->phase); 2278c2ecf20Sopenharmony_ci cmd_done(state, DID_ERROR << 16); 2288c2ecf20Sopenharmony_ci return; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci if (stat & STAT_ERROR) { 2318c2ecf20Sopenharmony_ci#if 0 2328c2ecf20Sopenharmony_ci /* XXX these seem to be harmless? */ 2338c2ecf20Sopenharmony_ci printk("53c94: bad error, intr=%x stat=%x seq=%x phase=%d\n", 2348c2ecf20Sopenharmony_ci intr, stat, seq, state->phase); 2358c2ecf20Sopenharmony_ci#endif 2368c2ecf20Sopenharmony_ci ++mac53c94_errors; 2378c2ecf20Sopenharmony_ci writeb(CMD_NOP + CMD_DMA_MODE, ®s->command); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci if (cmd == 0) { 2408c2ecf20Sopenharmony_ci printk(KERN_DEBUG "53c94: interrupt with no command active?\n"); 2418c2ecf20Sopenharmony_ci return; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci if (stat & STAT_PARITY) { 2448c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53c94: parity error\n"); 2458c2ecf20Sopenharmony_ci cmd_done(state, DID_PARITY << 16); 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci switch (state->phase) { 2498c2ecf20Sopenharmony_ci case selecting: 2508c2ecf20Sopenharmony_ci if (intr & INTR_DISCONNECT) { 2518c2ecf20Sopenharmony_ci /* selection timed out */ 2528c2ecf20Sopenharmony_ci cmd_done(state, DID_BAD_TARGET << 16); 2538c2ecf20Sopenharmony_ci return; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci if (intr != INTR_BUS_SERV + INTR_DONE) { 2568c2ecf20Sopenharmony_ci printk(KERN_DEBUG "got intr %x during selection\n", intr); 2578c2ecf20Sopenharmony_ci cmd_done(state, DID_ERROR << 16); 2588c2ecf20Sopenharmony_ci return; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci if ((seq & SS_MASK) != SS_DONE) { 2618c2ecf20Sopenharmony_ci printk(KERN_DEBUG "seq step %x after command\n", seq); 2628c2ecf20Sopenharmony_ci cmd_done(state, DID_ERROR << 16); 2638c2ecf20Sopenharmony_ci return; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci writeb(CMD_NOP, ®s->command); 2668c2ecf20Sopenharmony_ci /* set DMA controller going if any data to transfer */ 2678c2ecf20Sopenharmony_ci if ((stat & (STAT_MSG|STAT_CD)) == 0 2688c2ecf20Sopenharmony_ci && (scsi_sg_count(cmd) > 0 || scsi_bufflen(cmd))) { 2698c2ecf20Sopenharmony_ci nb = cmd->SCp.this_residual; 2708c2ecf20Sopenharmony_ci if (nb > 0xfff0) 2718c2ecf20Sopenharmony_ci nb = 0xfff0; 2728c2ecf20Sopenharmony_ci cmd->SCp.this_residual -= nb; 2738c2ecf20Sopenharmony_ci writeb(nb, ®s->count_lo); 2748c2ecf20Sopenharmony_ci writeb(nb >> 8, ®s->count_mid); 2758c2ecf20Sopenharmony_ci writeb(CMD_DMA_MODE + CMD_NOP, ®s->command); 2768c2ecf20Sopenharmony_ci writel(virt_to_phys(state->dma_cmds), &dma->cmdptr); 2778c2ecf20Sopenharmony_ci writel((RUN << 16) | RUN, &dma->control); 2788c2ecf20Sopenharmony_ci writeb(CMD_DMA_MODE + CMD_XFER_DATA, ®s->command); 2798c2ecf20Sopenharmony_ci state->phase = dataing; 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci } else if ((stat & STAT_PHASE) == STAT_CD + STAT_IO) { 2828c2ecf20Sopenharmony_ci /* up to status phase already */ 2838c2ecf20Sopenharmony_ci writeb(CMD_I_COMPLETE, ®s->command); 2848c2ecf20Sopenharmony_ci state->phase = completing; 2858c2ecf20Sopenharmony_ci } else { 2868c2ecf20Sopenharmony_ci printk(KERN_DEBUG "in unexpected phase %x after cmd\n", 2878c2ecf20Sopenharmony_ci stat & STAT_PHASE); 2888c2ecf20Sopenharmony_ci cmd_done(state, DID_ERROR << 16); 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci case dataing: 2948c2ecf20Sopenharmony_ci if (intr != INTR_BUS_SERV) { 2958c2ecf20Sopenharmony_ci printk(KERN_DEBUG "got intr %x before status\n", intr); 2968c2ecf20Sopenharmony_ci cmd_done(state, DID_ERROR << 16); 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci if (cmd->SCp.this_residual != 0 3008c2ecf20Sopenharmony_ci && (stat & (STAT_MSG|STAT_CD)) == 0) { 3018c2ecf20Sopenharmony_ci /* Set up the count regs to transfer more */ 3028c2ecf20Sopenharmony_ci nb = cmd->SCp.this_residual; 3038c2ecf20Sopenharmony_ci if (nb > 0xfff0) 3048c2ecf20Sopenharmony_ci nb = 0xfff0; 3058c2ecf20Sopenharmony_ci cmd->SCp.this_residual -= nb; 3068c2ecf20Sopenharmony_ci writeb(nb, ®s->count_lo); 3078c2ecf20Sopenharmony_ci writeb(nb >> 8, ®s->count_mid); 3088c2ecf20Sopenharmony_ci writeb(CMD_DMA_MODE + CMD_NOP, ®s->command); 3098c2ecf20Sopenharmony_ci writeb(CMD_DMA_MODE + CMD_XFER_DATA, ®s->command); 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci if ((stat & STAT_PHASE) != STAT_CD + STAT_IO) { 3138c2ecf20Sopenharmony_ci printk(KERN_DEBUG "intr %x before data xfer complete\n", intr); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci writel(RUN << 16, &dma->control); /* stop dma */ 3168c2ecf20Sopenharmony_ci scsi_dma_unmap(cmd); 3178c2ecf20Sopenharmony_ci /* should check dma status */ 3188c2ecf20Sopenharmony_ci writeb(CMD_I_COMPLETE, ®s->command); 3198c2ecf20Sopenharmony_ci state->phase = completing; 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci case completing: 3228c2ecf20Sopenharmony_ci if (intr != INTR_DONE) { 3238c2ecf20Sopenharmony_ci printk(KERN_DEBUG "got intr %x on completion\n", intr); 3248c2ecf20Sopenharmony_ci cmd_done(state, DID_ERROR << 16); 3258c2ecf20Sopenharmony_ci return; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci cmd->SCp.Status = readb(®s->fifo); 3288c2ecf20Sopenharmony_ci cmd->SCp.Message = readb(®s->fifo); 3298c2ecf20Sopenharmony_ci cmd->result = CMD_ACCEPT_MSG; 3308c2ecf20Sopenharmony_ci writeb(CMD_ACCEPT_MSG, ®s->command); 3318c2ecf20Sopenharmony_ci state->phase = busfreeing; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case busfreeing: 3348c2ecf20Sopenharmony_ci if (intr != INTR_DISCONNECT) { 3358c2ecf20Sopenharmony_ci printk(KERN_DEBUG "got intr %x when expected disconnect\n", intr); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci cmd_done(state, (DID_OK << 16) + (cmd->SCp.Message << 8) 3388c2ecf20Sopenharmony_ci + cmd->SCp.Status); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci default: 3418c2ecf20Sopenharmony_ci printk(KERN_DEBUG "don't know about phase %d\n", state->phase); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void cmd_done(struct fsc_state *state, int result) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci cmd = state->current_req; 3508c2ecf20Sopenharmony_ci if (cmd != 0) { 3518c2ecf20Sopenharmony_ci cmd->result = result; 3528c2ecf20Sopenharmony_ci (*cmd->scsi_done)(cmd); 3538c2ecf20Sopenharmony_ci state->current_req = NULL; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci state->phase = idle; 3568c2ecf20Sopenharmony_ci mac53c94_start(state); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* 3608c2ecf20Sopenharmony_ci * Set up DMA commands for transferring data. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_cistatic void set_dma_cmds(struct fsc_state *state, struct scsi_cmnd *cmd) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci int i, dma_cmd, total, nseg; 3658c2ecf20Sopenharmony_ci struct scatterlist *scl; 3668c2ecf20Sopenharmony_ci struct dbdma_cmd *dcmds; 3678c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 3688c2ecf20Sopenharmony_ci u32 dma_len; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci nseg = scsi_dma_map(cmd); 3718c2ecf20Sopenharmony_ci BUG_ON(nseg < 0); 3728c2ecf20Sopenharmony_ci if (!nseg) 3738c2ecf20Sopenharmony_ci return; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci dma_cmd = cmd->sc_data_direction == DMA_TO_DEVICE ? 3768c2ecf20Sopenharmony_ci OUTPUT_MORE : INPUT_MORE; 3778c2ecf20Sopenharmony_ci dcmds = state->dma_cmds; 3788c2ecf20Sopenharmony_ci total = 0; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci scsi_for_each_sg(cmd, scl, nseg, i) { 3818c2ecf20Sopenharmony_ci dma_addr = sg_dma_address(scl); 3828c2ecf20Sopenharmony_ci dma_len = sg_dma_len(scl); 3838c2ecf20Sopenharmony_ci if (dma_len > 0xffff) 3848c2ecf20Sopenharmony_ci panic("mac53c94: scatterlist element >= 64k"); 3858c2ecf20Sopenharmony_ci total += dma_len; 3868c2ecf20Sopenharmony_ci dcmds->req_count = cpu_to_le16(dma_len); 3878c2ecf20Sopenharmony_ci dcmds->command = cpu_to_le16(dma_cmd); 3888c2ecf20Sopenharmony_ci dcmds->phy_addr = cpu_to_le32(dma_addr); 3898c2ecf20Sopenharmony_ci dcmds->xfer_status = 0; 3908c2ecf20Sopenharmony_ci ++dcmds; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dma_cmd += OUTPUT_LAST - OUTPUT_MORE; 3948c2ecf20Sopenharmony_ci dcmds[-1].command = cpu_to_le16(dma_cmd); 3958c2ecf20Sopenharmony_ci dcmds->command = cpu_to_le16(DBDMA_STOP); 3968c2ecf20Sopenharmony_ci cmd->SCp.this_residual = total; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic struct scsi_host_template mac53c94_template = { 4008c2ecf20Sopenharmony_ci .proc_name = "53c94", 4018c2ecf20Sopenharmony_ci .name = "53C94", 4028c2ecf20Sopenharmony_ci .queuecommand = mac53c94_queue, 4038c2ecf20Sopenharmony_ci .eh_host_reset_handler = mac53c94_host_reset, 4048c2ecf20Sopenharmony_ci .can_queue = 1, 4058c2ecf20Sopenharmony_ci .this_id = 7, 4068c2ecf20Sopenharmony_ci .sg_tablesize = SG_ALL, 4078c2ecf20Sopenharmony_ci .max_segment_size = 65535, 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *match) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct device_node *node = macio_get_of_node(mdev); 4138c2ecf20Sopenharmony_ci struct pci_dev *pdev = macio_get_pci_dev(mdev); 4148c2ecf20Sopenharmony_ci struct fsc_state *state; 4158c2ecf20Sopenharmony_ci struct Scsi_Host *host; 4168c2ecf20Sopenharmony_ci void *dma_cmd_space; 4178c2ecf20Sopenharmony_ci const unsigned char *clkprop; 4188c2ecf20Sopenharmony_ci int proplen, rc = -ENODEV; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) { 4218c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53c94: expected 2 addrs and intrs" 4228c2ecf20Sopenharmony_ci " (got %d/%d)\n", 4238c2ecf20Sopenharmony_ci macio_resource_count(mdev), macio_irq_count(mdev)); 4248c2ecf20Sopenharmony_ci return -ENODEV; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (macio_request_resources(mdev, "mac53c94") != 0) { 4288c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53c94: unable to request memory resources"); 4298c2ecf20Sopenharmony_ci return -EBUSY; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci host = scsi_host_alloc(&mac53c94_template, sizeof(struct fsc_state)); 4338c2ecf20Sopenharmony_ci if (host == NULL) { 4348c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53c94: couldn't register host"); 4358c2ecf20Sopenharmony_ci rc = -ENOMEM; 4368c2ecf20Sopenharmony_ci goto out_release; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci state = (struct fsc_state *) host->hostdata; 4408c2ecf20Sopenharmony_ci macio_set_drvdata(mdev, state); 4418c2ecf20Sopenharmony_ci state->host = host; 4428c2ecf20Sopenharmony_ci state->pdev = pdev; 4438c2ecf20Sopenharmony_ci state->mdev = mdev; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci state->regs = (struct mac53c94_regs __iomem *) 4468c2ecf20Sopenharmony_ci ioremap(macio_resource_start(mdev, 0), 0x1000); 4478c2ecf20Sopenharmony_ci state->intr = macio_irq(mdev, 0); 4488c2ecf20Sopenharmony_ci state->dma = (struct dbdma_regs __iomem *) 4498c2ecf20Sopenharmony_ci ioremap(macio_resource_start(mdev, 1), 0x1000); 4508c2ecf20Sopenharmony_ci state->dmaintr = macio_irq(mdev, 1); 4518c2ecf20Sopenharmony_ci if (state->regs == NULL || state->dma == NULL) { 4528c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53c94: ioremap failed for %pOF\n", node); 4538c2ecf20Sopenharmony_ci goto out_free; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci clkprop = of_get_property(node, "clock-frequency", &proplen); 4578c2ecf20Sopenharmony_ci if (clkprop == NULL || proplen != sizeof(int)) { 4588c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: can't get clock frequency, " 4598c2ecf20Sopenharmony_ci "assuming 25MHz\n", node); 4608c2ecf20Sopenharmony_ci state->clk_freq = 25000000; 4618c2ecf20Sopenharmony_ci } else 4628c2ecf20Sopenharmony_ci state->clk_freq = *(int *)clkprop; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* Space for dma command list: +1 for stop command, 4658c2ecf20Sopenharmony_ci * +1 to allow for aligning. 4668c2ecf20Sopenharmony_ci * XXX FIXME: Use DMA consistent routines 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci dma_cmd_space = kmalloc_array(host->sg_tablesize + 2, 4698c2ecf20Sopenharmony_ci sizeof(struct dbdma_cmd), 4708c2ecf20Sopenharmony_ci GFP_KERNEL); 4718c2ecf20Sopenharmony_ci if (dma_cmd_space == 0) { 4728c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53c94: couldn't allocate dma " 4738c2ecf20Sopenharmony_ci "command space for %pOF\n", node); 4748c2ecf20Sopenharmony_ci rc = -ENOMEM; 4758c2ecf20Sopenharmony_ci goto out_free; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci state->dma_cmds = (struct dbdma_cmd *)DBDMA_ALIGN(dma_cmd_space); 4788c2ecf20Sopenharmony_ci memset(state->dma_cmds, 0, (host->sg_tablesize + 1) 4798c2ecf20Sopenharmony_ci * sizeof(struct dbdma_cmd)); 4808c2ecf20Sopenharmony_ci state->dma_cmd_space = dma_cmd_space; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci mac53c94_init(state); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (request_irq(state->intr, do_mac53c94_interrupt, 0, "53C94",state)) { 4858c2ecf20Sopenharmony_ci printk(KERN_ERR "mac53C94: can't get irq %d for %pOF\n", 4868c2ecf20Sopenharmony_ci state->intr, node); 4878c2ecf20Sopenharmony_ci goto out_free_dma; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci rc = scsi_add_host(host, &mdev->ofdev.dev); 4918c2ecf20Sopenharmony_ci if (rc != 0) 4928c2ecf20Sopenharmony_ci goto out_release_irq; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci scsi_scan_host(host); 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci out_release_irq: 4988c2ecf20Sopenharmony_ci free_irq(state->intr, state); 4998c2ecf20Sopenharmony_ci out_free_dma: 5008c2ecf20Sopenharmony_ci kfree(state->dma_cmd_space); 5018c2ecf20Sopenharmony_ci out_free: 5028c2ecf20Sopenharmony_ci if (state->dma != NULL) 5038c2ecf20Sopenharmony_ci iounmap(state->dma); 5048c2ecf20Sopenharmony_ci if (state->regs != NULL) 5058c2ecf20Sopenharmony_ci iounmap(state->regs); 5068c2ecf20Sopenharmony_ci scsi_host_put(host); 5078c2ecf20Sopenharmony_ci out_release: 5088c2ecf20Sopenharmony_ci macio_release_resources(mdev); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return rc; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int mac53c94_remove(struct macio_dev *mdev) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct fsc_state *fp = (struct fsc_state *)macio_get_drvdata(mdev); 5168c2ecf20Sopenharmony_ci struct Scsi_Host *host = fp->host; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci scsi_remove_host(host); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci free_irq(fp->intr, fp); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (fp->regs) 5238c2ecf20Sopenharmony_ci iounmap(fp->regs); 5248c2ecf20Sopenharmony_ci if (fp->dma) 5258c2ecf20Sopenharmony_ci iounmap(fp->dma); 5268c2ecf20Sopenharmony_ci kfree(fp->dma_cmd_space); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci scsi_host_put(host); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci macio_release_resources(mdev); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic struct of_device_id mac53c94_match[] = 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci { 5398c2ecf20Sopenharmony_ci .name = "53c94", 5408c2ecf20Sopenharmony_ci }, 5418c2ecf20Sopenharmony_ci {}, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (of, mac53c94_match); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic struct macio_driver mac53c94_driver = 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci .driver = { 5488c2ecf20Sopenharmony_ci .name = "mac53c94", 5498c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5508c2ecf20Sopenharmony_ci .of_match_table = mac53c94_match, 5518c2ecf20Sopenharmony_ci }, 5528c2ecf20Sopenharmony_ci .probe = mac53c94_probe, 5538c2ecf20Sopenharmony_ci .remove = mac53c94_remove, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int __init init_mac53c94(void) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci return macio_register_driver(&mac53c94_driver); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic void __exit exit_mac53c94(void) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci return macio_unregister_driver(&mac53c94_driver); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cimodule_init(init_mac53c94); 5688c2ecf20Sopenharmony_cimodule_exit(exit_mac53c94); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PowerMac 53c94 SCSI driver"); 5718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 5728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 573