| 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); |