cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/atp870u.c

Directory: drivers/scsi
/* 
 *  Copyright (C) 1997  Wu Ching Chen
 *  2.1.x update (C) 1998  Krzysztof G. Baranowski
 *  2.5.x update (C) 2002  Red Hat
 *  2.6.x update (C) 2004  Red Hat
 *
 * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
 *
 * Wu Ching Chen : NULL pointer fixes  2000/06/02
 *                 support atp876 chip
 *                 enable 32 bit fifo transfer
 *                 support cdrom & remove device run ultra speed
 *                 fix disconnect bug  2000/12/21
 *                 support atp880 chip lvd u160 2001/05/15
 *                 fix prd table bug 2001/09/12 (7.1)
 *
 * atp885 support add by ACARD Hao Ping Lian 2005/01/05
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <asm/io.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>

#include "atp870u.h"


static struct scsi_host_template atp870u_template;
static void send_s870(struct atp_unit *dev,unsigned char c);
static void atp_is(struct atp_unit *dev, unsigned char c, bool wide_chip, unsigned char lvdmode);


static inline void atp_writeb_base(struct atp_unit *atp, u8 reg, u8 val) { outb(val, atp->baseport + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary29100.00%1100.00%
Total29100.00%1100.00%


static inline void atp_writew_base(struct atp_unit *atp, u8 reg, u16 val) { outw(val, atp->baseport + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary29100.00%1100.00%
Total29100.00%1100.00%


static inline void atp_writeb_io(struct atp_unit *atp, u8 channel, u8 reg, u8 val) { outb(val, atp->ioport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary35100.00%1100.00%
Total35100.00%1100.00%


static inline void atp_writew_io(struct atp_unit *atp, u8 channel, u8 reg, u16 val) { outw(val, atp->ioport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary35100.00%1100.00%
Total35100.00%1100.00%


static inline void atp_writeb_pci(struct atp_unit *atp, u8 channel, u8 reg, u8 val) { outb(val, atp->pciport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary35100.00%1100.00%
Total35100.00%1100.00%


static inline void atp_writel_pci(struct atp_unit *atp, u8 channel, u8 reg, u32 val) { outl(val, atp->pciport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary35100.00%1100.00%
Total35100.00%1100.00%


static inline u8 atp_readb_base(struct atp_unit *atp, u8 reg) { return inb(atp->baseport + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary25100.00%1100.00%
Total25100.00%1100.00%


static inline u16 atp_readw_base(struct atp_unit *atp, u8 reg) { return inw(atp->baseport + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary25100.00%1100.00%
Total25100.00%1100.00%


static inline u32 atp_readl_base(struct atp_unit *atp, u8 reg) { return inl(atp->baseport + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary25100.00%1100.00%
Total25100.00%1100.00%


static inline u8 atp_readb_io(struct atp_unit *atp, u8 channel, u8 reg) { return inb(atp->ioport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary31100.00%1100.00%
Total31100.00%1100.00%


static inline u16 atp_readw_io(struct atp_unit *atp, u8 channel, u8 reg) { return inw(atp->ioport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary31100.00%1100.00%
Total31100.00%1100.00%


static inline u8 atp_readb_pci(struct atp_unit *atp, u8 channel, u8 reg) { return inb(atp->pciport[channel] + reg); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary31100.00%1100.00%
Total31100.00%1100.00%


static inline bool is880(struct atp_unit *atp) { return atp->pdev->device == ATP880_DEVID1 || atp->pdev->device == ATP880_DEVID2; }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary29100.00%1100.00%
Total29100.00%1100.00%


static inline bool is885(struct atp_unit *atp) { return atp->pdev->device == ATP885_DEVID; }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary21100.00%1100.00%
Total21100.00%1100.00%


static irqreturn_t atp870u_intr_handle(int irq, void *dev_id) { unsigned long flags; unsigned short int id; unsigned char i, j, c, target_id, lun,cmdp; unsigned char *prd; struct scsi_cmnd *workreq; unsigned long adrcnt, k; #ifdef ED_DBGP unsigned long l; #endif struct Scsi_Host *host = dev_id; struct atp_unit *dev = (struct atp_unit *)&host->hostdata; for (c = 0; c < 2; c++) { j = atp_readb_io(dev, c, 0x1f); if ((j & 0x80) != 0) break; dev->in_int[c] = 0; } if ((j & 0x80) == 0) return IRQ_NONE; #ifdef ED_DBGP printk("atp870u_intr_handle enter\n"); #endif dev->in_int[c] = 1; cmdp = atp_readb_io(dev, c, 0x10); if (dev->working[c] != 0) { if (is885(dev)) { if ((atp_readb_io(dev, c, 0x16) & 0x80) == 0) atp_writeb_io(dev, c, 0x16, (atp_readb_io(dev, c, 0x16) | 0x80)); } if ((atp_readb_pci(dev, c, 0x00) & 0x08) != 0) { for (k=0; k < 1000; k++) { if ((atp_readb_pci(dev, c, 2) & 0x08) == 0) break; if ((atp_readb_pci(dev, c, 2) & 0x01) == 0) break; } } atp_writeb_pci(dev, c, 0, 0x00); i = atp_readb_io(dev, c, 0x17); if (is885(dev)) atp_writeb_pci(dev, c, 2, 0x06); target_id = atp_readb_io(dev, c, 0x15); /* * Remap wide devices onto id numbers */ if ((target_id & 0x40) != 0) { target_id = (target_id & 0x07) | 0x08; } else { target_id &= 0x07; } if ((j & 0x40) != 0) { if (dev->last_cmd[c] == 0xff) { dev->last_cmd[c] = target_id; } dev->last_cmd[c] |= 0x40; } if (is885(dev)) dev->r1f[c][target_id] |= j; #ifdef ED_DBGP printk("atp870u_intr_handle status = %x\n",i); #endif if (i == 0x85) { if ((dev->last_cmd[c] & 0xf0) != 0x40) { dev->last_cmd[c] = 0xff; } if (is885(dev)) { adrcnt = 0; ((unsigned char *) &adrcnt)[2] = atp_readb_io(dev, c, 0x12); ((unsigned char *) &adrcnt)[1] = atp_readb_io(dev, c, 0x13); ((unsigned char *) &adrcnt)[0] = atp_readb_io(dev, c, 0x14); if (dev->id[c][target_id].last_len != adrcnt) { k = dev->id[c][target_id].last_len; k -= adrcnt; dev->id[c][target_id].tran_len = k; dev->id[c][target_id].last_len = adrcnt; } #ifdef ED_DBGP printk("dev->id[c][target_id].last_len = %d dev->id[c][target_id].tran_len = %d\n",dev->id[c][target_id].last_len,dev->id[c][target_id].tran_len); #endif } /* * Flip wide */ if (dev->wide_id[c] != 0) { atp_writeb_io(dev, c, 0x1b, 0x01); while ((atp_readb_io(dev, c, 0x1b) & 0x01) != 0x01) atp_writeb_io(dev, c, 0x1b, 0x01); } /* * Issue more commands */ spin_lock_irqsave(dev->host->host_lock, flags); if (((dev->quhd[c] != dev->quend[c]) || (dev->last_cmd[c] != 0xff)) && (dev->in_snd[c] == 0)) { #ifdef ED_DBGP printk("Call sent_s870\n"); #endif send_s870(dev,c); } spin_unlock_irqrestore(dev->host->host_lock, flags); /* * Done */ dev->in_int[c] = 0; #ifdef ED_DBGP printk("Status 0x85 return\n"); #endif return IRQ_HANDLED; } if (i == 0x40) { dev->last_cmd[c] |= 0x40; dev->in_int[c] = 0; return IRQ_HANDLED; } if (i == 0x21) { if ((dev->last_cmd[c] & 0xf0) != 0x40) { dev->last_cmd[c] = 0xff; } adrcnt = 0; ((unsigned char *) &adrcnt)[2] = atp_readb_io(dev, c, 0x12); ((unsigned char *) &adrcnt)[1] = atp_readb_io(dev, c, 0x13); ((unsigned char *) &adrcnt)[0] = atp_readb_io(dev, c, 0x14); k = dev->id[c][target_id].last_len; k -= adrcnt; dev->id[c][target_id].tran_len = k; dev->id[c][target_id].last_len = adrcnt; atp_writeb_io(dev, c, 0x10, 0x41); atp_writeb_io(dev, c, 0x18, 0x08); dev->in_int[c] = 0; return IRQ_HANDLED; } if (is885(dev)) { if ((i == 0x4c) || (i == 0x4d) || (i == 0x8c) || (i == 0x8d)) { if ((i == 0x4c) || (i == 0x8c)) i=0x48; else i=0x49; } } if ((i == 0x80) || (i == 0x8f)) { #ifdef ED_DBGP printk(KERN_DEBUG "Device reselect\n"); #endif lun = 0; if (cmdp == 0x44 || i == 0x80) lun = atp_readb_io(dev, c, 0x1d) & 0x07; else { if ((dev->last_cmd[c] & 0xf0) != 0x40) { dev->last_cmd[c] = 0xff; } if (cmdp == 0x41) { #ifdef ED_DBGP printk("cmdp = 0x41\n"); #endif adrcnt = 0; ((unsigned char *) &adrcnt)[2] = atp_readb_io(dev, c, 0x12); ((unsigned char *) &adrcnt)[1] = atp_readb_io(dev, c, 0x13); ((unsigned char *) &adrcnt)[0] = atp_readb_io(dev, c, 0x14); k = dev->id[c][target_id].last_len; k -= adrcnt; dev->id[c][target_id].tran_len = k; dev->id[c][target_id].last_len = adrcnt; atp_writeb_io(dev, c, 0x18, 0x08); dev->in_int[c] = 0; return IRQ_HANDLED; } else { #ifdef ED_DBGP printk("cmdp != 0x41\n"); #endif atp_writeb_io(dev, c, 0x10, 0x46); dev->id[c][target_id].dirct = 0x00; atp_writeb_io(dev, c, 0x12, 0x00); atp_writeb_io(dev, c, 0x13, 0x00); atp_writeb_io(dev, c, 0x14, 0x00); atp_writeb_io(dev, c, 0x18, 0x08); dev->in_int[c] = 0; return IRQ_HANDLED; } } if (dev->last_cmd[c] != 0xff) { dev->last_cmd[c] |= 0x40; } if (is885(dev)) { j = atp_readb_base(dev, 0x29) & 0xfe; atp_writeb_base(dev, 0x29, j); } else atp_writeb_io(dev, c, 0x10, 0x45); target_id = atp_readb_io(dev, c, 0x16); /* * Remap wide identifiers */ if ((target_id & 0x10) != 0) { target_id = (target_id & 0x07) | 0x08; } else { target_id &= 0x07; } if (is885(dev)) atp_writeb_io(dev, c, 0x10, 0x45); workreq = dev->id[c][target_id].curr_req; #ifdef ED_DBGP scmd_printk(KERN_DEBUG, workreq, "CDB"); for (l = 0; l < workreq->cmd_len; l++) printk(KERN_DEBUG " %x",workreq->cmnd[l]); printk("\n"); #endif atp_writeb_io(dev, c, 0x0f, lun); atp_writeb_io(dev, c, 0x11, dev->id[c][target_id].devsp); adrcnt = dev->id[c][target_id].tran_len; k = dev->id[c][target_id].last_len; atp_writeb_io(dev, c, 0x12, ((unsigned char *) &k)[2]); atp_writeb_io(dev, c, 0x13, ((unsigned char *) &k)[1]); atp_writeb_io(dev, c, 0x14, ((unsigned char *) &k)[0]); #ifdef ED_DBGP printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k, atp_readb_io(dev, c, 0x14), atp_readb_io(dev, c, 0x13), atp_readb_io(dev, c, 0x12)); #endif /* Remap wide */ j = target_id; if (target_id > 7) { j = (j & 0x07) | 0x40; } /* Add direction */ j |= dev->id[c][target_id].dirct; atp_writeb_io(dev, c, 0x15, j); atp_writeb_io(dev, c, 0x16, 0x80); /* enable 32 bit fifo transfer */ if (is885(dev)) { i = atp_readb_pci(dev, c, 1) & 0xf3; //j=workreq->cmnd[0]; if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) { i |= 0x0c; } atp_writeb_pci(dev, c, 1, i); } else if (is880(dev)) { if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) atp_writeb_base(dev, 0x3b, (atp_readb_base(dev, 0x3b) & 0x3f) | 0xc0); else atp_writeb_base(dev, 0x3b, atp_readb_base(dev, 0x3b) & 0x3f); } else { if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) atp_writeb_base(dev, 0x3a, (atp_readb_base(dev, 0x3a) & 0xf3) | 0x08); else atp_writeb_base(dev, 0x3a, atp_readb_base(dev, 0x3a) & 0xf3); } j = 0; id = 1; id = id << target_id; /* * Is this a wide device */ if ((id & dev->wide_id[c]) != 0) { j |= 0x01; } atp_writeb_io(dev, c, 0x1b, j); while ((atp_readb_io(dev, c, 0x1b) & 0x01) != j) atp_writeb_io(dev, c, 0x1b, j); if (dev->id[c][target_id].last_len == 0) { atp_writeb_io(dev, c, 0x18, 0x08); dev->in_int[c] = 0; #ifdef ED_DBGP printk("dev->id[c][target_id].last_len = 0\n"); #endif return IRQ_HANDLED; } #ifdef ED_DBGP printk("target_id = %d adrcnt = %d\n",target_id,adrcnt); #endif prd = dev->id[c][target_id].prd_pos; while (adrcnt != 0) { id = ((unsigned short int *)prd)[2]; if (id == 0) { k = 0x10000; } else { k = id; } if (k > adrcnt) { ((unsigned short int *)prd)[2] = (unsigned short int) (k - adrcnt); ((unsigned long *)prd)[0] += adrcnt; adrcnt = 0; dev->id[c][target_id].prd_pos = prd; } else { adrcnt -= k; dev->id[c][target_id].prdaddr += 0x08; prd += 0x08; if (adrcnt == 0) { dev->id[c][target_id].prd_pos = prd; } } } atp_writel_pci(dev, c, 0x04, dev->id[c][target_id].prdaddr); #ifdef ED_DBGP printk("dev->id[%d][%d].prdaddr 0x%8x\n", c, target_id, dev->id[c][target_id].prdaddr); #endif if (!is885(dev)) { atp_writeb_pci(dev, c, 2, 0x06); atp_writeb_pci(dev, c, 2, 0x00); } /* * Check transfer direction */ if (dev->id[c][target_id].dirct != 0) { atp_writeb_io(dev, c, 0x18, 0x08); atp_writeb_pci(dev, c, 0, 0x01); dev->in_int[c] = 0; #ifdef ED_DBGP printk("status 0x80 return dirct != 0\n"); #endif return IRQ_HANDLED; } atp_writeb_io(dev, c, 0x18, 0x08); atp_writeb_pci(dev, c, 0, 0x09); dev->in_int[c] = 0; #ifdef ED_DBGP printk("status 0x80 return dirct = 0\n"); #endif return IRQ_HANDLED; } /* * Current scsi request on this target */ workreq