| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 1 | /* | 
|  | 2 | * sata_inic162x.c - Driver for Initio 162x SATA controllers | 
|  | 3 | * | 
|  | 4 | * Copyright 2006  SUSE Linux Products GmbH | 
|  | 5 | * Copyright 2006  Tejun Heo <teheo@novell.com> | 
|  | 6 | * | 
|  | 7 | * This file is released under GPL v2. | 
|  | 8 | * | 
|  | 9 | * This controller is eccentric and easily locks up if something isn't | 
|  | 10 | * right.  Documentation is available at initio's website but it only | 
|  | 11 | * documents registers (not programming model). | 
|  | 12 | * | 
| Tejun Heo | 22bfc6d | 2008-04-30 16:35:17 +0900 | [diff] [blame] | 13 | * This driver has interesting history.  The first version was written | 
|  | 14 | * from the documentation and a 2.4 IDE driver posted on a Taiwan | 
|  | 15 | * company, which didn't use any IDMA features and couldn't handle | 
|  | 16 | * LBA48.  The resulting driver couldn't handle LBA48 devices either | 
|  | 17 | * making it pretty useless. | 
|  | 18 | * | 
|  | 19 | * After a while, initio picked the driver up, renamed it to | 
|  | 20 | * sata_initio162x, updated it to use IDMA for ATA DMA commands and | 
|  | 21 | * posted it on their website.  It only used ATA_PROT_DMA for IDMA and | 
|  | 22 | * attaching both devices and issuing IDMA and !IDMA commands | 
|  | 23 | * simultaneously broke it due to PIRQ masking interaction but it did | 
|  | 24 | * show how to use the IDMA (ADMA + some initio specific twists) | 
|  | 25 | * engine. | 
|  | 26 | * | 
|  | 27 | * Then, I picked up their changes again and here's the usable driver | 
|  | 28 | * which uses IDMA for everything.  Everything works now including | 
|  | 29 | * LBA48, CD/DVD burning, suspend/resume and hotplug.  There are some | 
|  | 30 | * issues tho.  Result Tf is not resported properly, NCQ isn't | 
|  | 31 | * supported yet and CD/DVD writing works with DMA assisted PIO | 
|  | 32 | * protocol (which, for native SATA devices, shouldn't cause any | 
|  | 33 | * noticeable difference). | 
|  | 34 | * | 
|  | 35 | * Anyways, so, here's finally a working driver for inic162x.  Enjoy! | 
|  | 36 | * | 
|  | 37 | * initio: If you guys wanna improve the driver regarding result TF | 
|  | 38 | * access and other stuff, please feel free to contact me.  I'll be | 
|  | 39 | * happy to assist. | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 40 | */ | 
|  | 41 |  | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 42 | #include <linux/gfp.h> | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 43 | #include <linux/kernel.h> | 
|  | 44 | #include <linux/module.h> | 
|  | 45 | #include <linux/pci.h> | 
|  | 46 | #include <scsi/scsi_host.h> | 
|  | 47 | #include <linux/libata.h> | 
|  | 48 | #include <linux/blkdev.h> | 
|  | 49 | #include <scsi/scsi_device.h> | 
|  | 50 |  | 
|  | 51 | #define DRV_NAME	"sata_inic162x" | 
| Tejun Heo | 22bfc6d | 2008-04-30 16:35:17 +0900 | [diff] [blame] | 52 | #define DRV_VERSION	"0.4" | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 53 |  | 
|  | 54 | enum { | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 55 | MMIO_BAR_PCI		= 5, | 
|  | 56 | MMIO_BAR_CARDBUS	= 1, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 57 |  | 
|  | 58 | NR_PORTS		= 2, | 
|  | 59 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 60 | IDMA_CPB_TBL_SIZE	= 4 * 32, | 
|  | 61 |  | 
|  | 62 | INIC_DMA_BOUNDARY	= 0xffffff, | 
|  | 63 |  | 
| Tejun Heo | b0dd9b8 | 2008-04-30 16:35:09 +0900 | [diff] [blame] | 64 | HOST_ACTRL		= 0x08, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 65 | HOST_CTL		= 0x7c, | 
|  | 66 | HOST_STAT		= 0x7e, | 
|  | 67 | HOST_IRQ_STAT		= 0xbc, | 
|  | 68 | HOST_IRQ_MASK		= 0xbe, | 
|  | 69 |  | 
|  | 70 | PORT_SIZE		= 0x40, | 
|  | 71 |  | 
|  | 72 | /* registers for ATA TF operation */ | 
| Tejun Heo | b0dd9b8 | 2008-04-30 16:35:09 +0900 | [diff] [blame] | 73 | PORT_TF_DATA		= 0x00, | 
|  | 74 | PORT_TF_FEATURE		= 0x01, | 
|  | 75 | PORT_TF_NSECT		= 0x02, | 
|  | 76 | PORT_TF_LBAL		= 0x03, | 
|  | 77 | PORT_TF_LBAM		= 0x04, | 
|  | 78 | PORT_TF_LBAH		= 0x05, | 
|  | 79 | PORT_TF_DEVICE		= 0x06, | 
|  | 80 | PORT_TF_COMMAND		= 0x07, | 
|  | 81 | PORT_TF_ALT_STAT	= 0x08, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 82 | PORT_IRQ_STAT		= 0x09, | 
|  | 83 | PORT_IRQ_MASK		= 0x0a, | 
|  | 84 | PORT_PRD_CTL		= 0x0b, | 
|  | 85 | PORT_PRD_ADDR		= 0x0c, | 
|  | 86 | PORT_PRD_XFERLEN	= 0x10, | 
| Tejun Heo | b0dd9b8 | 2008-04-30 16:35:09 +0900 | [diff] [blame] | 87 | PORT_CPB_CPBLAR		= 0x18, | 
|  | 88 | PORT_CPB_PTQFIFO	= 0x1c, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 89 |  | 
|  | 90 | /* IDMA register */ | 
|  | 91 | PORT_IDMA_CTL		= 0x14, | 
| Tejun Heo | b0dd9b8 | 2008-04-30 16:35:09 +0900 | [diff] [blame] | 92 | PORT_IDMA_STAT		= 0x16, | 
|  | 93 |  | 
|  | 94 | PORT_RPQ_FIFO		= 0x1e, | 
|  | 95 | PORT_RPQ_CNT		= 0x1f, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 96 |  | 
|  | 97 | PORT_SCR		= 0x20, | 
|  | 98 |  | 
|  | 99 | /* HOST_CTL bits */ | 
| Bob Stewart | 9958066 | 2008-09-11 11:50:03 +0200 | [diff] [blame] | 100 | HCTL_LEDEN		= (1 << 3),  /* enable LED operation */ | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 101 | HCTL_IRQOFF		= (1 << 8),  /* global IRQ off */ | 
| Tejun Heo | b0dd9b8 | 2008-04-30 16:35:09 +0900 | [diff] [blame] | 102 | HCTL_FTHD0		= (1 << 10), /* fifo threshold 0 */ | 
|  | 103 | HCTL_FTHD1		= (1 << 11), /* fifo threshold 1*/ | 
|  | 104 | HCTL_PWRDWN		= (1 << 12), /* power down PHYs */ | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 105 | HCTL_SOFTRST		= (1 << 13), /* global reset (no phy reset) */ | 
|  | 106 | HCTL_RPGSEL		= (1 << 15), /* register page select */ | 
|  | 107 |  | 
|  | 108 | HCTL_KNOWN_BITS		= HCTL_IRQOFF | HCTL_PWRDWN | HCTL_SOFTRST | | 
|  | 109 | HCTL_RPGSEL, | 
|  | 110 |  | 
|  | 111 | /* HOST_IRQ_(STAT|MASK) bits */ | 
|  | 112 | HIRQ_PORT0		= (1 << 0), | 
|  | 113 | HIRQ_PORT1		= (1 << 1), | 
|  | 114 | HIRQ_SOFT		= (1 << 14), | 
|  | 115 | HIRQ_GLOBAL		= (1 << 15), /* STAT only */ | 
|  | 116 |  | 
|  | 117 | /* PORT_IRQ_(STAT|MASK) bits */ | 
|  | 118 | PIRQ_OFFLINE		= (1 << 0),  /* device unplugged */ | 
|  | 119 | PIRQ_ONLINE		= (1 << 1),  /* device plugged */ | 
|  | 120 | PIRQ_COMPLETE		= (1 << 2),  /* completion interrupt */ | 
|  | 121 | PIRQ_FATAL		= (1 << 3),  /* fatal error */ | 
|  | 122 | PIRQ_ATA		= (1 << 4),  /* ATA interrupt */ | 
|  | 123 | PIRQ_REPLY		= (1 << 5),  /* reply FIFO not empty */ | 
|  | 124 | PIRQ_PENDING		= (1 << 7),  /* port IRQ pending (STAT only) */ | 
|  | 125 |  | 
|  | 126 | PIRQ_ERR		= PIRQ_OFFLINE | PIRQ_ONLINE | PIRQ_FATAL, | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 127 | PIRQ_MASK_DEFAULT	= PIRQ_REPLY | PIRQ_ATA, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 128 | PIRQ_MASK_FREEZE	= 0xff, | 
|  | 129 |  | 
|  | 130 | /* PORT_PRD_CTL bits */ | 
|  | 131 | PRD_CTL_START		= (1 << 0), | 
|  | 132 | PRD_CTL_WR		= (1 << 3), | 
|  | 133 | PRD_CTL_DMAEN		= (1 << 7),  /* DMA enable */ | 
|  | 134 |  | 
|  | 135 | /* PORT_IDMA_CTL bits */ | 
|  | 136 | IDMA_CTL_RST_ATA	= (1 << 2),  /* hardreset ATA bus */ | 
|  | 137 | IDMA_CTL_RST_IDMA	= (1 << 5),  /* reset IDMA machinary */ | 
|  | 138 | IDMA_CTL_GO		= (1 << 7),  /* IDMA mode go */ | 
|  | 139 | IDMA_CTL_ATA_NIEN	= (1 << 8),  /* ATA IRQ disable */ | 
| Tejun Heo | b0dd9b8 | 2008-04-30 16:35:09 +0900 | [diff] [blame] | 140 |  | 
|  | 141 | /* PORT_IDMA_STAT bits */ | 
|  | 142 | IDMA_STAT_PERR		= (1 << 0),  /* PCI ERROR MODE */ | 
|  | 143 | IDMA_STAT_CPBERR	= (1 << 1),  /* ADMA CPB error */ | 
|  | 144 | IDMA_STAT_LGCY		= (1 << 3),  /* ADMA legacy */ | 
|  | 145 | IDMA_STAT_UIRQ		= (1 << 4),  /* ADMA unsolicited irq */ | 
|  | 146 | IDMA_STAT_STPD		= (1 << 5),  /* ADMA stopped */ | 
|  | 147 | IDMA_STAT_PSD		= (1 << 6),  /* ADMA pause */ | 
|  | 148 | IDMA_STAT_DONE		= (1 << 7),  /* ADMA done */ | 
|  | 149 |  | 
|  | 150 | IDMA_STAT_ERR		= IDMA_STAT_PERR | IDMA_STAT_CPBERR, | 
|  | 151 |  | 
|  | 152 | /* CPB Control Flags*/ | 
|  | 153 | CPB_CTL_VALID		= (1 << 0),  /* CPB valid */ | 
|  | 154 | CPB_CTL_QUEUED		= (1 << 1),  /* queued command */ | 
|  | 155 | CPB_CTL_DATA		= (1 << 2),  /* data, rsvd in datasheet */ | 
|  | 156 | CPB_CTL_IEN		= (1 << 3),  /* PCI interrupt enable */ | 
|  | 157 | CPB_CTL_DEVDIR		= (1 << 4),  /* device direction control */ | 
|  | 158 |  | 
|  | 159 | /* CPB Response Flags */ | 
|  | 160 | CPB_RESP_DONE		= (1 << 0),  /* ATA command complete */ | 
|  | 161 | CPB_RESP_REL		= (1 << 1),  /* ATA release */ | 
|  | 162 | CPB_RESP_IGNORED	= (1 << 2),  /* CPB ignored */ | 
|  | 163 | CPB_RESP_ATA_ERR	= (1 << 3),  /* ATA command error */ | 
|  | 164 | CPB_RESP_SPURIOUS	= (1 << 4),  /* ATA spurious interrupt error */ | 
|  | 165 | CPB_RESP_UNDERFLOW	= (1 << 5),  /* APRD deficiency length error */ | 
|  | 166 | CPB_RESP_OVERFLOW	= (1 << 6),  /* APRD exccess length error */ | 
|  | 167 | CPB_RESP_CPB_ERR	= (1 << 7),  /* CPB error flag */ | 
|  | 168 |  | 
|  | 169 | /* PRD Control Flags */ | 
|  | 170 | PRD_DRAIN		= (1 << 1),  /* ignore data excess */ | 
|  | 171 | PRD_CDB			= (1 << 2),  /* atapi packet command pointer */ | 
|  | 172 | PRD_DIRECT_INTR		= (1 << 3),  /* direct interrupt */ | 
|  | 173 | PRD_DMA			= (1 << 4),  /* data transfer method */ | 
|  | 174 | PRD_WRITE		= (1 << 5),  /* data dir, rsvd in datasheet */ | 
|  | 175 | PRD_IOM			= (1 << 6),  /* io/memory transfer */ | 
|  | 176 | PRD_END			= (1 << 7),  /* APRD chain end */ | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 177 | }; | 
|  | 178 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 179 | /* Comman Parameter Block */ | 
|  | 180 | struct inic_cpb { | 
|  | 181 | u8		resp_flags;	/* Response Flags */ | 
|  | 182 | u8		error;		/* ATA Error */ | 
|  | 183 | u8		status;		/* ATA Status */ | 
|  | 184 | u8		ctl_flags;	/* Control Flags */ | 
|  | 185 | __le32		len;		/* Total Transfer Length */ | 
|  | 186 | __le32		prd;		/* First PRD pointer */ | 
|  | 187 | u8		rsvd[4]; | 
|  | 188 | /* 16 bytes */ | 
|  | 189 | u8		feature;	/* ATA Feature */ | 
|  | 190 | u8		hob_feature;	/* ATA Ex. Feature */ | 
|  | 191 | u8		device;		/* ATA Device/Head */ | 
|  | 192 | u8		mirctl;		/* Mirror Control */ | 
|  | 193 | u8		nsect;		/* ATA Sector Count */ | 
|  | 194 | u8		hob_nsect;	/* ATA Ex. Sector Count */ | 
|  | 195 | u8		lbal;		/* ATA Sector Number */ | 
|  | 196 | u8		hob_lbal;	/* ATA Ex. Sector Number */ | 
|  | 197 | u8		lbam;		/* ATA Cylinder Low */ | 
|  | 198 | u8		hob_lbam;	/* ATA Ex. Cylinder Low */ | 
|  | 199 | u8		lbah;		/* ATA Cylinder High */ | 
|  | 200 | u8		hob_lbah;	/* ATA Ex. Cylinder High */ | 
|  | 201 | u8		command;	/* ATA Command */ | 
|  | 202 | u8		ctl;		/* ATA Control */ | 
|  | 203 | u8		slave_error;	/* Slave ATA Error */ | 
|  | 204 | u8		slave_status;	/* Slave ATA Status */ | 
|  | 205 | /* 32 bytes */ | 
|  | 206 | } __packed; | 
|  | 207 |  | 
|  | 208 | /* Physical Region Descriptor */ | 
|  | 209 | struct inic_prd { | 
|  | 210 | __le32		mad;		/* Physical Memory Address */ | 
|  | 211 | __le16		len;		/* Transfer Length */ | 
|  | 212 | u8		rsvd; | 
|  | 213 | u8		flags;		/* Control Flags */ | 
|  | 214 | } __packed; | 
|  | 215 |  | 
|  | 216 | struct inic_pkt { | 
|  | 217 | struct inic_cpb	cpb; | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 218 | struct inic_prd	prd[LIBATA_MAX_PRD + 1];	/* + 1 for cdb */ | 
|  | 219 | u8		cdb[ATAPI_CDB_LEN]; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 220 | } __packed; | 
|  | 221 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 222 | struct inic_host_priv { | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 223 | void __iomem	*mmio_base; | 
| Tejun Heo | 36f674d | 2008-04-30 16:35:08 +0900 | [diff] [blame] | 224 | u16		cached_hctl; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 225 | }; | 
|  | 226 |  | 
|  | 227 | struct inic_port_priv { | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 228 | struct inic_pkt	*pkt; | 
|  | 229 | dma_addr_t	pkt_dma; | 
|  | 230 | u32		*cpb_tbl; | 
|  | 231 | dma_addr_t	cpb_tbl_dma; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 232 | }; | 
|  | 233 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 234 | static struct scsi_host_template inic_sht = { | 
| Tejun Heo | ab5b023 | 2008-04-30 16:35:12 +0900 | [diff] [blame] | 235 | ATA_BASE_SHT(DRV_NAME), | 
|  | 236 | .sg_tablesize	= LIBATA_MAX_PRD,	/* maybe it can be larger? */ | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 237 | .dma_boundary	= INIC_DMA_BOUNDARY, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 238 | }; | 
|  | 239 |  | 
|  | 240 | static const int scr_map[] = { | 
|  | 241 | [SCR_STATUS]	= 0, | 
|  | 242 | [SCR_ERROR]	= 1, | 
|  | 243 | [SCR_CONTROL]	= 2, | 
|  | 244 | }; | 
|  | 245 |  | 
| Jeff Garzik | 5796d1c | 2007-10-26 00:03:37 -0400 | [diff] [blame] | 246 | static void __iomem *inic_port_base(struct ata_port *ap) | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 247 | { | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 248 | struct inic_host_priv *hpriv = ap->host->private_data; | 
|  | 249 |  | 
|  | 250 | return hpriv->mmio_base + ap->port_no * PORT_SIZE; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 251 | } | 
|  | 252 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 253 | static void inic_reset_port(void __iomem *port_base) | 
|  | 254 | { | 
|  | 255 | void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 256 |  | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 257 | /* stop IDMA engine */ | 
|  | 258 | readw(idma_ctl); /* flush */ | 
|  | 259 | msleep(1); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 260 |  | 
|  | 261 | /* mask IRQ and assert reset */ | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 262 | writew(IDMA_CTL_RST_IDMA, idma_ctl); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 263 | readw(idma_ctl); /* flush */ | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 264 | msleep(1); | 
|  | 265 |  | 
|  | 266 | /* release reset */ | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 267 | writew(0, idma_ctl); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 268 |  | 
|  | 269 | /* clear irq */ | 
|  | 270 | writeb(0xff, port_base + PORT_IRQ_STAT); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 271 | } | 
|  | 272 |  | 
| Tejun Heo | 82ef04f | 2008-07-31 17:02:40 +0900 | [diff] [blame] | 273 | static int inic_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 274 | { | 
| Tejun Heo | 82ef04f | 2008-07-31 17:02:40 +0900 | [diff] [blame] | 275 | void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 276 | void __iomem *addr; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 277 |  | 
|  | 278 | if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) | 
| Tejun Heo | da3dbb1 | 2007-07-16 14:29:40 +0900 | [diff] [blame] | 279 | return -EINVAL; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 280 |  | 
|  | 281 | addr = scr_addr + scr_map[sc_reg] * 4; | 
| Tejun Heo | da3dbb1 | 2007-07-16 14:29:40 +0900 | [diff] [blame] | 282 | *val = readl(scr_addr + scr_map[sc_reg] * 4); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 283 |  | 
|  | 284 | /* this controller has stuck DIAG.N, ignore it */ | 
|  | 285 | if (sc_reg == SCR_ERROR) | 
| Tejun Heo | da3dbb1 | 2007-07-16 14:29:40 +0900 | [diff] [blame] | 286 | *val &= ~SERR_PHYRDY_CHG; | 
|  | 287 | return 0; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 288 | } | 
|  | 289 |  | 
| Tejun Heo | 82ef04f | 2008-07-31 17:02:40 +0900 | [diff] [blame] | 290 | static int inic_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 291 | { | 
| Tejun Heo | 82ef04f | 2008-07-31 17:02:40 +0900 | [diff] [blame] | 292 | void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 293 |  | 
|  | 294 | if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) | 
| Tejun Heo | da3dbb1 | 2007-07-16 14:29:40 +0900 | [diff] [blame] | 295 | return -EINVAL; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 296 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 297 | writel(val, scr_addr + scr_map[sc_reg] * 4); | 
| Tejun Heo | da3dbb1 | 2007-07-16 14:29:40 +0900 | [diff] [blame] | 298 | return 0; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 299 | } | 
|  | 300 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 301 | static void inic_stop_idma(struct ata_port *ap) | 
|  | 302 | { | 
|  | 303 | void __iomem *port_base = inic_port_base(ap); | 
|  | 304 |  | 
|  | 305 | readb(port_base + PORT_RPQ_FIFO); | 
|  | 306 | readb(port_base + PORT_RPQ_CNT); | 
|  | 307 | writew(0, port_base + PORT_IDMA_CTL); | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | static void inic_host_err_intr(struct ata_port *ap, u8 irq_stat, u16 idma_stat) | 
|  | 311 | { | 
|  | 312 | struct ata_eh_info *ehi = &ap->link.eh_info; | 
|  | 313 | struct inic_port_priv *pp = ap->private_data; | 
|  | 314 | struct inic_cpb *cpb = &pp->pkt->cpb; | 
|  | 315 | bool freeze = false; | 
|  | 316 |  | 
|  | 317 | ata_ehi_clear_desc(ehi); | 
|  | 318 | ata_ehi_push_desc(ehi, "irq_stat=0x%x idma_stat=0x%x", | 
|  | 319 | irq_stat, idma_stat); | 
|  | 320 |  | 
|  | 321 | inic_stop_idma(ap); | 
|  | 322 |  | 
|  | 323 | if (irq_stat & (PIRQ_OFFLINE | PIRQ_ONLINE)) { | 
|  | 324 | ata_ehi_push_desc(ehi, "hotplug"); | 
|  | 325 | ata_ehi_hotplugged(ehi); | 
|  | 326 | freeze = true; | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | if (idma_stat & IDMA_STAT_PERR) { | 
|  | 330 | ata_ehi_push_desc(ehi, "PCI error"); | 
|  | 331 | freeze = true; | 
|  | 332 | } | 
|  | 333 |  | 
|  | 334 | if (idma_stat & IDMA_STAT_CPBERR) { | 
|  | 335 | ata_ehi_push_desc(ehi, "CPB error"); | 
|  | 336 |  | 
|  | 337 | if (cpb->resp_flags & CPB_RESP_IGNORED) { | 
|  | 338 | __ata_ehi_push_desc(ehi, " ignored"); | 
|  | 339 | ehi->err_mask |= AC_ERR_INVALID; | 
|  | 340 | freeze = true; | 
|  | 341 | } | 
|  | 342 |  | 
|  | 343 | if (cpb->resp_flags & CPB_RESP_ATA_ERR) | 
|  | 344 | ehi->err_mask |= AC_ERR_DEV; | 
|  | 345 |  | 
|  | 346 | if (cpb->resp_flags & CPB_RESP_SPURIOUS) { | 
|  | 347 | __ata_ehi_push_desc(ehi, " spurious-intr"); | 
|  | 348 | ehi->err_mask |= AC_ERR_HSM; | 
|  | 349 | freeze = true; | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | if (cpb->resp_flags & | 
|  | 353 | (CPB_RESP_UNDERFLOW | CPB_RESP_OVERFLOW)) { | 
|  | 354 | __ata_ehi_push_desc(ehi, " data-over/underflow"); | 
|  | 355 | ehi->err_mask |= AC_ERR_HSM; | 
|  | 356 | freeze = true; | 
|  | 357 | } | 
|  | 358 | } | 
|  | 359 |  | 
|  | 360 | if (freeze) | 
|  | 361 | ata_port_freeze(ap); | 
|  | 362 | else | 
|  | 363 | ata_port_abort(ap); | 
|  | 364 | } | 
|  | 365 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 366 | static void inic_host_intr(struct ata_port *ap) | 
|  | 367 | { | 
|  | 368 | void __iomem *port_base = inic_port_base(ap); | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 369 | struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->link.active_tag); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 370 | u8 irq_stat; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 371 | u16 idma_stat; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 372 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 373 | /* read and clear IRQ status */ | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 374 | irq_stat = readb(port_base + PORT_IRQ_STAT); | 
|  | 375 | writeb(irq_stat, port_base + PORT_IRQ_STAT); | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 376 | idma_stat = readw(port_base + PORT_IDMA_STAT); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 377 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 378 | if (unlikely((irq_stat & PIRQ_ERR) || (idma_stat & IDMA_STAT_ERR))) | 
|  | 379 | inic_host_err_intr(ap, irq_stat, idma_stat); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 380 |  | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 381 | if (unlikely(!qc)) | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 382 | goto spurious; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 383 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 384 | if (likely(idma_stat & IDMA_STAT_DONE)) { | 
|  | 385 | inic_stop_idma(ap); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 386 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 387 | /* Depending on circumstances, device error | 
|  | 388 | * isn't reported by IDMA, check it explicitly. | 
|  | 389 | */ | 
|  | 390 | if (unlikely(readb(port_base + PORT_TF_COMMAND) & | 
|  | 391 | (ATA_DF | ATA_ERR))) | 
|  | 392 | qc->err_mask |= AC_ERR_DEV; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 393 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 394 | ata_qc_complete(qc); | 
|  | 395 | return; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 396 | } | 
|  | 397 |  | 
|  | 398 | spurious: | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 399 | ata_port_printk(ap, KERN_WARNING, "unhandled interrupt: " | 
|  | 400 | "cmd=0x%x irq_stat=0x%x idma_stat=0x%x\n", | 
|  | 401 | qc ? qc->tf.command : 0xff, irq_stat, idma_stat); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 402 | } | 
|  | 403 |  | 
|  | 404 | static irqreturn_t inic_interrupt(int irq, void *dev_instance) | 
|  | 405 | { | 
|  | 406 | struct ata_host *host = dev_instance; | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 407 | struct inic_host_priv *hpriv = host->private_data; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 408 | u16 host_irq_stat; | 
| Joe Perches | 87c8b22 | 2009-06-28 09:26:17 -0700 | [diff] [blame] | 409 | int i, handled = 0; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 410 |  | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 411 | host_irq_stat = readw(hpriv->mmio_base + HOST_IRQ_STAT); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 412 |  | 
|  | 413 | if (unlikely(!(host_irq_stat & HIRQ_GLOBAL))) | 
|  | 414 | goto out; | 
|  | 415 |  | 
|  | 416 | spin_lock(&host->lock); | 
|  | 417 |  | 
| Tejun Heo | 3e4ec34 | 2010-05-10 21:41:30 +0200 | [diff] [blame] | 418 | for (i = 0; i < NR_PORTS; i++) | 
|  | 419 | if (host_irq_stat & (HIRQ_PORT0 << i)) { | 
|  | 420 | inic_host_intr(host->ports[i]); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 421 | handled++; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 422 | } | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 423 |  | 
|  | 424 | spin_unlock(&host->lock); | 
|  | 425 |  | 
|  | 426 | out: | 
|  | 427 | return IRQ_RETVAL(handled); | 
|  | 428 | } | 
|  | 429 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 430 | static int inic_check_atapi_dma(struct ata_queued_cmd *qc) | 
|  | 431 | { | 
|  | 432 | /* For some reason ATAPI_PROT_DMA doesn't work for some | 
|  | 433 | * commands including writes and other misc ops.  Use PIO | 
|  | 434 | * protocol instead, which BTW is driven by the DMA engine | 
|  | 435 | * anyway, so it shouldn't make much difference for native | 
|  | 436 | * SATA devices. | 
|  | 437 | */ | 
|  | 438 | if (atapi_cmd_type(qc->cdb[0]) == READ) | 
|  | 439 | return 0; | 
|  | 440 | return 1; | 
|  | 441 | } | 
|  | 442 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 443 | static void inic_fill_sg(struct inic_prd *prd, struct ata_queued_cmd *qc) | 
|  | 444 | { | 
|  | 445 | struct scatterlist *sg; | 
|  | 446 | unsigned int si; | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 447 | u8 flags = 0; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 448 |  | 
|  | 449 | if (qc->tf.flags & ATA_TFLAG_WRITE) | 
|  | 450 | flags |= PRD_WRITE; | 
|  | 451 |  | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 452 | if (ata_is_dma(qc->tf.protocol)) | 
|  | 453 | flags |= PRD_DMA; | 
|  | 454 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 455 | for_each_sg(qc->sg, sg, qc->n_elem, si) { | 
|  | 456 | prd->mad = cpu_to_le32(sg_dma_address(sg)); | 
|  | 457 | prd->len = cpu_to_le16(sg_dma_len(sg)); | 
|  | 458 | prd->flags = flags; | 
|  | 459 | prd++; | 
|  | 460 | } | 
|  | 461 |  | 
|  | 462 | WARN_ON(!si); | 
|  | 463 | prd[-1].flags |= PRD_END; | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | static void inic_qc_prep(struct ata_queued_cmd *qc) | 
|  | 467 | { | 
|  | 468 | struct inic_port_priv *pp = qc->ap->private_data; | 
|  | 469 | struct inic_pkt *pkt = pp->pkt; | 
|  | 470 | struct inic_cpb *cpb = &pkt->cpb; | 
|  | 471 | struct inic_prd *prd = pkt->prd; | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 472 | bool is_atapi = ata_is_atapi(qc->tf.protocol); | 
|  | 473 | bool is_data = ata_is_data(qc->tf.protocol); | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 474 | unsigned int cdb_len = 0; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 475 |  | 
|  | 476 | VPRINTK("ENTER\n"); | 
|  | 477 |  | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 478 | if (is_atapi) | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 479 | cdb_len = qc->dev->cdb_len; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 480 |  | 
|  | 481 | /* prepare packet, based on initio driver */ | 
|  | 482 | memset(pkt, 0, sizeof(struct inic_pkt)); | 
|  | 483 |  | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 484 | cpb->ctl_flags = CPB_CTL_VALID | CPB_CTL_IEN; | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 485 | if (is_atapi || is_data) | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 486 | cpb->ctl_flags |= CPB_CTL_DATA; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 487 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 488 | cpb->len = cpu_to_le32(qc->nbytes + cdb_len); | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 489 | cpb->prd = cpu_to_le32(pp->pkt_dma + offsetof(struct inic_pkt, prd)); | 
|  | 490 |  | 
|  | 491 | cpb->device = qc->tf.device; | 
|  | 492 | cpb->feature = qc->tf.feature; | 
|  | 493 | cpb->nsect = qc->tf.nsect; | 
|  | 494 | cpb->lbal = qc->tf.lbal; | 
|  | 495 | cpb->lbam = qc->tf.lbam; | 
|  | 496 | cpb->lbah = qc->tf.lbah; | 
|  | 497 |  | 
|  | 498 | if (qc->tf.flags & ATA_TFLAG_LBA48) { | 
|  | 499 | cpb->hob_feature = qc->tf.hob_feature; | 
|  | 500 | cpb->hob_nsect = qc->tf.hob_nsect; | 
|  | 501 | cpb->hob_lbal = qc->tf.hob_lbal; | 
|  | 502 | cpb->hob_lbam = qc->tf.hob_lbam; | 
|  | 503 | cpb->hob_lbah = qc->tf.hob_lbah; | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 | cpb->command = qc->tf.command; | 
|  | 507 | /* don't load ctl - dunno why.  it's like that in the initio driver */ | 
|  | 508 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 509 | /* setup PRD for CDB */ | 
|  | 510 | if (is_atapi) { | 
|  | 511 | memcpy(pkt->cdb, qc->cdb, ATAPI_CDB_LEN); | 
|  | 512 | prd->mad = cpu_to_le32(pp->pkt_dma + | 
|  | 513 | offsetof(struct inic_pkt, cdb)); | 
|  | 514 | prd->len = cpu_to_le16(cdb_len); | 
|  | 515 | prd->flags = PRD_CDB | PRD_WRITE; | 
|  | 516 | if (!is_data) | 
|  | 517 | prd->flags |= PRD_END; | 
|  | 518 | prd++; | 
|  | 519 | } | 
|  | 520 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 521 | /* setup sg table */ | 
| Tejun Heo | 049e8e0 | 2008-04-30 16:35:13 +0900 | [diff] [blame] | 522 | if (is_data) | 
|  | 523 | inic_fill_sg(prd, qc); | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 524 |  | 
|  | 525 | pp->cpb_tbl[0] = pp->pkt_dma; | 
|  | 526 | } | 
|  | 527 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 528 | static unsigned int inic_qc_issue(struct ata_queued_cmd *qc) | 
|  | 529 | { | 
|  | 530 | struct ata_port *ap = qc->ap; | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 531 | void __iomem *port_base = inic_port_base(ap); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 532 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 533 | /* fire up the ADMA engine */ | 
| Bob Stewart | 9958066 | 2008-09-11 11:50:03 +0200 | [diff] [blame] | 534 | writew(HCTL_FTHD0 | HCTL_LEDEN, port_base + HOST_CTL); | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 535 | writew(IDMA_CTL_GO, port_base + PORT_IDMA_CTL); | 
|  | 536 | writeb(0, port_base + PORT_CPB_PTQFIFO); | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 537 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 538 | return 0; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 539 | } | 
|  | 540 |  | 
| Tejun Heo | 364fac0 | 2008-05-01 23:55:58 +0900 | [diff] [blame] | 541 | static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf) | 
|  | 542 | { | 
|  | 543 | void __iomem *port_base = inic_port_base(ap); | 
|  | 544 |  | 
|  | 545 | tf->feature	= readb(port_base + PORT_TF_FEATURE); | 
|  | 546 | tf->nsect	= readb(port_base + PORT_TF_NSECT); | 
|  | 547 | tf->lbal	= readb(port_base + PORT_TF_LBAL); | 
|  | 548 | tf->lbam	= readb(port_base + PORT_TF_LBAM); | 
|  | 549 | tf->lbah	= readb(port_base + PORT_TF_LBAH); | 
|  | 550 | tf->device	= readb(port_base + PORT_TF_DEVICE); | 
|  | 551 | tf->command	= readb(port_base + PORT_TF_COMMAND); | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc) | 
|  | 555 | { | 
|  | 556 | struct ata_taskfile *rtf = &qc->result_tf; | 
|  | 557 | struct ata_taskfile tf; | 
|  | 558 |  | 
|  | 559 | /* FIXME: Except for status and error, result TF access | 
|  | 560 | * doesn't work.  I tried reading from BAR0/2, CPB and BAR5. | 
|  | 561 | * None works regardless of which command interface is used. | 
|  | 562 | * For now return true iff status indicates device error. | 
|  | 563 | * This means that we're reporting bogus sector for RW | 
|  | 564 | * failures.  Eeekk.... | 
|  | 565 | */ | 
|  | 566 | inic_tf_read(qc->ap, &tf); | 
|  | 567 |  | 
|  | 568 | if (!(tf.command & ATA_ERR)) | 
|  | 569 | return false; | 
|  | 570 |  | 
|  | 571 | rtf->command = tf.command; | 
|  | 572 | rtf->feature = tf.feature; | 
|  | 573 | return true; | 
|  | 574 | } | 
|  | 575 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 576 | static void inic_freeze(struct ata_port *ap) | 
|  | 577 | { | 
|  | 578 | void __iomem *port_base = inic_port_base(ap); | 
|  | 579 |  | 
| Tejun Heo | ab5b023 | 2008-04-30 16:35:12 +0900 | [diff] [blame] | 580 | writeb(PIRQ_MASK_FREEZE, port_base + PORT_IRQ_MASK); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 581 | writeb(0xff, port_base + PORT_IRQ_STAT); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 582 | } | 
|  | 583 |  | 
|  | 584 | static void inic_thaw(struct ata_port *ap) | 
|  | 585 | { | 
|  | 586 | void __iomem *port_base = inic_port_base(ap); | 
|  | 587 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 588 | writeb(0xff, port_base + PORT_IRQ_STAT); | 
| Tejun Heo | ab5b023 | 2008-04-30 16:35:12 +0900 | [diff] [blame] | 589 | writeb(PIRQ_MASK_DEFAULT, port_base + PORT_IRQ_MASK); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 590 | } | 
|  | 591 |  | 
| Tejun Heo | 364fac0 | 2008-05-01 23:55:58 +0900 | [diff] [blame] | 592 | static int inic_check_ready(struct ata_link *link) | 
|  | 593 | { | 
|  | 594 | void __iomem *port_base = inic_port_base(link->ap); | 
|  | 595 |  | 
|  | 596 | return ata_check_ready(readb(port_base + PORT_TF_COMMAND)); | 
|  | 597 | } | 
|  | 598 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 599 | /* | 
|  | 600 | * SRST and SControl hardreset don't give valid signature on this | 
|  | 601 | * controller.  Only controller specific hardreset mechanism works. | 
|  | 602 | */ | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 603 | static int inic_hardreset(struct ata_link *link, unsigned int *class, | 
| Tejun Heo | d4b2bab | 2007-02-02 16:50:52 +0900 | [diff] [blame] | 604 | unsigned long deadline) | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 605 | { | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 606 | struct ata_port *ap = link->ap; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 607 | void __iomem *port_base = inic_port_base(ap); | 
|  | 608 | void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 609 | const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 610 | int rc; | 
|  | 611 |  | 
|  | 612 | /* hammer it into sane state */ | 
|  | 613 | inic_reset_port(port_base); | 
|  | 614 |  | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 615 | writew(IDMA_CTL_RST_ATA, idma_ctl); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 616 | readw(idma_ctl);	/* flush */ | 
| Tejun Heo | 97750ce | 2010-09-06 17:56:29 +0200 | [diff] [blame] | 617 | ata_msleep(ap, 1); | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 618 | writew(0, idma_ctl); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 619 |  | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 620 | rc = sata_link_resume(link, timing, deadline); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 621 | if (rc) { | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 622 | ata_link_printk(link, KERN_WARNING, "failed to resume " | 
| Tejun Heo | fe33460 | 2007-02-02 15:29:52 +0900 | [diff] [blame] | 623 | "link after reset (errno=%d)\n", rc); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 624 | return rc; | 
|  | 625 | } | 
|  | 626 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 627 | *class = ATA_DEV_NONE; | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 628 | if (ata_link_online(link)) { | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 629 | struct ata_taskfile tf; | 
|  | 630 |  | 
| Tejun Heo | 705e76b | 2008-04-07 22:47:19 +0900 | [diff] [blame] | 631 | /* wait for link to become ready */ | 
| Tejun Heo | 364fac0 | 2008-05-01 23:55:58 +0900 | [diff] [blame] | 632 | rc = ata_wait_after_reset(link, deadline, inic_check_ready); | 
| Tejun Heo | 9b89391 | 2007-02-02 16:50:52 +0900 | [diff] [blame] | 633 | /* link occupied, -ENODEV too is an error */ | 
|  | 634 | if (rc) { | 
| Tejun Heo | cc0680a | 2007-08-06 18:36:23 +0900 | [diff] [blame] | 635 | ata_link_printk(link, KERN_WARNING, "device not ready " | 
| Tejun Heo | d4b2bab | 2007-02-02 16:50:52 +0900 | [diff] [blame] | 636 | "after hardreset (errno=%d)\n", rc); | 
|  | 637 | return rc; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 638 | } | 
|  | 639 |  | 
| Tejun Heo | 364fac0 | 2008-05-01 23:55:58 +0900 | [diff] [blame] | 640 | inic_tf_read(ap, &tf); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 641 | *class = ata_dev_classify(&tf); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 642 | } | 
|  | 643 |  | 
|  | 644 | return 0; | 
|  | 645 | } | 
|  | 646 |  | 
|  | 647 | static void inic_error_handler(struct ata_port *ap) | 
|  | 648 | { | 
|  | 649 | void __iomem *port_base = inic_port_base(ap); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 650 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 651 | inic_reset_port(port_base); | 
| Tejun Heo | a1efdab | 2008-03-25 12:22:50 +0900 | [diff] [blame] | 652 | ata_std_error_handler(ap); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 653 | } | 
|  | 654 |  | 
|  | 655 | static void inic_post_internal_cmd(struct ata_queued_cmd *qc) | 
|  | 656 | { | 
|  | 657 | /* make DMA engine forget about the failed command */ | 
| Tejun Heo | a51d644 | 2007-03-20 15:24:11 +0900 | [diff] [blame] | 658 | if (qc->flags & ATA_QCFLAG_FAILED) | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 659 | inic_reset_port(inic_port_base(qc->ap)); | 
|  | 660 | } | 
|  | 661 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 662 | static void init_port(struct ata_port *ap) | 
|  | 663 | { | 
|  | 664 | void __iomem *port_base = inic_port_base(ap); | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 665 | struct inic_port_priv *pp = ap->private_data; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 666 |  | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 667 | /* clear packet and CPB table */ | 
|  | 668 | memset(pp->pkt, 0, sizeof(struct inic_pkt)); | 
|  | 669 | memset(pp->cpb_tbl, 0, IDMA_CPB_TBL_SIZE); | 
|  | 670 |  | 
| Tejun Heo | 6bc0d39 | 2010-05-10 21:41:31 +0200 | [diff] [blame] | 671 | /* setup CPB lookup table addresses */ | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 672 | writel(pp->cpb_tbl_dma, port_base + PORT_CPB_CPBLAR); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 673 | } | 
|  | 674 |  | 
|  | 675 | static int inic_port_resume(struct ata_port *ap) | 
|  | 676 | { | 
|  | 677 | init_port(ap); | 
|  | 678 | return 0; | 
|  | 679 | } | 
|  | 680 |  | 
|  | 681 | static int inic_port_start(struct ata_port *ap) | 
|  | 682 | { | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 683 | struct device *dev = ap->host->dev; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 684 | struct inic_port_priv *pp; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 685 |  | 
|  | 686 | /* alloc and initialize private data */ | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 687 | pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 688 | if (!pp) | 
|  | 689 | return -ENOMEM; | 
|  | 690 | ap->private_data = pp; | 
|  | 691 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 692 | /* Alloc resources */ | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 693 | pp->pkt = dmam_alloc_coherent(dev, sizeof(struct inic_pkt), | 
|  | 694 | &pp->pkt_dma, GFP_KERNEL); | 
|  | 695 | if (!pp->pkt) | 
|  | 696 | return -ENOMEM; | 
|  | 697 |  | 
|  | 698 | pp->cpb_tbl = dmam_alloc_coherent(dev, IDMA_CPB_TBL_SIZE, | 
|  | 699 | &pp->cpb_tbl_dma, GFP_KERNEL); | 
|  | 700 | if (!pp->cpb_tbl) | 
|  | 701 | return -ENOMEM; | 
|  | 702 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 703 | init_port(ap); | 
|  | 704 |  | 
|  | 705 | return 0; | 
|  | 706 | } | 
|  | 707 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 708 | static struct ata_port_operations inic_port_ops = { | 
| Tejun Heo | f8b0685a | 2008-04-30 16:35:15 +0900 | [diff] [blame] | 709 | .inherits		= &sata_port_ops, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 710 |  | 
| Tejun Heo | b3f677e | 2008-04-30 16:35:14 +0900 | [diff] [blame] | 711 | .check_atapi_dma	= inic_check_atapi_dma, | 
| Tejun Heo | 3ad400a | 2008-04-30 16:35:11 +0900 | [diff] [blame] | 712 | .qc_prep		= inic_qc_prep, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 713 | .qc_issue		= inic_qc_issue, | 
| Tejun Heo | 364fac0 | 2008-05-01 23:55:58 +0900 | [diff] [blame] | 714 | .qc_fill_rtf		= inic_qc_fill_rtf, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 715 |  | 
|  | 716 | .freeze			= inic_freeze, | 
|  | 717 | .thaw			= inic_thaw, | 
| Tejun Heo | a1efdab | 2008-03-25 12:22:50 +0900 | [diff] [blame] | 718 | .hardreset		= inic_hardreset, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 719 | .error_handler		= inic_error_handler, | 
|  | 720 | .post_internal_cmd	= inic_post_internal_cmd, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 721 |  | 
| Tejun Heo | 029cfd6 | 2008-03-25 12:22:49 +0900 | [diff] [blame] | 722 | .scr_read		= inic_scr_read, | 
|  | 723 | .scr_write		= inic_scr_write, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 724 |  | 
| Tejun Heo | 029cfd6 | 2008-03-25 12:22:49 +0900 | [diff] [blame] | 725 | .port_resume		= inic_port_resume, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 726 | .port_start		= inic_port_start, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 727 | }; | 
|  | 728 |  | 
|  | 729 | static struct ata_port_info inic_port_info = { | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 730 | .flags			= ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, | 
| Erik Inge Bolsø | 14bdef9 | 2009-03-14 21:38:24 +0100 | [diff] [blame] | 731 | .pio_mask		= ATA_PIO4, | 
|  | 732 | .mwdma_mask		= ATA_MWDMA2, | 
| Jeff Garzik | bf6263a | 2007-07-09 12:16:50 -0400 | [diff] [blame] | 733 | .udma_mask		= ATA_UDMA6, | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 734 | .port_ops		= &inic_port_ops | 
|  | 735 | }; | 
|  | 736 |  | 
|  | 737 | static int init_controller(void __iomem *mmio_base, u16 hctl) | 
|  | 738 | { | 
|  | 739 | int i; | 
|  | 740 | u16 val; | 
|  | 741 |  | 
|  | 742 | hctl &= ~HCTL_KNOWN_BITS; | 
|  | 743 |  | 
|  | 744 | /* Soft reset whole controller.  Spec says reset duration is 3 | 
|  | 745 | * PCI clocks, be generous and give it 10ms. | 
|  | 746 | */ | 
|  | 747 | writew(hctl | HCTL_SOFTRST, mmio_base + HOST_CTL); | 
|  | 748 | readw(mmio_base + HOST_CTL); /* flush */ | 
|  | 749 |  | 
|  | 750 | for (i = 0; i < 10; i++) { | 
|  | 751 | msleep(1); | 
|  | 752 | val = readw(mmio_base + HOST_CTL); | 
|  | 753 | if (!(val & HCTL_SOFTRST)) | 
|  | 754 | break; | 
|  | 755 | } | 
|  | 756 |  | 
|  | 757 | if (val & HCTL_SOFTRST) | 
|  | 758 | return -EIO; | 
|  | 759 |  | 
|  | 760 | /* mask all interrupts and reset ports */ | 
|  | 761 | for (i = 0; i < NR_PORTS; i++) { | 
|  | 762 | void __iomem *port_base = mmio_base + i * PORT_SIZE; | 
|  | 763 |  | 
|  | 764 | writeb(0xff, port_base + PORT_IRQ_MASK); | 
|  | 765 | inic_reset_port(port_base); | 
|  | 766 | } | 
|  | 767 |  | 
|  | 768 | /* port IRQ is masked now, unmask global IRQ */ | 
|  | 769 | writew(hctl & ~HCTL_IRQOFF, mmio_base + HOST_CTL); | 
|  | 770 | val = readw(mmio_base + HOST_IRQ_MASK); | 
|  | 771 | val &= ~(HIRQ_PORT0 | HIRQ_PORT1); | 
|  | 772 | writew(val, mmio_base + HOST_IRQ_MASK); | 
|  | 773 |  | 
|  | 774 | return 0; | 
|  | 775 | } | 
|  | 776 |  | 
| Tejun Heo | 438ac6d | 2007-03-02 17:31:26 +0900 | [diff] [blame] | 777 | #ifdef CONFIG_PM | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 778 | static int inic_pci_device_resume(struct pci_dev *pdev) | 
|  | 779 | { | 
|  | 780 | struct ata_host *host = dev_get_drvdata(&pdev->dev); | 
|  | 781 | struct inic_host_priv *hpriv = host->private_data; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 782 | int rc; | 
|  | 783 |  | 
| Dmitriy Monakhov | 5aea408 | 2007-03-06 02:37:54 -0800 | [diff] [blame] | 784 | rc = ata_pci_device_do_resume(pdev); | 
|  | 785 | if (rc) | 
|  | 786 | return rc; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 787 |  | 
|  | 788 | if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 789 | rc = init_controller(hpriv->mmio_base, hpriv->cached_hctl); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 790 | if (rc) | 
|  | 791 | return rc; | 
|  | 792 | } | 
|  | 793 |  | 
|  | 794 | ata_host_resume(host); | 
|  | 795 |  | 
|  | 796 | return 0; | 
|  | 797 | } | 
| Tejun Heo | 438ac6d | 2007-03-02 17:31:26 +0900 | [diff] [blame] | 798 | #endif | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 799 |  | 
|  | 800 | static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | 
|  | 801 | { | 
|  | 802 | static int printed_version; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 803 | const struct ata_port_info *ppi[] = { &inic_port_info, NULL }; | 
|  | 804 | struct ata_host *host; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 805 | struct inic_host_priv *hpriv; | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 806 | void __iomem * const *iomap; | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 807 | int mmio_bar; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 808 | int i, rc; | 
|  | 809 |  | 
|  | 810 | if (!printed_version++) | 
|  | 811 | dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); | 
|  | 812 |  | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 813 | /* alloc host */ | 
|  | 814 | host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS); | 
|  | 815 | hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); | 
|  | 816 | if (!host || !hpriv) | 
|  | 817 | return -ENOMEM; | 
|  | 818 |  | 
|  | 819 | host->private_data = hpriv; | 
|  | 820 |  | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 821 | /* Acquire resources and fill host.  Note that PCI and cardbus | 
|  | 822 | * use different BARs. | 
|  | 823 | */ | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 824 | rc = pcim_enable_device(pdev); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 825 | if (rc) | 
|  | 826 | return rc; | 
|  | 827 |  | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 828 | if (pci_resource_flags(pdev, MMIO_BAR_PCI) & IORESOURCE_MEM) | 
|  | 829 | mmio_bar = MMIO_BAR_PCI; | 
|  | 830 | else | 
|  | 831 | mmio_bar = MMIO_BAR_CARDBUS; | 
|  | 832 |  | 
|  | 833 | rc = pcim_iomap_regions(pdev, 1 << mmio_bar, DRV_NAME); | 
| Tejun Heo | 0d5ff56 | 2007-02-01 15:06:36 +0900 | [diff] [blame] | 834 | if (rc) | 
|  | 835 | return rc; | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 836 | host->iomap = iomap = pcim_iomap_table(pdev); | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 837 | hpriv->mmio_base = iomap[mmio_bar]; | 
|  | 838 | hpriv->cached_hctl = readw(hpriv->mmio_base + HOST_CTL); | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 839 |  | 
|  | 840 | for (i = 0; i < NR_PORTS; i++) { | 
| Tejun Heo | cbcdd87 | 2007-08-18 13:14:55 +0900 | [diff] [blame] | 841 | struct ata_port *ap = host->ports[i]; | 
| Tejun Heo | cbcdd87 | 2007-08-18 13:14:55 +0900 | [diff] [blame] | 842 |  | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 843 | ata_port_pbar_desc(ap, mmio_bar, -1, "mmio"); | 
|  | 844 | ata_port_pbar_desc(ap, mmio_bar, i * PORT_SIZE, "port"); | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 845 | } | 
|  | 846 |  | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 847 | /* Set dma_mask.  This devices doesn't support 64bit addressing. */ | 
| Yang Hongyang | 284901a | 2009-04-06 19:01:15 -0700 | [diff] [blame] | 848 | rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 849 | if (rc) { | 
|  | 850 | dev_printk(KERN_ERR, &pdev->dev, | 
|  | 851 | "32-bit DMA enable failed\n"); | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 852 | return rc; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 853 | } | 
|  | 854 |  | 
| Yang Hongyang | 284901a | 2009-04-06 19:01:15 -0700 | [diff] [blame] | 855 | rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 856 | if (rc) { | 
|  | 857 | dev_printk(KERN_ERR, &pdev->dev, | 
|  | 858 | "32-bit consistent DMA enable failed\n"); | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 859 | return rc; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 860 | } | 
|  | 861 |  | 
| FUJITA Tomonori | b7d8629 | 2008-02-04 22:28:05 -0800 | [diff] [blame] | 862 | /* | 
|  | 863 | * This controller is braindamaged.  dma_boundary is 0xffff | 
|  | 864 | * like others but it will lock up the whole machine HARD if | 
|  | 865 | * 65536 byte PRD entry is fed. Reduce maximum segment size. | 
|  | 866 | */ | 
|  | 867 | rc = pci_set_dma_max_seg_size(pdev, 65536 - 512); | 
|  | 868 | if (rc) { | 
|  | 869 | dev_printk(KERN_ERR, &pdev->dev, | 
|  | 870 | "failed to set the maximum segment size.\n"); | 
|  | 871 | return rc; | 
|  | 872 | } | 
|  | 873 |  | 
| Tejun Heo | ba66b24 | 2008-04-30 16:35:16 +0900 | [diff] [blame] | 874 | rc = init_controller(hpriv->mmio_base, hpriv->cached_hctl); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 875 | if (rc) { | 
|  | 876 | dev_printk(KERN_ERR, &pdev->dev, | 
|  | 877 | "failed to initialize controller\n"); | 
| Tejun Heo | 24dc5f3 | 2007-01-20 16:00:28 +0900 | [diff] [blame] | 878 | return rc; | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 879 | } | 
|  | 880 |  | 
|  | 881 | pci_set_master(pdev); | 
| Tejun Heo | 4447d35 | 2007-04-17 23:44:08 +0900 | [diff] [blame] | 882 | return ata_host_activate(host, pdev->irq, inic_interrupt, IRQF_SHARED, | 
|  | 883 | &inic_sht); | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 884 | } | 
|  | 885 |  | 
|  | 886 | static const struct pci_device_id inic_pci_tbl[] = { | 
|  | 887 | { PCI_VDEVICE(INIT, 0x1622), }, | 
|  | 888 | { }, | 
|  | 889 | }; | 
|  | 890 |  | 
|  | 891 | static struct pci_driver inic_pci_driver = { | 
|  | 892 | .name 		= DRV_NAME, | 
|  | 893 | .id_table	= inic_pci_tbl, | 
| Tejun Heo | 438ac6d | 2007-03-02 17:31:26 +0900 | [diff] [blame] | 894 | #ifdef CONFIG_PM | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 895 | .suspend	= ata_pci_device_suspend, | 
|  | 896 | .resume		= inic_pci_device_resume, | 
| Tejun Heo | 438ac6d | 2007-03-02 17:31:26 +0900 | [diff] [blame] | 897 | #endif | 
| Tejun Heo | 1fd7a69 | 2007-01-03 17:32:45 +0900 | [diff] [blame] | 898 | .probe 		= inic_init_one, | 
|  | 899 | .remove		= ata_pci_remove_one, | 
|  | 900 | }; | 
|  | 901 |  | 
|  | 902 | static int __init inic_init(void) | 
|  | 903 | { | 
|  | 904 | return pci_register_driver(&inic_pci_driver); | 
|  | 905 | } | 
|  | 906 |  | 
|  | 907 | static void __exit inic_exit(void) | 
|  | 908 | { | 
|  | 909 | pci_unregister_driver(&inic_pci_driver); | 
|  | 910 | } | 
|  | 911 |  | 
|  | 912 | MODULE_AUTHOR("Tejun Heo"); | 
|  | 913 | MODULE_DESCRIPTION("low-level driver for Initio 162x SATA"); | 
|  | 914 | MODULE_LICENSE("GPL v2"); | 
|  | 915 | MODULE_DEVICE_TABLE(pci, inic_pci_tbl); | 
|  | 916 | MODULE_VERSION(DRV_VERSION); | 
|  | 917 |  | 
|  | 918 | module_init(inic_init); | 
|  | 919 | module_exit(inic_exit); |