xref: /openbmc/linux/drivers/scsi/hptiop.c (revision d67790dd)
18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ede1e6f8SHighPoint Linux Team /*
300f59701SHighPoint Linux Team  * HighPoint RR3xxx/4xxx controller driver for Linux
4a93429c3Slinux  * Copyright (C) 2006-2015 HighPoint Technologies, Inc. All Rights Reserved.
5ede1e6f8SHighPoint Linux Team  *
6ede1e6f8SHighPoint Linux Team  * Please report bugs/comments/suggestions to linux@highpoint-tech.com
7ede1e6f8SHighPoint Linux Team  *
8ede1e6f8SHighPoint Linux Team  * For more information, visit http://www.highpoint-tech.com
9ede1e6f8SHighPoint Linux Team  */
10ede1e6f8SHighPoint Linux Team #include <linux/module.h>
11ede1e6f8SHighPoint Linux Team #include <linux/types.h>
12ede1e6f8SHighPoint Linux Team #include <linux/string.h>
13ede1e6f8SHighPoint Linux Team #include <linux/kernel.h>
14ede1e6f8SHighPoint Linux Team #include <linux/pci.h>
15ede1e6f8SHighPoint Linux Team #include <linux/interrupt.h>
16ede1e6f8SHighPoint Linux Team #include <linux/errno.h>
17ede1e6f8SHighPoint Linux Team #include <linux/delay.h>
18ede1e6f8SHighPoint Linux Team #include <linux/timer.h>
19ede1e6f8SHighPoint Linux Team #include <linux/spinlock.h>
205a0e3ad6STejun Heo #include <linux/gfp.h>
217c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
22ede1e6f8SHighPoint Linux Team #include <asm/io.h>
23ede1e6f8SHighPoint Linux Team #include <asm/div64.h>
24ede1e6f8SHighPoint Linux Team #include <scsi/scsi_cmnd.h>
25ede1e6f8SHighPoint Linux Team #include <scsi/scsi_device.h>
26ede1e6f8SHighPoint Linux Team #include <scsi/scsi.h>
27ede1e6f8SHighPoint Linux Team #include <scsi/scsi_tcq.h>
28ede1e6f8SHighPoint Linux Team #include <scsi/scsi_host.h>
29ede1e6f8SHighPoint Linux Team 
30ede1e6f8SHighPoint Linux Team #include "hptiop.h"
31ede1e6f8SHighPoint Linux Team 
32ede1e6f8SHighPoint Linux Team MODULE_AUTHOR("HighPoint Technologies, Inc.");
3300f59701SHighPoint Linux Team MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver");
34ede1e6f8SHighPoint Linux Team 
35ede1e6f8SHighPoint Linux Team static char driver_name[] = "hptiop";
3600f59701SHighPoint Linux Team static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver";
37a93429c3Slinux static const char driver_ver[] = "v1.10.0";
38ede1e6f8SHighPoint Linux Team 
3900f59701SHighPoint Linux Team static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec);
4000f59701SHighPoint Linux Team static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
4100f59701SHighPoint Linux Team 				struct hpt_iop_request_scsi_command *req);
4200f59701SHighPoint Linux Team static void hptiop_host_request_callback_itl(struct hptiop_hba *hba, u32 tag);
4300f59701SHighPoint Linux Team static void hptiop_iop_request_callback_itl(struct hptiop_hba *hba, u32 tag);
44ede1e6f8SHighPoint Linux Team static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg);
45ede1e6f8SHighPoint Linux Team 
iop_wait_ready_itl(struct hptiop_hba * hba,u32 millisec)4600f59701SHighPoint Linux Team static int iop_wait_ready_itl(struct hptiop_hba *hba, u32 millisec)
47ede1e6f8SHighPoint Linux Team {
48ede1e6f8SHighPoint Linux Team 	u32 req = 0;
49ede1e6f8SHighPoint Linux Team 	int i;
50ede1e6f8SHighPoint Linux Team 
51ede1e6f8SHighPoint Linux Team 	for (i = 0; i < millisec; i++) {
5200f59701SHighPoint Linux Team 		req = readl(&hba->u.itl.iop->inbound_queue);
53ede1e6f8SHighPoint Linux Team 		if (req != IOPMU_QUEUE_EMPTY)
54ede1e6f8SHighPoint Linux Team 			break;
55ede1e6f8SHighPoint Linux Team 		msleep(1);
56ede1e6f8SHighPoint Linux Team 	}
57ede1e6f8SHighPoint Linux Team 
58ede1e6f8SHighPoint Linux Team 	if (req != IOPMU_QUEUE_EMPTY) {
5900f59701SHighPoint Linux Team 		writel(req, &hba->u.itl.iop->outbound_queue);
6000f59701SHighPoint Linux Team 		readl(&hba->u.itl.iop->outbound_intstatus);
61ede1e6f8SHighPoint Linux Team 		return 0;
62ede1e6f8SHighPoint Linux Team 	}
63ede1e6f8SHighPoint Linux Team 
64ede1e6f8SHighPoint Linux Team 	return -1;
65ede1e6f8SHighPoint Linux Team }
66ede1e6f8SHighPoint Linux Team 
iop_wait_ready_mv(struct hptiop_hba * hba,u32 millisec)6700f59701SHighPoint Linux Team static int iop_wait_ready_mv(struct hptiop_hba *hba, u32 millisec)
68ede1e6f8SHighPoint Linux Team {
6900f59701SHighPoint Linux Team 	return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
70ede1e6f8SHighPoint Linux Team }
71ede1e6f8SHighPoint Linux Team 
iop_wait_ready_mvfrey(struct hptiop_hba * hba,u32 millisec)72286aa031SHighPoint Linux Team static int iop_wait_ready_mvfrey(struct hptiop_hba *hba, u32 millisec)
73286aa031SHighPoint Linux Team {
74286aa031SHighPoint Linux Team 	return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
75286aa031SHighPoint Linux Team }
76286aa031SHighPoint Linux Team 
hptiop_request_callback_itl(struct hptiop_hba * hba,u32 tag)7700f59701SHighPoint Linux Team static void hptiop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
7800f59701SHighPoint Linux Team {
7900f59701SHighPoint Linux Team 	if (tag & IOPMU_QUEUE_ADDR_HOST_BIT)
8000f59701SHighPoint Linux Team 		hptiop_host_request_callback_itl(hba,
8100f59701SHighPoint Linux Team 				tag & ~IOPMU_QUEUE_ADDR_HOST_BIT);
8200f59701SHighPoint Linux Team 	else
8300f59701SHighPoint Linux Team 		hptiop_iop_request_callback_itl(hba, tag);
8400f59701SHighPoint Linux Team }
8500f59701SHighPoint Linux Team 
hptiop_drain_outbound_queue_itl(struct hptiop_hba * hba)8600f59701SHighPoint Linux Team static void hptiop_drain_outbound_queue_itl(struct hptiop_hba *hba)
87ede1e6f8SHighPoint Linux Team {
88ede1e6f8SHighPoint Linux Team 	u32 req;
89ede1e6f8SHighPoint Linux Team 
9000f59701SHighPoint Linux Team 	while ((req = readl(&hba->u.itl.iop->outbound_queue)) !=
9100f59701SHighPoint Linux Team 						IOPMU_QUEUE_EMPTY) {
92ede1e6f8SHighPoint Linux Team 
93ede1e6f8SHighPoint Linux Team 		if (req & IOPMU_QUEUE_MASK_HOST_BITS)
9400f59701SHighPoint Linux Team 			hptiop_request_callback_itl(hba, req);
95ede1e6f8SHighPoint Linux Team 		else {
96ede1e6f8SHighPoint Linux Team 			struct hpt_iop_request_header __iomem * p;
97ede1e6f8SHighPoint Linux Team 
98ede1e6f8SHighPoint Linux Team 			p = (struct hpt_iop_request_header __iomem *)
9900f59701SHighPoint Linux Team 				((char __iomem *)hba->u.itl.iop + req);
100ede1e6f8SHighPoint Linux Team 
101ede1e6f8SHighPoint Linux Team 			if (readl(&p->flags) & IOP_REQUEST_FLAG_SYNC_REQUEST) {
102ede1e6f8SHighPoint Linux Team 				if (readl(&p->context))
10300f59701SHighPoint Linux Team 					hptiop_request_callback_itl(hba, req);
104ede1e6f8SHighPoint Linux Team 				else
105ede1e6f8SHighPoint Linux Team 					writel(1, &p->context);
106ede1e6f8SHighPoint Linux Team 			}
107ede1e6f8SHighPoint Linux Team 			else
10800f59701SHighPoint Linux Team 				hptiop_request_callback_itl(hba, req);
109ede1e6f8SHighPoint Linux Team 		}
110ede1e6f8SHighPoint Linux Team 	}
111ede1e6f8SHighPoint Linux Team }
112ede1e6f8SHighPoint Linux Team 
iop_intr_itl(struct hptiop_hba * hba)11300f59701SHighPoint Linux Team static int iop_intr_itl(struct hptiop_hba *hba)
114ede1e6f8SHighPoint Linux Team {
11500f59701SHighPoint Linux Team 	struct hpt_iopmu_itl __iomem *iop = hba->u.itl.iop;
1163bfc13c2SHighPoint Linux Team 	void __iomem *plx = hba->u.itl.plx;
117ede1e6f8SHighPoint Linux Team 	u32 status;
118ede1e6f8SHighPoint Linux Team 	int ret = 0;
119ede1e6f8SHighPoint Linux Team 
1203bfc13c2SHighPoint Linux Team 	if (plx && readl(plx + 0x11C5C) & 0xf)
1213bfc13c2SHighPoint Linux Team 		writel(1, plx + 0x11C60);
1223bfc13c2SHighPoint Linux Team 
123ede1e6f8SHighPoint Linux Team 	status = readl(&iop->outbound_intstatus);
124ede1e6f8SHighPoint Linux Team 
125ede1e6f8SHighPoint Linux Team 	if (status & IOPMU_OUTBOUND_INT_MSG0) {
126ede1e6f8SHighPoint Linux Team 		u32 msg = readl(&iop->outbound_msgaddr0);
12700f59701SHighPoint Linux Team 
128ede1e6f8SHighPoint Linux Team 		dprintk("received outbound msg %x\n", msg);
129ede1e6f8SHighPoint Linux Team 		writel(IOPMU_OUTBOUND_INT_MSG0, &iop->outbound_intstatus);
130ede1e6f8SHighPoint Linux Team 		hptiop_message_callback(hba, msg);
131ede1e6f8SHighPoint Linux Team 		ret = 1;
132ede1e6f8SHighPoint Linux Team 	}
133ede1e6f8SHighPoint Linux Team 
134ede1e6f8SHighPoint Linux Team 	if (status & IOPMU_OUTBOUND_INT_POSTQUEUE) {
13500f59701SHighPoint Linux Team 		hptiop_drain_outbound_queue_itl(hba);
136ede1e6f8SHighPoint Linux Team 		ret = 1;
137ede1e6f8SHighPoint Linux Team 	}
138ede1e6f8SHighPoint Linux Team 
139ede1e6f8SHighPoint Linux Team 	return ret;
140ede1e6f8SHighPoint Linux Team }
141ede1e6f8SHighPoint Linux Team 
mv_outbound_read(struct hpt_iopmu_mv __iomem * mu)14200f59701SHighPoint Linux Team static u64 mv_outbound_read(struct hpt_iopmu_mv __iomem *mu)
14300f59701SHighPoint Linux Team {
14400f59701SHighPoint Linux Team 	u32 outbound_tail = readl(&mu->outbound_tail);
14500f59701SHighPoint Linux Team 	u32 outbound_head = readl(&mu->outbound_head);
14600f59701SHighPoint Linux Team 
14700f59701SHighPoint Linux Team 	if (outbound_tail != outbound_head) {
14800f59701SHighPoint Linux Team 		u64 p;
14900f59701SHighPoint Linux Team 
15000f59701SHighPoint Linux Team 		memcpy_fromio(&p, &mu->outbound_q[mu->outbound_tail], 8);
15100f59701SHighPoint Linux Team 		outbound_tail++;
15200f59701SHighPoint Linux Team 
15300f59701SHighPoint Linux Team 		if (outbound_tail == MVIOP_QUEUE_LEN)
15400f59701SHighPoint Linux Team 			outbound_tail = 0;
15500f59701SHighPoint Linux Team 		writel(outbound_tail, &mu->outbound_tail);
15600f59701SHighPoint Linux Team 		return p;
15700f59701SHighPoint Linux Team 	} else
15800f59701SHighPoint Linux Team 		return 0;
15900f59701SHighPoint Linux Team }
16000f59701SHighPoint Linux Team 
mv_inbound_write(u64 p,struct hptiop_hba * hba)16100f59701SHighPoint Linux Team static void mv_inbound_write(u64 p, struct hptiop_hba *hba)
16200f59701SHighPoint Linux Team {
16300f59701SHighPoint Linux Team 	u32 inbound_head = readl(&hba->u.mv.mu->inbound_head);
16400f59701SHighPoint Linux Team 	u32 head = inbound_head + 1;
16500f59701SHighPoint Linux Team 
16600f59701SHighPoint Linux Team 	if (head == MVIOP_QUEUE_LEN)
16700f59701SHighPoint Linux Team 		head = 0;
16800f59701SHighPoint Linux Team 
16900f59701SHighPoint Linux Team 	memcpy_toio(&hba->u.mv.mu->inbound_q[inbound_head], &p, 8);
17000f59701SHighPoint Linux Team 	writel(head, &hba->u.mv.mu->inbound_head);
17100f59701SHighPoint Linux Team 	writel(MVIOP_MU_INBOUND_INT_POSTQUEUE,
17200f59701SHighPoint Linux Team 			&hba->u.mv.regs->inbound_doorbell);
17300f59701SHighPoint Linux Team }
17400f59701SHighPoint Linux Team 
hptiop_request_callback_mv(struct hptiop_hba * hba,u64 tag)17500f59701SHighPoint Linux Team static void hptiop_request_callback_mv(struct hptiop_hba *hba, u64 tag)
17600f59701SHighPoint Linux Team {
17700f59701SHighPoint Linux Team 	u32 req_type = (tag >> 5) & 0x7;
17800f59701SHighPoint Linux Team 	struct hpt_iop_request_scsi_command *req;
17900f59701SHighPoint Linux Team 
18000f59701SHighPoint Linux Team 	dprintk("hptiop_request_callback_mv: tag=%llx\n", tag);
18100f59701SHighPoint Linux Team 
18200f59701SHighPoint Linux Team 	BUG_ON((tag & MVIOP_MU_QUEUE_REQUEST_RETURN_CONTEXT) == 0);
18300f59701SHighPoint Linux Team 
18400f59701SHighPoint Linux Team 	switch (req_type) {
18500f59701SHighPoint Linux Team 	case IOP_REQUEST_TYPE_GET_CONFIG:
18600f59701SHighPoint Linux Team 	case IOP_REQUEST_TYPE_SET_CONFIG:
18700f59701SHighPoint Linux Team 		hba->msg_done = 1;
18800f59701SHighPoint Linux Team 		break;
18900f59701SHighPoint Linux Team 
19000f59701SHighPoint Linux Team 	case IOP_REQUEST_TYPE_SCSI_COMMAND:
19100f59701SHighPoint Linux Team 		req = hba->reqs[tag >> 8].req_virt;
19200f59701SHighPoint Linux Team 		if (likely(tag & MVIOP_MU_QUEUE_REQUEST_RESULT_BIT))
19300f59701SHighPoint Linux Team 			req->header.result = cpu_to_le32(IOP_RESULT_SUCCESS);
19400f59701SHighPoint Linux Team 
19500f59701SHighPoint Linux Team 		hptiop_finish_scsi_req(hba, tag>>8, req);
19600f59701SHighPoint Linux Team 		break;
19700f59701SHighPoint Linux Team 
19800f59701SHighPoint Linux Team 	default:
19900f59701SHighPoint Linux Team 		break;
20000f59701SHighPoint Linux Team 	}
20100f59701SHighPoint Linux Team }
20200f59701SHighPoint Linux Team 
iop_intr_mv(struct hptiop_hba * hba)20300f59701SHighPoint Linux Team static int iop_intr_mv(struct hptiop_hba *hba)
20400f59701SHighPoint Linux Team {
20500f59701SHighPoint Linux Team 	u32 status;
20600f59701SHighPoint Linux Team 	int ret = 0;
20700f59701SHighPoint Linux Team 
20800f59701SHighPoint Linux Team 	status = readl(&hba->u.mv.regs->outbound_doorbell);
20900f59701SHighPoint Linux Team 	writel(~status, &hba->u.mv.regs->outbound_doorbell);
21000f59701SHighPoint Linux Team 
21100f59701SHighPoint Linux Team 	if (status & MVIOP_MU_OUTBOUND_INT_MSG) {
21200f59701SHighPoint Linux Team 		u32 msg;
21300f59701SHighPoint Linux Team 		msg = readl(&hba->u.mv.mu->outbound_msg);
21400f59701SHighPoint Linux Team 		dprintk("received outbound msg %x\n", msg);
21500f59701SHighPoint Linux Team 		hptiop_message_callback(hba, msg);
21600f59701SHighPoint Linux Team 		ret = 1;
21700f59701SHighPoint Linux Team 	}
21800f59701SHighPoint Linux Team 
21900f59701SHighPoint Linux Team 	if (status & MVIOP_MU_OUTBOUND_INT_POSTQUEUE) {
22000f59701SHighPoint Linux Team 		u64 tag;
22100f59701SHighPoint Linux Team 
22200f59701SHighPoint Linux Team 		while ((tag = mv_outbound_read(hba->u.mv.mu)))
22300f59701SHighPoint Linux Team 			hptiop_request_callback_mv(hba, tag);
22400f59701SHighPoint Linux Team 		ret = 1;
22500f59701SHighPoint Linux Team 	}
22600f59701SHighPoint Linux Team 
22700f59701SHighPoint Linux Team 	return ret;
22800f59701SHighPoint Linux Team }
22900f59701SHighPoint Linux Team 
hptiop_request_callback_mvfrey(struct hptiop_hba * hba,u32 _tag)230286aa031SHighPoint Linux Team static void hptiop_request_callback_mvfrey(struct hptiop_hba *hba, u32 _tag)
231286aa031SHighPoint Linux Team {
232286aa031SHighPoint Linux Team 	u32 req_type = _tag & 0xf;
233286aa031SHighPoint Linux Team 	struct hpt_iop_request_scsi_command *req;
234286aa031SHighPoint Linux Team 
235286aa031SHighPoint Linux Team 	switch (req_type) {
236286aa031SHighPoint Linux Team 	case IOP_REQUEST_TYPE_GET_CONFIG:
237286aa031SHighPoint Linux Team 	case IOP_REQUEST_TYPE_SET_CONFIG:
238286aa031SHighPoint Linux Team 		hba->msg_done = 1;
239286aa031SHighPoint Linux Team 		break;
240286aa031SHighPoint Linux Team 
241286aa031SHighPoint Linux Team 	case IOP_REQUEST_TYPE_SCSI_COMMAND:
242286aa031SHighPoint Linux Team 		req = hba->reqs[(_tag >> 4) & 0xff].req_virt;
243286aa031SHighPoint Linux Team 		if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT))
244286aa031SHighPoint Linux Team 			req->header.result = IOP_RESULT_SUCCESS;
245286aa031SHighPoint Linux Team 		hptiop_finish_scsi_req(hba, (_tag >> 4) & 0xff, req);
246286aa031SHighPoint Linux Team 		break;
247286aa031SHighPoint Linux Team 
248286aa031SHighPoint Linux Team 	default:
249286aa031SHighPoint Linux Team 		break;
250286aa031SHighPoint Linux Team 	}
251286aa031SHighPoint Linux Team }
252286aa031SHighPoint Linux Team 
iop_intr_mvfrey(struct hptiop_hba * hba)253286aa031SHighPoint Linux Team static int iop_intr_mvfrey(struct hptiop_hba *hba)
254286aa031SHighPoint Linux Team {
255286aa031SHighPoint Linux Team 	u32 _tag, status, cptr, cur_rptr;
256286aa031SHighPoint Linux Team 	int ret = 0;
257286aa031SHighPoint Linux Team 
258286aa031SHighPoint Linux Team 	if (hba->initialized)
259286aa031SHighPoint Linux Team 		writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
260286aa031SHighPoint Linux Team 
261286aa031SHighPoint Linux Team 	status = readl(&(hba->u.mvfrey.mu->f0_doorbell));
262286aa031SHighPoint Linux Team 	if (status) {
263286aa031SHighPoint Linux Team 		writel(status, &(hba->u.mvfrey.mu->f0_doorbell));
264286aa031SHighPoint Linux Team 		if (status & CPU_TO_F0_DRBL_MSG_BIT) {
265286aa031SHighPoint Linux Team 			u32 msg = readl(&(hba->u.mvfrey.mu->cpu_to_f0_msg_a));
266286aa031SHighPoint Linux Team 			dprintk("received outbound msg %x\n", msg);
267286aa031SHighPoint Linux Team 			hptiop_message_callback(hba, msg);
268286aa031SHighPoint Linux Team 		}
269286aa031SHighPoint Linux Team 		ret = 1;
270286aa031SHighPoint Linux Team 	}
271286aa031SHighPoint Linux Team 
272286aa031SHighPoint Linux Team 	status = readl(&(hba->u.mvfrey.mu->isr_cause));
273286aa031SHighPoint Linux Team 	if (status) {
274286aa031SHighPoint Linux Team 		writel(status, &(hba->u.mvfrey.mu->isr_cause));
275286aa031SHighPoint Linux Team 		do {
276286aa031SHighPoint Linux Team 			cptr = *hba->u.mvfrey.outlist_cptr & 0xff;
277286aa031SHighPoint Linux Team 			cur_rptr = hba->u.mvfrey.outlist_rptr;
278286aa031SHighPoint Linux Team 			while (cur_rptr != cptr) {
279286aa031SHighPoint Linux Team 				cur_rptr++;
280286aa031SHighPoint Linux Team 				if (cur_rptr ==	hba->u.mvfrey.list_count)
281286aa031SHighPoint Linux Team 					cur_rptr = 0;
282286aa031SHighPoint Linux Team 
283286aa031SHighPoint Linux Team 				_tag = hba->u.mvfrey.outlist[cur_rptr].val;
284286aa031SHighPoint Linux Team 				BUG_ON(!(_tag & IOPMU_QUEUE_MASK_HOST_BITS));
285286aa031SHighPoint Linux Team 				hptiop_request_callback_mvfrey(hba, _tag);
286286aa031SHighPoint Linux Team 				ret = 1;
287286aa031SHighPoint Linux Team 			}
288286aa031SHighPoint Linux Team 			hba->u.mvfrey.outlist_rptr = cur_rptr;
289286aa031SHighPoint Linux Team 		} while (cptr != (*hba->u.mvfrey.outlist_cptr & 0xff));
290286aa031SHighPoint Linux Team 	}
291286aa031SHighPoint Linux Team 
292286aa031SHighPoint Linux Team 	if (hba->initialized)
293286aa031SHighPoint Linux Team 		writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
294286aa031SHighPoint Linux Team 
295286aa031SHighPoint Linux Team 	return ret;
296286aa031SHighPoint Linux Team }
297286aa031SHighPoint Linux Team 
iop_send_sync_request_itl(struct hptiop_hba * hba,void __iomem * _req,u32 millisec)29800f59701SHighPoint Linux Team static int iop_send_sync_request_itl(struct hptiop_hba *hba,
299ede1e6f8SHighPoint Linux Team 					void __iomem *_req, u32 millisec)
300ede1e6f8SHighPoint Linux Team {
301ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_header __iomem *req = _req;
302ede1e6f8SHighPoint Linux Team 	u32 i;
303ede1e6f8SHighPoint Linux Team 
30400f59701SHighPoint Linux Team 	writel(readl(&req->flags) | IOP_REQUEST_FLAG_SYNC_REQUEST, &req->flags);
305ede1e6f8SHighPoint Linux Team 	writel(0, &req->context);
30600f59701SHighPoint Linux Team 	writel((unsigned long)req - (unsigned long)hba->u.itl.iop,
30700f59701SHighPoint Linux Team 			&hba->u.itl.iop->inbound_queue);
30800f59701SHighPoint Linux Team 	readl(&hba->u.itl.iop->outbound_intstatus);
309ede1e6f8SHighPoint Linux Team 
310ede1e6f8SHighPoint Linux Team 	for (i = 0; i < millisec; i++) {
31100f59701SHighPoint Linux Team 		iop_intr_itl(hba);
312ede1e6f8SHighPoint Linux Team 		if (readl(&req->context))
313ede1e6f8SHighPoint Linux Team 			return 0;
314ede1e6f8SHighPoint Linux Team 		msleep(1);
315ede1e6f8SHighPoint Linux Team 	}
316ede1e6f8SHighPoint Linux Team 
317ede1e6f8SHighPoint Linux Team 	return -1;
318ede1e6f8SHighPoint Linux Team }
319ede1e6f8SHighPoint Linux Team 
iop_send_sync_request_mv(struct hptiop_hba * hba,u32 size_bits,u32 millisec)32000f59701SHighPoint Linux Team static int iop_send_sync_request_mv(struct hptiop_hba *hba,
32100f59701SHighPoint Linux Team 					u32 size_bits, u32 millisec)
32200f59701SHighPoint Linux Team {
32300f59701SHighPoint Linux Team 	struct hpt_iop_request_header *reqhdr = hba->u.mv.internal_req;
32400f59701SHighPoint Linux Team 	u32 i;
32500f59701SHighPoint Linux Team 
32600f59701SHighPoint Linux Team 	hba->msg_done = 0;
32700f59701SHighPoint Linux Team 	reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST);
32800f59701SHighPoint Linux Team 	mv_inbound_write(hba->u.mv.internal_req_phy |
32900f59701SHighPoint Linux Team 			MVIOP_MU_QUEUE_ADDR_HOST_BIT | size_bits, hba);
33000f59701SHighPoint Linux Team 
33100f59701SHighPoint Linux Team 	for (i = 0; i < millisec; i++) {
33200f59701SHighPoint Linux Team 		iop_intr_mv(hba);
33300f59701SHighPoint Linux Team 		if (hba->msg_done)
33400f59701SHighPoint Linux Team 			return 0;
33500f59701SHighPoint Linux Team 		msleep(1);
33600f59701SHighPoint Linux Team 	}
33700f59701SHighPoint Linux Team 	return -1;
33800f59701SHighPoint Linux Team }
33900f59701SHighPoint Linux Team 
iop_send_sync_request_mvfrey(struct hptiop_hba * hba,u32 size_bits,u32 millisec)340286aa031SHighPoint Linux Team static int iop_send_sync_request_mvfrey(struct hptiop_hba *hba,
341286aa031SHighPoint Linux Team 					u32 size_bits, u32 millisec)
342286aa031SHighPoint Linux Team {
343286aa031SHighPoint Linux Team 	struct hpt_iop_request_header *reqhdr =
344286aa031SHighPoint Linux Team 		hba->u.mvfrey.internal_req.req_virt;
345286aa031SHighPoint Linux Team 	u32 i;
346286aa031SHighPoint Linux Team 
347286aa031SHighPoint Linux Team 	hba->msg_done = 0;
348286aa031SHighPoint Linux Team 	reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST);
349286aa031SHighPoint Linux Team 	hba->ops->post_req(hba, &(hba->u.mvfrey.internal_req));
350286aa031SHighPoint Linux Team 
351286aa031SHighPoint Linux Team 	for (i = 0; i < millisec; i++) {
352286aa031SHighPoint Linux Team 		iop_intr_mvfrey(hba);
353286aa031SHighPoint Linux Team 		if (hba->msg_done)
354286aa031SHighPoint Linux Team 			break;
355286aa031SHighPoint Linux Team 		msleep(1);
356286aa031SHighPoint Linux Team 	}
357286aa031SHighPoint Linux Team 	return hba->msg_done ? 0 : -1;
358286aa031SHighPoint Linux Team }
359286aa031SHighPoint Linux Team 
hptiop_post_msg_itl(struct hptiop_hba * hba,u32 msg)36000f59701SHighPoint Linux Team static void hptiop_post_msg_itl(struct hptiop_hba *hba, u32 msg)
36100f59701SHighPoint Linux Team {
36200f59701SHighPoint Linux Team 	writel(msg, &hba->u.itl.iop->inbound_msgaddr0);
36300f59701SHighPoint Linux Team 	readl(&hba->u.itl.iop->outbound_intstatus);
36400f59701SHighPoint Linux Team }
36500f59701SHighPoint Linux Team 
hptiop_post_msg_mv(struct hptiop_hba * hba,u32 msg)36600f59701SHighPoint Linux Team static void hptiop_post_msg_mv(struct hptiop_hba *hba, u32 msg)
36700f59701SHighPoint Linux Team {
36800f59701SHighPoint Linux Team 	writel(msg, &hba->u.mv.mu->inbound_msg);
36900f59701SHighPoint Linux Team 	writel(MVIOP_MU_INBOUND_INT_MSG, &hba->u.mv.regs->inbound_doorbell);
37000f59701SHighPoint Linux Team 	readl(&hba->u.mv.regs->inbound_doorbell);
37100f59701SHighPoint Linux Team }
37200f59701SHighPoint Linux Team 
hptiop_post_msg_mvfrey(struct hptiop_hba * hba,u32 msg)373286aa031SHighPoint Linux Team static void hptiop_post_msg_mvfrey(struct hptiop_hba *hba, u32 msg)
374286aa031SHighPoint Linux Team {
375286aa031SHighPoint Linux Team 	writel(msg, &(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
376286aa031SHighPoint Linux Team 	readl(&(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
377286aa031SHighPoint Linux Team }
378286aa031SHighPoint Linux Team 
iop_send_sync_msg(struct hptiop_hba * hba,u32 msg,u32 millisec)379ede1e6f8SHighPoint Linux Team static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec)
380ede1e6f8SHighPoint Linux Team {
381ede1e6f8SHighPoint Linux Team 	u32 i;
382ede1e6f8SHighPoint Linux Team 
383ede1e6f8SHighPoint Linux Team 	hba->msg_done = 0;
384286aa031SHighPoint Linux Team 	hba->ops->disable_intr(hba);
38500f59701SHighPoint Linux Team 	hba->ops->post_msg(hba, msg);
386ede1e6f8SHighPoint Linux Team 
387ede1e6f8SHighPoint Linux Team 	for (i = 0; i < millisec; i++) {
388ede1e6f8SHighPoint Linux Team 		spin_lock_irq(hba->host->host_lock);
38900f59701SHighPoint Linux Team 		hba->ops->iop_intr(hba);
390ede1e6f8SHighPoint Linux Team 		spin_unlock_irq(hba->host->host_lock);
391ede1e6f8SHighPoint Linux Team 		if (hba->msg_done)
392ede1e6f8SHighPoint Linux Team 			break;
393ede1e6f8SHighPoint Linux Team 		msleep(1);
394ede1e6f8SHighPoint Linux Team 	}
395ede1e6f8SHighPoint Linux Team 
396286aa031SHighPoint Linux Team 	hba->ops->enable_intr(hba);
397ede1e6f8SHighPoint Linux Team 	return hba->msg_done? 0 : -1;
398ede1e6f8SHighPoint Linux Team }
399ede1e6f8SHighPoint Linux Team 
iop_get_config_itl(struct hptiop_hba * hba,struct hpt_iop_request_get_config * config)40000f59701SHighPoint Linux Team static int iop_get_config_itl(struct hptiop_hba *hba,
401ede1e6f8SHighPoint Linux Team 				struct hpt_iop_request_get_config *config)
402ede1e6f8SHighPoint Linux Team {
403ede1e6f8SHighPoint Linux Team 	u32 req32;
404ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_get_config __iomem *req;
405ede1e6f8SHighPoint Linux Team 
40600f59701SHighPoint Linux Team 	req32 = readl(&hba->u.itl.iop->inbound_queue);
407ede1e6f8SHighPoint Linux Team 	if (req32 == IOPMU_QUEUE_EMPTY)
408ede1e6f8SHighPoint Linux Team 		return -1;
409ede1e6f8SHighPoint Linux Team 
410ede1e6f8SHighPoint Linux Team 	req = (struct hpt_iop_request_get_config __iomem *)
41100f59701SHighPoint Linux Team 			((unsigned long)hba->u.itl.iop + req32);
412ede1e6f8SHighPoint Linux Team 
413ede1e6f8SHighPoint Linux Team 	writel(0, &req->header.flags);
414ede1e6f8SHighPoint Linux Team 	writel(IOP_REQUEST_TYPE_GET_CONFIG, &req->header.type);
415ede1e6f8SHighPoint Linux Team 	writel(sizeof(struct hpt_iop_request_get_config), &req->header.size);
416ede1e6f8SHighPoint Linux Team 	writel(IOP_RESULT_PENDING, &req->header.result);
417ede1e6f8SHighPoint Linux Team 
41800f59701SHighPoint Linux Team 	if (iop_send_sync_request_itl(hba, req, 20000)) {
419ede1e6f8SHighPoint Linux Team 		dprintk("Get config send cmd failed\n");
420ede1e6f8SHighPoint Linux Team 		return -1;
421ede1e6f8SHighPoint Linux Team 	}
422ede1e6f8SHighPoint Linux Team 
423ede1e6f8SHighPoint Linux Team 	memcpy_fromio(config, req, sizeof(*config));
42400f59701SHighPoint Linux Team 	writel(req32, &hba->u.itl.iop->outbound_queue);
425ede1e6f8SHighPoint Linux Team 	return 0;
426ede1e6f8SHighPoint Linux Team }
427ede1e6f8SHighPoint Linux Team 
iop_get_config_mv(struct hptiop_hba * hba,struct hpt_iop_request_get_config * config)42800f59701SHighPoint Linux Team static int iop_get_config_mv(struct hptiop_hba *hba,
42900f59701SHighPoint Linux Team 				struct hpt_iop_request_get_config *config)
43000f59701SHighPoint Linux Team {
43100f59701SHighPoint Linux Team 	struct hpt_iop_request_get_config *req = hba->u.mv.internal_req;
43200f59701SHighPoint Linux Team 
43300f59701SHighPoint Linux Team 	req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
43400f59701SHighPoint Linux Team 	req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_GET_CONFIG);
43500f59701SHighPoint Linux Team 	req->header.size =
43600f59701SHighPoint Linux Team 		cpu_to_le32(sizeof(struct hpt_iop_request_get_config));
43700f59701SHighPoint Linux Team 	req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
438f6b196a2SJames Bottomley 	req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_GET_CONFIG<<5);
439f6b196a2SJames Bottomley 	req->header.context_hi32 = 0;
44000f59701SHighPoint Linux Team 
44100f59701SHighPoint Linux Team 	if (iop_send_sync_request_mv(hba, 0, 20000)) {
44200f59701SHighPoint Linux Team 		dprintk("Get config send cmd failed\n");
44300f59701SHighPoint Linux Team 		return -1;
44400f59701SHighPoint Linux Team 	}
44500f59701SHighPoint Linux Team 
44600f59701SHighPoint Linux Team 	memcpy(config, req, sizeof(struct hpt_iop_request_get_config));
44700f59701SHighPoint Linux Team 	return 0;
44800f59701SHighPoint Linux Team }
44900f59701SHighPoint Linux Team 
iop_get_config_mvfrey(struct hptiop_hba * hba,struct hpt_iop_request_get_config * config)450286aa031SHighPoint Linux Team static int iop_get_config_mvfrey(struct hptiop_hba *hba,
451286aa031SHighPoint Linux Team 				struct hpt_iop_request_get_config *config)
452286aa031SHighPoint Linux Team {
453286aa031SHighPoint Linux Team 	struct hpt_iop_request_get_config *info = hba->u.mvfrey.config;
454286aa031SHighPoint Linux Team 
455286aa031SHighPoint Linux Team 	if (info->header.size != sizeof(struct hpt_iop_request_get_config) ||
456286aa031SHighPoint Linux Team 			info->header.type != IOP_REQUEST_TYPE_GET_CONFIG)
457286aa031SHighPoint Linux Team 		return -1;
458286aa031SHighPoint Linux Team 
459286aa031SHighPoint Linux Team 	config->interface_version = info->interface_version;
460286aa031SHighPoint Linux Team 	config->firmware_version = info->firmware_version;
461286aa031SHighPoint Linux Team 	config->max_requests = info->max_requests;
462286aa031SHighPoint Linux Team 	config->request_size = info->request_size;
463286aa031SHighPoint Linux Team 	config->max_sg_count = info->max_sg_count;
464286aa031SHighPoint Linux Team 	config->data_transfer_length = info->data_transfer_length;
465286aa031SHighPoint Linux Team 	config->alignment_mask = info->alignment_mask;
466286aa031SHighPoint Linux Team 	config->max_devices = info->max_devices;
467286aa031SHighPoint Linux Team 	config->sdram_size = info->sdram_size;
468286aa031SHighPoint Linux Team 
469286aa031SHighPoint Linux Team 	return 0;
470286aa031SHighPoint Linux Team }
471286aa031SHighPoint Linux Team 
iop_set_config_itl(struct hptiop_hba * hba,struct hpt_iop_request_set_config * config)47200f59701SHighPoint Linux Team static int iop_set_config_itl(struct hptiop_hba *hba,
473ede1e6f8SHighPoint Linux Team 				struct hpt_iop_request_set_config *config)
474ede1e6f8SHighPoint Linux Team {
475ede1e6f8SHighPoint Linux Team 	u32 req32;
476ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_set_config __iomem *req;
477ede1e6f8SHighPoint Linux Team 
47800f59701SHighPoint Linux Team 	req32 = readl(&hba->u.itl.iop->inbound_queue);
479ede1e6f8SHighPoint Linux Team 	if (req32 == IOPMU_QUEUE_EMPTY)
480ede1e6f8SHighPoint Linux Team 		return -1;
481ede1e6f8SHighPoint Linux Team 
482ede1e6f8SHighPoint Linux Team 	req = (struct hpt_iop_request_set_config __iomem *)
48300f59701SHighPoint Linux Team 			((unsigned long)hba->u.itl.iop + req32);
484ede1e6f8SHighPoint Linux Team 
485ede1e6f8SHighPoint Linux Team 	memcpy_toio((u8 __iomem *)req + sizeof(struct hpt_iop_request_header),
486ede1e6f8SHighPoint Linux Team 		(u8 *)config + sizeof(struct hpt_iop_request_header),
487ede1e6f8SHighPoint Linux Team 		sizeof(struct hpt_iop_request_set_config) -
488ede1e6f8SHighPoint Linux Team 			sizeof(struct hpt_iop_request_header));
489ede1e6f8SHighPoint Linux Team 
490ede1e6f8SHighPoint Linux Team 	writel(0, &req->header.flags);
491ede1e6f8SHighPoint Linux Team 	writel(IOP_REQUEST_TYPE_SET_CONFIG, &req->header.type);
492ede1e6f8SHighPoint Linux Team 	writel(sizeof(struct hpt_iop_request_set_config), &req->header.size);
493ede1e6f8SHighPoint Linux Team 	writel(IOP_RESULT_PENDING, &req->header.result);
494ede1e6f8SHighPoint Linux Team 
49500f59701SHighPoint Linux Team 	if (iop_send_sync_request_itl(hba, req, 20000)) {
496ede1e6f8SHighPoint Linux Team 		dprintk("Set config send cmd failed\n");
497ede1e6f8SHighPoint Linux Team 		return -1;
498ede1e6f8SHighPoint Linux Team 	}
499ede1e6f8SHighPoint Linux Team 
50000f59701SHighPoint Linux Team 	writel(req32, &hba->u.itl.iop->outbound_queue);
501ede1e6f8SHighPoint Linux Team 	return 0;
502ede1e6f8SHighPoint Linux Team }
503ede1e6f8SHighPoint Linux Team 
iop_set_config_mv(struct hptiop_hba * hba,struct hpt_iop_request_set_config * config)50400f59701SHighPoint Linux Team static int iop_set_config_mv(struct hptiop_hba *hba,
50500f59701SHighPoint Linux Team 				struct hpt_iop_request_set_config *config)
50600f59701SHighPoint Linux Team {
50700f59701SHighPoint Linux Team 	struct hpt_iop_request_set_config *req = hba->u.mv.internal_req;
50800f59701SHighPoint Linux Team 
50900f59701SHighPoint Linux Team 	memcpy(req, config, sizeof(struct hpt_iop_request_set_config));
51000f59701SHighPoint Linux Team 	req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
51100f59701SHighPoint Linux Team 	req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG);
51200f59701SHighPoint Linux Team 	req->header.size =
51300f59701SHighPoint Linux Team 		cpu_to_le32(sizeof(struct hpt_iop_request_set_config));
51400f59701SHighPoint Linux Team 	req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
515f6b196a2SJames Bottomley 	req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG<<5);
516f6b196a2SJames Bottomley 	req->header.context_hi32 = 0;
51700f59701SHighPoint Linux Team 
51800f59701SHighPoint Linux Team 	if (iop_send_sync_request_mv(hba, 0, 20000)) {
51900f59701SHighPoint Linux Team 		dprintk("Set config send cmd failed\n");
52000f59701SHighPoint Linux Team 		return -1;
52100f59701SHighPoint Linux Team 	}
52200f59701SHighPoint Linux Team 
52300f59701SHighPoint Linux Team 	return 0;
52400f59701SHighPoint Linux Team }
52500f59701SHighPoint Linux Team 
iop_set_config_mvfrey(struct hptiop_hba * hba,struct hpt_iop_request_set_config * config)526286aa031SHighPoint Linux Team static int iop_set_config_mvfrey(struct hptiop_hba *hba,
527286aa031SHighPoint Linux Team 				struct hpt_iop_request_set_config *config)
528286aa031SHighPoint Linux Team {
529286aa031SHighPoint Linux Team 	struct hpt_iop_request_set_config *req =
530286aa031SHighPoint Linux Team 		hba->u.mvfrey.internal_req.req_virt;
531286aa031SHighPoint Linux Team 
532286aa031SHighPoint Linux Team 	memcpy(req, config, sizeof(struct hpt_iop_request_set_config));
533286aa031SHighPoint Linux Team 	req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
534286aa031SHighPoint Linux Team 	req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG);
535286aa031SHighPoint Linux Team 	req->header.size =
536286aa031SHighPoint Linux Team 		cpu_to_le32(sizeof(struct hpt_iop_request_set_config));
537286aa031SHighPoint Linux Team 	req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
538286aa031SHighPoint Linux Team 	req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG<<5);
539286aa031SHighPoint Linux Team 	req->header.context_hi32 = 0;
540286aa031SHighPoint Linux Team 
541286aa031SHighPoint Linux Team 	if (iop_send_sync_request_mvfrey(hba, 0, 20000)) {
542286aa031SHighPoint Linux Team 		dprintk("Set config send cmd failed\n");
543286aa031SHighPoint Linux Team 		return -1;
544286aa031SHighPoint Linux Team 	}
545286aa031SHighPoint Linux Team 
546286aa031SHighPoint Linux Team 	return 0;
547286aa031SHighPoint Linux Team }
548286aa031SHighPoint Linux Team 
hptiop_enable_intr_itl(struct hptiop_hba * hba)54900f59701SHighPoint Linux Team static void hptiop_enable_intr_itl(struct hptiop_hba *hba)
55000f59701SHighPoint Linux Team {
55100f59701SHighPoint Linux Team 	writel(~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0),
55200f59701SHighPoint Linux Team 		&hba->u.itl.iop->outbound_intmask);
55300f59701SHighPoint Linux Team }
55400f59701SHighPoint Linux Team 
hptiop_enable_intr_mv(struct hptiop_hba * hba)55500f59701SHighPoint Linux Team static void hptiop_enable_intr_mv(struct hptiop_hba *hba)
55600f59701SHighPoint Linux Team {
55700f59701SHighPoint Linux Team 	writel(MVIOP_MU_OUTBOUND_INT_POSTQUEUE | MVIOP_MU_OUTBOUND_INT_MSG,
55800f59701SHighPoint Linux Team 		&hba->u.mv.regs->outbound_intmask);
55900f59701SHighPoint Linux Team }
56000f59701SHighPoint Linux Team 
hptiop_enable_intr_mvfrey(struct hptiop_hba * hba)561286aa031SHighPoint Linux Team static void hptiop_enable_intr_mvfrey(struct hptiop_hba *hba)
562286aa031SHighPoint Linux Team {
563286aa031SHighPoint Linux Team 	writel(CPU_TO_F0_DRBL_MSG_BIT, &(hba->u.mvfrey.mu->f0_doorbell_enable));
564286aa031SHighPoint Linux Team 	writel(0x1, &(hba->u.mvfrey.mu->isr_enable));
565286aa031SHighPoint Linux Team 	writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
566286aa031SHighPoint Linux Team }
567286aa031SHighPoint Linux Team 
hptiop_initialize_iop(struct hptiop_hba * hba)568ede1e6f8SHighPoint Linux Team static int hptiop_initialize_iop(struct hptiop_hba *hba)
569ede1e6f8SHighPoint Linux Team {
570ede1e6f8SHighPoint Linux Team 	/* enable interrupts */
57100f59701SHighPoint Linux Team 	hba->ops->enable_intr(hba);
572ede1e6f8SHighPoint Linux Team 
573ede1e6f8SHighPoint Linux Team 	hba->initialized = 1;
574ede1e6f8SHighPoint Linux Team 
575ede1e6f8SHighPoint Linux Team 	/* start background tasks */
576ede1e6f8SHighPoint Linux Team 	if (iop_send_sync_msg(hba,
577ede1e6f8SHighPoint Linux Team 			IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) {
578ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: fail to start background task\n",
579ede1e6f8SHighPoint Linux Team 			hba->host->host_no);
580ede1e6f8SHighPoint Linux Team 		return -1;
581ede1e6f8SHighPoint Linux Team 	}
582ede1e6f8SHighPoint Linux Team 	return 0;
583ede1e6f8SHighPoint Linux Team }
584ede1e6f8SHighPoint Linux Team 
hptiop_map_pci_bar(struct hptiop_hba * hba,int index)58500f59701SHighPoint Linux Team static void __iomem *hptiop_map_pci_bar(struct hptiop_hba *hba, int index)
586ede1e6f8SHighPoint Linux Team {
587ede1e6f8SHighPoint Linux Team 	u32 mem_base_phy, length;
588ede1e6f8SHighPoint Linux Team 	void __iomem *mem_base_virt;
58900f59701SHighPoint Linux Team 
590ede1e6f8SHighPoint Linux Team 	struct pci_dev *pcidev = hba->pcidev;
591ede1e6f8SHighPoint Linux Team 
59200f59701SHighPoint Linux Team 
59300f59701SHighPoint Linux Team 	if (!(pci_resource_flags(pcidev, index) & IORESOURCE_MEM)) {
594ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: pci resource invalid\n",
595ede1e6f8SHighPoint Linux Team 				hba->host->host_no);
5969bcf0910SHarvey Harrison 		return NULL;
597ede1e6f8SHighPoint Linux Team 	}
598ede1e6f8SHighPoint Linux Team 
59900f59701SHighPoint Linux Team 	mem_base_phy = pci_resource_start(pcidev, index);
60000f59701SHighPoint Linux Team 	length = pci_resource_len(pcidev, index);
601ede1e6f8SHighPoint Linux Team 	mem_base_virt = ioremap(mem_base_phy, length);
602ede1e6f8SHighPoint Linux Team 
603ede1e6f8SHighPoint Linux Team 	if (!mem_base_virt) {
604ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: Fail to ioremap memory space\n",
605ede1e6f8SHighPoint Linux Team 				hba->host->host_no);
6069bcf0910SHarvey Harrison 		return NULL;
60700f59701SHighPoint Linux Team 	}
60800f59701SHighPoint Linux Team 	return mem_base_virt;
60900f59701SHighPoint Linux Team }
61000f59701SHighPoint Linux Team 
hptiop_map_pci_bar_itl(struct hptiop_hba * hba)61100f59701SHighPoint Linux Team static int hptiop_map_pci_bar_itl(struct hptiop_hba *hba)
61200f59701SHighPoint Linux Team {
6133bfc13c2SHighPoint Linux Team 	struct pci_dev *pcidev = hba->pcidev;
61400f59701SHighPoint Linux Team 	hba->u.itl.iop = hptiop_map_pci_bar(hba, 0);
6153bfc13c2SHighPoint Linux Team 	if (hba->u.itl.iop == NULL)
616ede1e6f8SHighPoint Linux Team 		return -1;
6173bfc13c2SHighPoint Linux Team 	if ((pcidev->device & 0xff00) == 0x4400) {
6183bfc13c2SHighPoint Linux Team 		hba->u.itl.plx = hba->u.itl.iop;
6193bfc13c2SHighPoint Linux Team 		hba->u.itl.iop = hptiop_map_pci_bar(hba, 2);
6203bfc13c2SHighPoint Linux Team 		if (hba->u.itl.iop == NULL) {
6213bfc13c2SHighPoint Linux Team 			iounmap(hba->u.itl.plx);
6223bfc13c2SHighPoint Linux Team 			return -1;
6233bfc13c2SHighPoint Linux Team 		}
6243bfc13c2SHighPoint Linux Team 	}
6253bfc13c2SHighPoint Linux Team 	return 0;
626ede1e6f8SHighPoint Linux Team }
627ede1e6f8SHighPoint Linux Team 
hptiop_unmap_pci_bar_itl(struct hptiop_hba * hba)62800f59701SHighPoint Linux Team static void hptiop_unmap_pci_bar_itl(struct hptiop_hba *hba)
62900f59701SHighPoint Linux Team {
6303bfc13c2SHighPoint Linux Team 	if (hba->u.itl.plx)
6313bfc13c2SHighPoint Linux Team 		iounmap(hba->u.itl.plx);
63200f59701SHighPoint Linux Team 	iounmap(hba->u.itl.iop);
63300f59701SHighPoint Linux Team }
63400f59701SHighPoint Linux Team 
hptiop_map_pci_bar_mv(struct hptiop_hba * hba)63500f59701SHighPoint Linux Team static int hptiop_map_pci_bar_mv(struct hptiop_hba *hba)
63600f59701SHighPoint Linux Team {
63700f59701SHighPoint Linux Team 	hba->u.mv.regs = hptiop_map_pci_bar(hba, 0);
6389bcf0910SHarvey Harrison 	if (hba->u.mv.regs == NULL)
63900f59701SHighPoint Linux Team 		return -1;
64000f59701SHighPoint Linux Team 
64100f59701SHighPoint Linux Team 	hba->u.mv.mu = hptiop_map_pci_bar(hba, 2);
6429bcf0910SHarvey Harrison 	if (hba->u.mv.mu == NULL) {
64300f59701SHighPoint Linux Team 		iounmap(hba->u.mv.regs);
64400f59701SHighPoint Linux Team 		return -1;
64500f59701SHighPoint Linux Team 	}
64600f59701SHighPoint Linux Team 
647ede1e6f8SHighPoint Linux Team 	return 0;
648ede1e6f8SHighPoint Linux Team }
649ede1e6f8SHighPoint Linux Team 
hptiop_map_pci_bar_mvfrey(struct hptiop_hba * hba)650286aa031SHighPoint Linux Team static int hptiop_map_pci_bar_mvfrey(struct hptiop_hba *hba)
651286aa031SHighPoint Linux Team {
652286aa031SHighPoint Linux Team 	hba->u.mvfrey.config = hptiop_map_pci_bar(hba, 0);
653286aa031SHighPoint Linux Team 	if (hba->u.mvfrey.config == NULL)
654286aa031SHighPoint Linux Team 		return -1;
655286aa031SHighPoint Linux Team 
656286aa031SHighPoint Linux Team 	hba->u.mvfrey.mu = hptiop_map_pci_bar(hba, 2);
657286aa031SHighPoint Linux Team 	if (hba->u.mvfrey.mu == NULL) {
658286aa031SHighPoint Linux Team 		iounmap(hba->u.mvfrey.config);
659286aa031SHighPoint Linux Team 		return -1;
660286aa031SHighPoint Linux Team 	}
661286aa031SHighPoint Linux Team 
662286aa031SHighPoint Linux Team 	return 0;
663286aa031SHighPoint Linux Team }
664286aa031SHighPoint Linux Team 
hptiop_unmap_pci_bar_mv(struct hptiop_hba * hba)66500f59701SHighPoint Linux Team static void hptiop_unmap_pci_bar_mv(struct hptiop_hba *hba)
66600f59701SHighPoint Linux Team {
66700f59701SHighPoint Linux Team 	iounmap(hba->u.mv.regs);
66800f59701SHighPoint Linux Team 	iounmap(hba->u.mv.mu);
66900f59701SHighPoint Linux Team }
67000f59701SHighPoint Linux Team 
hptiop_unmap_pci_bar_mvfrey(struct hptiop_hba * hba)671286aa031SHighPoint Linux Team static void hptiop_unmap_pci_bar_mvfrey(struct hptiop_hba *hba)
672286aa031SHighPoint Linux Team {
673286aa031SHighPoint Linux Team 	iounmap(hba->u.mvfrey.config);
674286aa031SHighPoint Linux Team 	iounmap(hba->u.mvfrey.mu);
675286aa031SHighPoint Linux Team }
676286aa031SHighPoint Linux Team 
hptiop_message_callback(struct hptiop_hba * hba,u32 msg)677ede1e6f8SHighPoint Linux Team static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg)
678ede1e6f8SHighPoint Linux Team {
679ede1e6f8SHighPoint Linux Team 	dprintk("iop message 0x%x\n", msg);
680ede1e6f8SHighPoint Linux Team 
681286aa031SHighPoint Linux Team 	if (msg == IOPMU_INBOUND_MSG0_NOP ||
682286aa031SHighPoint Linux Team 		msg == IOPMU_INBOUND_MSG0_RESET_COMM)
68300f59701SHighPoint Linux Team 		hba->msg_done = 1;
68400f59701SHighPoint Linux Team 
685ede1e6f8SHighPoint Linux Team 	if (!hba->initialized)
686ede1e6f8SHighPoint Linux Team 		return;
687ede1e6f8SHighPoint Linux Team 
688ede1e6f8SHighPoint Linux Team 	if (msg == IOPMU_INBOUND_MSG0_RESET) {
689ede1e6f8SHighPoint Linux Team 		atomic_set(&hba->resetting, 0);
690ede1e6f8SHighPoint Linux Team 		wake_up(&hba->reset_wq);
691ede1e6f8SHighPoint Linux Team 	}
692ede1e6f8SHighPoint Linux Team 	else if (msg <= IOPMU_INBOUND_MSG0_MAX)
693ede1e6f8SHighPoint Linux Team 		hba->msg_done = 1;
694ede1e6f8SHighPoint Linux Team }
695ede1e6f8SHighPoint Linux Team 
get_req(struct hptiop_hba * hba)69600f59701SHighPoint Linux Team static struct hptiop_request *get_req(struct hptiop_hba *hba)
697ede1e6f8SHighPoint Linux Team {
698ede1e6f8SHighPoint Linux Team 	struct hptiop_request *ret;
699ede1e6f8SHighPoint Linux Team 
700ede1e6f8SHighPoint Linux Team 	dprintk("get_req : req=%p\n", hba->req_list);
701ede1e6f8SHighPoint Linux Team 
702ede1e6f8SHighPoint Linux Team 	ret = hba->req_list;
703ede1e6f8SHighPoint Linux Team 	if (ret)
704ede1e6f8SHighPoint Linux Team 		hba->req_list = ret->next;
705ede1e6f8SHighPoint Linux Team 
706ede1e6f8SHighPoint Linux Team 	return ret;
707ede1e6f8SHighPoint Linux Team }
708ede1e6f8SHighPoint Linux Team 
free_req(struct hptiop_hba * hba,struct hptiop_request * req)70900f59701SHighPoint Linux Team static void free_req(struct hptiop_hba *hba, struct hptiop_request *req)
710ede1e6f8SHighPoint Linux Team {
711ede1e6f8SHighPoint Linux Team 	dprintk("free_req(%d, %p)\n", req->index, req);
712ede1e6f8SHighPoint Linux Team 	req->next = hba->req_list;
713ede1e6f8SHighPoint Linux Team 	hba->req_list = req;
714ede1e6f8SHighPoint Linux Team }
715ede1e6f8SHighPoint Linux Team 
hptiop_finish_scsi_req(struct hptiop_hba * hba,u32 tag,struct hpt_iop_request_scsi_command * req)71600f59701SHighPoint Linux Team static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
71700f59701SHighPoint Linux Team 				struct hpt_iop_request_scsi_command *req)
718ede1e6f8SHighPoint Linux Team {
719ede1e6f8SHighPoint Linux Team 	struct scsi_cmnd *scp;
720ede1e6f8SHighPoint Linux Team 
72100f59701SHighPoint Linux Team 	dprintk("hptiop_finish_scsi_req: req=%p, type=%d, "
722ede1e6f8SHighPoint Linux Team 			"result=%d, context=0x%x tag=%d\n",
723ede1e6f8SHighPoint Linux Team 			req, req->header.type, req->header.result,
724ede1e6f8SHighPoint Linux Team 			req->header.context, tag);
725ede1e6f8SHighPoint Linux Team 
726ede1e6f8SHighPoint Linux Team 	BUG_ON(!req->header.result);
727ede1e6f8SHighPoint Linux Team 	BUG_ON(req->header.type != cpu_to_le32(IOP_REQUEST_TYPE_SCSI_COMMAND));
728ede1e6f8SHighPoint Linux Team 
729ede1e6f8SHighPoint Linux Team 	scp = hba->reqs[tag].scp;
730ede1e6f8SHighPoint Linux Team 
731f9875496SFUJITA Tomonori 	if (HPT_SCP(scp)->mapped)
732f9875496SFUJITA Tomonori 		scsi_dma_unmap(scp);
733ede1e6f8SHighPoint Linux Team 
734ede1e6f8SHighPoint Linux Team 	switch (le32_to_cpu(req->header.result)) {
735ede1e6f8SHighPoint Linux Team 	case IOP_RESULT_SUCCESS:
73600f59701SHighPoint Linux Team 		scsi_set_resid(scp,
73700f59701SHighPoint Linux Team 			scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length));
738ede1e6f8SHighPoint Linux Team 		scp->result = (DID_OK<<16);
739ede1e6f8SHighPoint Linux Team 		break;
740ede1e6f8SHighPoint Linux Team 	case IOP_RESULT_BAD_TARGET:
741ede1e6f8SHighPoint Linux Team 		scp->result = (DID_BAD_TARGET<<16);
742ede1e6f8SHighPoint Linux Team 		break;
743ede1e6f8SHighPoint Linux Team 	case IOP_RESULT_BUSY:
744ede1e6f8SHighPoint Linux Team 		scp->result = (DID_BUS_BUSY<<16);
745ede1e6f8SHighPoint Linux Team 		break;
746ede1e6f8SHighPoint Linux Team 	case IOP_RESULT_RESET:
747ede1e6f8SHighPoint Linux Team 		scp->result = (DID_RESET<<16);
748ede1e6f8SHighPoint Linux Team 		break;
749ede1e6f8SHighPoint Linux Team 	case IOP_RESULT_FAIL:
750ede1e6f8SHighPoint Linux Team 		scp->result = (DID_ERROR<<16);
751ede1e6f8SHighPoint Linux Team 		break;
752ede1e6f8SHighPoint Linux Team 	case IOP_RESULT_INVALID_REQUEST:
753ede1e6f8SHighPoint Linux Team 		scp->result = (DID_ABORT<<16);
754ede1e6f8SHighPoint Linux Team 		break;
75500f59701SHighPoint Linux Team 	case IOP_RESULT_CHECK_CONDITION:
75600f59701SHighPoint Linux Team 		scsi_set_resid(scp,
75700f59701SHighPoint Linux Team 			scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length));
758ede1e6f8SHighPoint Linux Team 		scp->result = SAM_STAT_CHECK_CONDITION;
759a93429c3Slinux 		memcpy(scp->sense_buffer, &req->sg_list, SCSI_SENSE_BUFFERSIZE);
760286aa031SHighPoint Linux Team 		goto skip_resid;
761ede1e6f8SHighPoint Linux Team 
762ede1e6f8SHighPoint Linux Team 	default:
76316576ad8SHannes Reinecke 		scp->result = DID_ABORT << 16;
764ede1e6f8SHighPoint Linux Team 		break;
765ede1e6f8SHighPoint Linux Team 	}
766ede1e6f8SHighPoint Linux Team 
767286aa031SHighPoint Linux Team 	scsi_set_resid(scp,
768286aa031SHighPoint Linux Team 		scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length));
769286aa031SHighPoint Linux Team 
770286aa031SHighPoint Linux Team skip_resid:
771ede1e6f8SHighPoint Linux Team 	dprintk("scsi_done(%p)\n", scp);
772574015a8SBart Van Assche 	scsi_done(scp);
773ede1e6f8SHighPoint Linux Team 	free_req(hba, &hba->reqs[tag]);
774ede1e6f8SHighPoint Linux Team }
775ede1e6f8SHighPoint Linux Team 
hptiop_host_request_callback_itl(struct hptiop_hba * hba,u32 _tag)77600f59701SHighPoint Linux Team static void hptiop_host_request_callback_itl(struct hptiop_hba *hba, u32 _tag)
77700f59701SHighPoint Linux Team {
77800f59701SHighPoint Linux Team 	struct hpt_iop_request_scsi_command *req;
77900f59701SHighPoint Linux Team 	u32 tag;
78000f59701SHighPoint Linux Team 
78100f59701SHighPoint Linux Team 	if (hba->iopintf_v2) {
78200f59701SHighPoint Linux Team 		tag = _tag & ~IOPMU_QUEUE_REQUEST_RESULT_BIT;
78300f59701SHighPoint Linux Team 		req = hba->reqs[tag].req_virt;
78400f59701SHighPoint Linux Team 		if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT))
78500f59701SHighPoint Linux Team 			req->header.result = cpu_to_le32(IOP_RESULT_SUCCESS);
78600f59701SHighPoint Linux Team 	} else {
78700f59701SHighPoint Linux Team 		tag = _tag;
78800f59701SHighPoint Linux Team 		req = hba->reqs[tag].req_virt;
78900f59701SHighPoint Linux Team 	}
79000f59701SHighPoint Linux Team 
79100f59701SHighPoint Linux Team 	hptiop_finish_scsi_req(hba, tag, req);
79200f59701SHighPoint Linux Team }
79300f59701SHighPoint Linux Team 
hptiop_iop_request_callback_itl(struct hptiop_hba * hba,u32 tag)79406d9eb4eSColin Ian King static void hptiop_iop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
795ede1e6f8SHighPoint Linux Team {
796ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_header __iomem *req;
797ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_ioctl_command __iomem *p;
798ede1e6f8SHighPoint Linux Team 	struct hpt_ioctl_k *arg;
799ede1e6f8SHighPoint Linux Team 
800ede1e6f8SHighPoint Linux Team 	req = (struct hpt_iop_request_header __iomem *)
80100f59701SHighPoint Linux Team 			((unsigned long)hba->u.itl.iop + tag);
80200f59701SHighPoint Linux Team 	dprintk("hptiop_iop_request_callback_itl: req=%p, type=%d, "
803ede1e6f8SHighPoint Linux Team 			"result=%d, context=0x%x tag=%d\n",
804ede1e6f8SHighPoint Linux Team 			req, readl(&req->type), readl(&req->result),
805ede1e6f8SHighPoint Linux Team 			readl(&req->context), tag);
806ede1e6f8SHighPoint Linux Team 
807ede1e6f8SHighPoint Linux Team 	BUG_ON(!readl(&req->result));
808ede1e6f8SHighPoint Linux Team 	BUG_ON(readl(&req->type) != IOP_REQUEST_TYPE_IOCTL_COMMAND);
809ede1e6f8SHighPoint Linux Team 
810ede1e6f8SHighPoint Linux Team 	p = (struct hpt_iop_request_ioctl_command __iomem *)req;
811ede1e6f8SHighPoint Linux Team 	arg = (struct hpt_ioctl_k *)(unsigned long)
812ede1e6f8SHighPoint Linux Team 		(readl(&req->context) |
813ede1e6f8SHighPoint Linux Team 			((u64)readl(&req->context_hi32)<<32));
814ede1e6f8SHighPoint Linux Team 
815ede1e6f8SHighPoint Linux Team 	if (readl(&req->result) == IOP_RESULT_SUCCESS) {
816ede1e6f8SHighPoint Linux Team 		arg->result = HPT_IOCTL_RESULT_OK;
817ede1e6f8SHighPoint Linux Team 
818ede1e6f8SHighPoint Linux Team 		if (arg->outbuf_size)
819ede1e6f8SHighPoint Linux Team 			memcpy_fromio(arg->outbuf,
820ede1e6f8SHighPoint Linux Team 				&p->buf[(readl(&p->inbuf_size) + 3)& ~3],
821ede1e6f8SHighPoint Linux Team 				arg->outbuf_size);
822ede1e6f8SHighPoint Linux Team 
823ede1e6f8SHighPoint Linux Team 		if (arg->bytes_returned)
824ede1e6f8SHighPoint Linux Team 			*arg->bytes_returned = arg->outbuf_size;
825ede1e6f8SHighPoint Linux Team 	}
826ede1e6f8SHighPoint Linux Team 	else
827ede1e6f8SHighPoint Linux Team 		arg->result = HPT_IOCTL_RESULT_FAILED;
828ede1e6f8SHighPoint Linux Team 
829ede1e6f8SHighPoint Linux Team 	arg->done(arg);
83000f59701SHighPoint Linux Team 	writel(tag, &hba->u.itl.iop->outbound_queue);
831ede1e6f8SHighPoint Linux Team }
832ede1e6f8SHighPoint Linux Team 
hptiop_intr(int irq,void * dev_id)8337d12e780SDavid Howells static irqreturn_t hptiop_intr(int irq, void *dev_id)
834ede1e6f8SHighPoint Linux Team {
835ede1e6f8SHighPoint Linux Team 	struct hptiop_hba  *hba = dev_id;
836ede1e6f8SHighPoint Linux Team 	int  handled;
837ede1e6f8SHighPoint Linux Team 	unsigned long flags;
838ede1e6f8SHighPoint Linux Team 
839ede1e6f8SHighPoint Linux Team 	spin_lock_irqsave(hba->host->host_lock, flags);
84000f59701SHighPoint Linux Team 	handled = hba->ops->iop_intr(hba);
841ede1e6f8SHighPoint Linux Team 	spin_unlock_irqrestore(hba->host->host_lock, flags);
842ede1e6f8SHighPoint Linux Team 
843ede1e6f8SHighPoint Linux Team 	return handled;
844ede1e6f8SHighPoint Linux Team }
845ede1e6f8SHighPoint Linux Team 
hptiop_buildsgl(struct scsi_cmnd * scp,struct hpt_iopsg * psg)846ede1e6f8SHighPoint Linux Team static int hptiop_buildsgl(struct scsi_cmnd *scp, struct hpt_iopsg *psg)
847ede1e6f8SHighPoint Linux Team {
848ede1e6f8SHighPoint Linux Team 	struct Scsi_Host *host = scp->device->host;
849ede1e6f8SHighPoint Linux Team 	struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
850f9875496SFUJITA Tomonori 	struct scatterlist *sg;
851f9875496SFUJITA Tomonori 	int idx, nseg;
852ede1e6f8SHighPoint Linux Team 
853f9875496SFUJITA Tomonori 	nseg = scsi_dma_map(scp);
854f9875496SFUJITA Tomonori 	BUG_ON(nseg < 0);
855f9875496SFUJITA Tomonori 	if (!nseg)
856f9875496SFUJITA Tomonori 		return 0;
857ede1e6f8SHighPoint Linux Team 
858f9875496SFUJITA Tomonori 	HPT_SCP(scp)->sgcnt = nseg;
859ede1e6f8SHighPoint Linux Team 	HPT_SCP(scp)->mapped = 1;
860f9875496SFUJITA Tomonori 
861ede1e6f8SHighPoint Linux Team 	BUG_ON(HPT_SCP(scp)->sgcnt > hba->max_sg_descriptors);
862ede1e6f8SHighPoint Linux Team 
863f9875496SFUJITA Tomonori 	scsi_for_each_sg(scp, sg, HPT_SCP(scp)->sgcnt, idx) {
864286aa031SHighPoint Linux Team 		psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)) |
865286aa031SHighPoint Linux Team 			hba->ops->host_phy_flag;
866f9875496SFUJITA Tomonori 		psg[idx].size = cpu_to_le32(sg_dma_len(sg));
867ede1e6f8SHighPoint Linux Team 		psg[idx].eot = (idx == HPT_SCP(scp)->sgcnt - 1) ?
868ede1e6f8SHighPoint Linux Team 			cpu_to_le32(1) : 0;
869ede1e6f8SHighPoint Linux Team 	}
870ede1e6f8SHighPoint Linux Team 	return HPT_SCP(scp)->sgcnt;
871ede1e6f8SHighPoint Linux Team }
872ede1e6f8SHighPoint Linux Team 
hptiop_post_req_itl(struct hptiop_hba * hba,struct hptiop_request * _req)87300f59701SHighPoint Linux Team static void hptiop_post_req_itl(struct hptiop_hba *hba,
87400f59701SHighPoint Linux Team 					struct hptiop_request *_req)
87500f59701SHighPoint Linux Team {
87600f59701SHighPoint Linux Team 	struct hpt_iop_request_header *reqhdr = _req->req_virt;
87700f59701SHighPoint Linux Team 
87800f59701SHighPoint Linux Team 	reqhdr->context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT |
87900f59701SHighPoint Linux Team 							(u32)_req->index);
88000f59701SHighPoint Linux Team 	reqhdr->context_hi32 = 0;
88100f59701SHighPoint Linux Team 
88200f59701SHighPoint Linux Team 	if (hba->iopintf_v2) {
88300f59701SHighPoint Linux Team 		u32 size, size_bits;
88400f59701SHighPoint Linux Team 
88500f59701SHighPoint Linux Team 		size = le32_to_cpu(reqhdr->size);
88600f59701SHighPoint Linux Team 		if (size < 256)
88700f59701SHighPoint Linux Team 			size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT;
88800f59701SHighPoint Linux Team 		else if (size < 512)
88900f59701SHighPoint Linux Team 			size_bits = IOPMU_QUEUE_ADDR_HOST_BIT;
89000f59701SHighPoint Linux Team 		else
89100f59701SHighPoint Linux Team 			size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT |
89200f59701SHighPoint Linux Team 						IOPMU_QUEUE_ADDR_HOST_BIT;
89300f59701SHighPoint Linux Team 		writel(_req->req_shifted_phy | size_bits,
89400f59701SHighPoint Linux Team 			&hba->u.itl.iop->inbound_queue);
89500f59701SHighPoint Linux Team 	} else
89600f59701SHighPoint Linux Team 		writel(_req->req_shifted_phy | IOPMU_QUEUE_ADDR_HOST_BIT,
89700f59701SHighPoint Linux Team 					&hba->u.itl.iop->inbound_queue);
89800f59701SHighPoint Linux Team }
89900f59701SHighPoint Linux Team 
hptiop_post_req_mv(struct hptiop_hba * hba,struct hptiop_request * _req)90000f59701SHighPoint Linux Team static void hptiop_post_req_mv(struct hptiop_hba *hba,
90100f59701SHighPoint Linux Team 					struct hptiop_request *_req)
90200f59701SHighPoint Linux Team {
90300f59701SHighPoint Linux Team 	struct hpt_iop_request_header *reqhdr = _req->req_virt;
90400f59701SHighPoint Linux Team 	u32 size, size_bit;
90500f59701SHighPoint Linux Team 
90600f59701SHighPoint Linux Team 	reqhdr->context = cpu_to_le32(_req->index<<8 |
90700f59701SHighPoint Linux Team 					IOP_REQUEST_TYPE_SCSI_COMMAND<<5);
90800f59701SHighPoint Linux Team 	reqhdr->context_hi32 = 0;
90900f59701SHighPoint Linux Team 	size = le32_to_cpu(reqhdr->size);
91000f59701SHighPoint Linux Team 
91100f59701SHighPoint Linux Team 	if (size <= 256)
91200f59701SHighPoint Linux Team 		size_bit = 0;
91300f59701SHighPoint Linux Team 	else if (size <= 256*2)
91400f59701SHighPoint Linux Team 		size_bit = 1;
91500f59701SHighPoint Linux Team 	else if (size <= 256*3)
91600f59701SHighPoint Linux Team 		size_bit = 2;
91700f59701SHighPoint Linux Team 	else
91800f59701SHighPoint Linux Team 		size_bit = 3;
91900f59701SHighPoint Linux Team 
92000f59701SHighPoint Linux Team 	mv_inbound_write((_req->req_shifted_phy << 5) |
92100f59701SHighPoint Linux Team 		MVIOP_MU_QUEUE_ADDR_HOST_BIT | size_bit, hba);
92200f59701SHighPoint Linux Team }
92300f59701SHighPoint Linux Team 
hptiop_post_req_mvfrey(struct hptiop_hba * hba,struct hptiop_request * _req)924286aa031SHighPoint Linux Team static void hptiop_post_req_mvfrey(struct hptiop_hba *hba,
925286aa031SHighPoint Linux Team 					struct hptiop_request *_req)
926286aa031SHighPoint Linux Team {
927286aa031SHighPoint Linux Team 	struct hpt_iop_request_header *reqhdr = _req->req_virt;
928286aa031SHighPoint Linux Team 	u32 index;
929286aa031SHighPoint Linux Team 
930286aa031SHighPoint Linux Team 	reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT |
931286aa031SHighPoint Linux Team 			IOP_REQUEST_FLAG_ADDR_BITS |
932286aa031SHighPoint Linux Team 			((_req->req_shifted_phy >> 11) & 0xffff0000));
933286aa031SHighPoint Linux Team 	reqhdr->context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT |
934286aa031SHighPoint Linux Team 			(_req->index << 4) | reqhdr->type);
935286aa031SHighPoint Linux Team 	reqhdr->context_hi32 = cpu_to_le32((_req->req_shifted_phy << 5) &
936286aa031SHighPoint Linux Team 			0xffffffff);
937286aa031SHighPoint Linux Team 
938286aa031SHighPoint Linux Team 	hba->u.mvfrey.inlist_wptr++;
939286aa031SHighPoint Linux Team 	index = hba->u.mvfrey.inlist_wptr & 0x3fff;
940286aa031SHighPoint Linux Team 
941286aa031SHighPoint Linux Team 	if (index == hba->u.mvfrey.list_count) {
942286aa031SHighPoint Linux Team 		index = 0;
943286aa031SHighPoint Linux Team 		hba->u.mvfrey.inlist_wptr &= ~0x3fff;
944286aa031SHighPoint Linux Team 		hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE;
945286aa031SHighPoint Linux Team 	}
946286aa031SHighPoint Linux Team 
947286aa031SHighPoint Linux Team 	hba->u.mvfrey.inlist[index].addr =
948286aa031SHighPoint Linux Team 			(dma_addr_t)_req->req_shifted_phy << 5;
949286aa031SHighPoint Linux Team 	hba->u.mvfrey.inlist[index].intrfc_len = (reqhdr->size + 3) / 4;
950286aa031SHighPoint Linux Team 	writel(hba->u.mvfrey.inlist_wptr,
951286aa031SHighPoint Linux Team 		&(hba->u.mvfrey.mu->inbound_write_ptr));
952286aa031SHighPoint Linux Team 	readl(&(hba->u.mvfrey.mu->inbound_write_ptr));
953286aa031SHighPoint Linux Team }
954286aa031SHighPoint Linux Team 
hptiop_reset_comm_itl(struct hptiop_hba * hba)955286aa031SHighPoint Linux Team static int hptiop_reset_comm_itl(struct hptiop_hba *hba)
956286aa031SHighPoint Linux Team {
957286aa031SHighPoint Linux Team 	return 0;
958286aa031SHighPoint Linux Team }
959286aa031SHighPoint Linux Team 
hptiop_reset_comm_mv(struct hptiop_hba * hba)960286aa031SHighPoint Linux Team static int hptiop_reset_comm_mv(struct hptiop_hba *hba)
961286aa031SHighPoint Linux Team {
962286aa031SHighPoint Linux Team 	return 0;
963286aa031SHighPoint Linux Team }
964286aa031SHighPoint Linux Team 
hptiop_reset_comm_mvfrey(struct hptiop_hba * hba)965286aa031SHighPoint Linux Team static int hptiop_reset_comm_mvfrey(struct hptiop_hba *hba)
966286aa031SHighPoint Linux Team {
967286aa031SHighPoint Linux Team 	u32 list_count = hba->u.mvfrey.list_count;
968286aa031SHighPoint Linux Team 
969286aa031SHighPoint Linux Team 	if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET_COMM, 3000))
970286aa031SHighPoint Linux Team 		return -1;
971286aa031SHighPoint Linux Team 
972286aa031SHighPoint Linux Team 	/* wait 100ms for MCU ready */
973286aa031SHighPoint Linux Team 	msleep(100);
974286aa031SHighPoint Linux Team 
975286aa031SHighPoint Linux Team 	writel(cpu_to_le32(hba->u.mvfrey.inlist_phy & 0xffffffff),
976286aa031SHighPoint Linux Team 			&(hba->u.mvfrey.mu->inbound_base));
977286aa031SHighPoint Linux Team 	writel(cpu_to_le32((hba->u.mvfrey.inlist_phy >> 16) >> 16),
978286aa031SHighPoint Linux Team 			&(hba->u.mvfrey.mu->inbound_base_high));
979286aa031SHighPoint Linux Team 
980286aa031SHighPoint Linux Team 	writel(cpu_to_le32(hba->u.mvfrey.outlist_phy & 0xffffffff),
981286aa031SHighPoint Linux Team 			&(hba->u.mvfrey.mu->outbound_base));
982286aa031SHighPoint Linux Team 	writel(cpu_to_le32((hba->u.mvfrey.outlist_phy >> 16) >> 16),
983286aa031SHighPoint Linux Team 			&(hba->u.mvfrey.mu->outbound_base_high));
984286aa031SHighPoint Linux Team 
985286aa031SHighPoint Linux Team 	writel(cpu_to_le32(hba->u.mvfrey.outlist_cptr_phy & 0xffffffff),
986286aa031SHighPoint Linux Team 			&(hba->u.mvfrey.mu->outbound_shadow_base));
987286aa031SHighPoint Linux Team 	writel(cpu_to_le32((hba->u.mvfrey.outlist_cptr_phy >> 16) >> 16),
988286aa031SHighPoint Linux Team 			&(hba->u.mvfrey.mu->outbound_shadow_base_high));
989286aa031SHighPoint Linux Team 
990286aa031SHighPoint Linux Team 	hba->u.mvfrey.inlist_wptr = (list_count - 1) | CL_POINTER_TOGGLE;
991286aa031SHighPoint Linux Team 	*hba->u.mvfrey.outlist_cptr = (list_count - 1) | CL_POINTER_TOGGLE;
992286aa031SHighPoint Linux Team 	hba->u.mvfrey.outlist_rptr = list_count - 1;
993286aa031SHighPoint Linux Team 	return 0;
994286aa031SHighPoint Linux Team }
995286aa031SHighPoint Linux Team 
hptiop_queuecommand_lck(struct scsi_cmnd * scp)996af049dfdSBart Van Assche static int hptiop_queuecommand_lck(struct scsi_cmnd *scp)
997ede1e6f8SHighPoint Linux Team {
998ede1e6f8SHighPoint Linux Team 	struct Scsi_Host *host = scp->device->host;
999ede1e6f8SHighPoint Linux Team 	struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
1000ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_scsi_command *req;
1001ede1e6f8SHighPoint Linux Team 	int sg_count = 0;
1002ede1e6f8SHighPoint Linux Team 	struct hptiop_request *_req;
1003ede1e6f8SHighPoint Linux Team 
1004ede1e6f8SHighPoint Linux Team 	_req = get_req(hba);
1005ede1e6f8SHighPoint Linux Team 	if (_req == NULL) {
1006ede1e6f8SHighPoint Linux Team 		dprintk("hptiop_queuecmd : no free req\n");
10074f2ddba3SHighPoint Linux Team 		return SCSI_MLQUEUE_HOST_BUSY;
1008ede1e6f8SHighPoint Linux Team 	}
1009ede1e6f8SHighPoint Linux Team 
1010ede1e6f8SHighPoint Linux Team 	_req->scp = scp;
1011ede1e6f8SHighPoint Linux Team 
10129cb78c16SHannes Reinecke 	dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%llu cdb=(%08x-%08x-%08x-%08x) "
1013ede1e6f8SHighPoint Linux Team 			"req_index=%d, req=%p\n",
1014ede1e6f8SHighPoint Linux Team 			scp,
1015ede1e6f8SHighPoint Linux Team 			host->host_no, scp->device->channel,
1016ede1e6f8SHighPoint Linux Team 			scp->device->id, scp->device->lun,
1017286aa031SHighPoint Linux Team 			cpu_to_be32(((u32 *)scp->cmnd)[0]),
1018286aa031SHighPoint Linux Team 			cpu_to_be32(((u32 *)scp->cmnd)[1]),
1019286aa031SHighPoint Linux Team 			cpu_to_be32(((u32 *)scp->cmnd)[2]),
1020286aa031SHighPoint Linux Team 			cpu_to_be32(((u32 *)scp->cmnd)[3]),
1021ede1e6f8SHighPoint Linux Team 			_req->index, _req->req_virt);
1022ede1e6f8SHighPoint Linux Team 
1023ede1e6f8SHighPoint Linux Team 	scp->result = 0;
1024ede1e6f8SHighPoint Linux Team 
1025a93429c3Slinux 	if (scp->device->channel ||
1026a93429c3Slinux 			(scp->device->id > hba->max_devices) ||
1027a93429c3Slinux 			((scp->device->id == (hba->max_devices-1)) && scp->device->lun)) {
1028ede1e6f8SHighPoint Linux Team 		scp->result = DID_BAD_TARGET << 16;
1029ede1e6f8SHighPoint Linux Team 		free_req(hba, _req);
1030ede1e6f8SHighPoint Linux Team 		goto cmd_done;
1031ede1e6f8SHighPoint Linux Team 	}
1032ede1e6f8SHighPoint Linux Team 
1033db9b6e89SHighPoint Linux Team 	req = _req->req_virt;
1034ede1e6f8SHighPoint Linux Team 
1035ede1e6f8SHighPoint Linux Team 	/* build S/G table */
1036ede1e6f8SHighPoint Linux Team 	sg_count = hptiop_buildsgl(scp, req->sg_list);
1037f9875496SFUJITA Tomonori 	if (!sg_count)
1038ede1e6f8SHighPoint Linux Team 		HPT_SCP(scp)->mapped = 0;
1039ede1e6f8SHighPoint Linux Team 
1040ede1e6f8SHighPoint Linux Team 	req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
1041ede1e6f8SHighPoint Linux Team 	req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SCSI_COMMAND);
1042ede1e6f8SHighPoint Linux Team 	req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
1043f9875496SFUJITA Tomonori 	req->dataxfer_length = cpu_to_le32(scsi_bufflen(scp));
1044ede1e6f8SHighPoint Linux Team 	req->channel = scp->device->channel;
1045ede1e6f8SHighPoint Linux Team 	req->target = scp->device->id;
1046ede1e6f8SHighPoint Linux Team 	req->lun = scp->device->lun;
10475b12a568SGustavo A. R. Silva 	req->header.size = cpu_to_le32(struct_size(req, sg_list, sg_count));
1048ede1e6f8SHighPoint Linux Team 
1049ede1e6f8SHighPoint Linux Team 	memcpy(req->cdb, scp->cmnd, sizeof(req->cdb));
105000f59701SHighPoint Linux Team 	hba->ops->post_req(hba, _req);
1051ede1e6f8SHighPoint Linux Team 	return 0;
1052ede1e6f8SHighPoint Linux Team 
1053ede1e6f8SHighPoint Linux Team cmd_done:
1054ede1e6f8SHighPoint Linux Team 	dprintk("scsi_done(scp=%p)\n", scp);
1055574015a8SBart Van Assche 	scsi_done(scp);
1056ede1e6f8SHighPoint Linux Team 	return 0;
1057ede1e6f8SHighPoint Linux Team }
1058ede1e6f8SHighPoint Linux Team 
DEF_SCSI_QCMD(hptiop_queuecommand)1059f281233dSJeff Garzik static DEF_SCSI_QCMD(hptiop_queuecommand)
1060f281233dSJeff Garzik 
1061ede1e6f8SHighPoint Linux Team static const char *hptiop_info(struct Scsi_Host *host)
1062ede1e6f8SHighPoint Linux Team {
1063ede1e6f8SHighPoint Linux Team 	return driver_name_long;
1064ede1e6f8SHighPoint Linux Team }
1065ede1e6f8SHighPoint Linux Team 
hptiop_reset_hba(struct hptiop_hba * hba)1066ede1e6f8SHighPoint Linux Team static int hptiop_reset_hba(struct hptiop_hba *hba)
1067ede1e6f8SHighPoint Linux Team {
1068ede1e6f8SHighPoint Linux Team 	if (atomic_xchg(&hba->resetting, 1) == 0) {
1069ede1e6f8SHighPoint Linux Team 		atomic_inc(&hba->reset_count);
107000f59701SHighPoint Linux Team 		hba->ops->post_msg(hba, IOPMU_INBOUND_MSG0_RESET);
1071ede1e6f8SHighPoint Linux Team 	}
1072ede1e6f8SHighPoint Linux Team 
1073ede1e6f8SHighPoint Linux Team 	wait_event_timeout(hba->reset_wq,
1074ede1e6f8SHighPoint Linux Team 			atomic_read(&hba->resetting) == 0, 60 * HZ);
1075ede1e6f8SHighPoint Linux Team 
1076ede1e6f8SHighPoint Linux Team 	if (atomic_read(&hba->resetting)) {
1077af901ca1SAndré Goddard Rosa 		/* IOP is in unknown state, abort reset */
1078ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: reset failed\n", hba->host->host_no);
1079ede1e6f8SHighPoint Linux Team 		return -1;
1080ede1e6f8SHighPoint Linux Team 	}
1081ede1e6f8SHighPoint Linux Team 
1082ede1e6f8SHighPoint Linux Team 	if (iop_send_sync_msg(hba,
1083ede1e6f8SHighPoint Linux Team 		IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) {
1084ede1e6f8SHighPoint Linux Team 		dprintk("scsi%d: fail to start background task\n",
1085ede1e6f8SHighPoint Linux Team 				hba->host->host_no);
1086ede1e6f8SHighPoint Linux Team 	}
1087ede1e6f8SHighPoint Linux Team 
1088ede1e6f8SHighPoint Linux Team 	return 0;
1089ede1e6f8SHighPoint Linux Team }
1090ede1e6f8SHighPoint Linux Team 
hptiop_reset(struct scsi_cmnd * scp)1091ede1e6f8SHighPoint Linux Team static int hptiop_reset(struct scsi_cmnd *scp)
1092ede1e6f8SHighPoint Linux Team {
1093aceb2948SHannes Reinecke 	struct hptiop_hba * hba = (struct hptiop_hba *)scp->device->host->hostdata;
1094ede1e6f8SHighPoint Linux Team 
1095aceb2948SHannes Reinecke 	printk(KERN_WARNING "hptiop_reset(%d/%d/%d)\n",
1096aceb2948SHannes Reinecke 	       scp->device->host->host_no, -1, -1);
1097ede1e6f8SHighPoint Linux Team 
1098ede1e6f8SHighPoint Linux Team 	return hptiop_reset_hba(hba)? FAILED : SUCCESS;
1099ede1e6f8SHighPoint Linux Team }
1100ede1e6f8SHighPoint Linux Team 
hptiop_adjust_disk_queue_depth(struct scsi_device * sdev,int queue_depth)1101ede1e6f8SHighPoint Linux Team static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev,
1102db5ed4dfSChristoph Hellwig 					  int queue_depth)
1103ede1e6f8SHighPoint Linux Team {
110400f59701SHighPoint Linux Team 	struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata;
110500f59701SHighPoint Linux Team 
110600f59701SHighPoint Linux Team 	if (queue_depth > hba->max_requests)
110700f59701SHighPoint Linux Team 		queue_depth = hba->max_requests;
1108db5ed4dfSChristoph Hellwig 	return scsi_change_queue_depth(sdev, queue_depth);
1109ede1e6f8SHighPoint Linux Team }
1110ede1e6f8SHighPoint Linux Team 
hptiop_show_version(struct device * dev,struct device_attribute * attr,char * buf)1111ee959b00STony Jones static ssize_t hptiop_show_version(struct device *dev,
1112ee959b00STony Jones 				   struct device_attribute *attr, char *buf)
1113ede1e6f8SHighPoint Linux Team {
1114ede1e6f8SHighPoint Linux Team 	return snprintf(buf, PAGE_SIZE, "%s\n", driver_ver);
1115ede1e6f8SHighPoint Linux Team }
1116ede1e6f8SHighPoint Linux Team 
hptiop_show_fw_version(struct device * dev,struct device_attribute * attr,char * buf)1117ee959b00STony Jones static ssize_t hptiop_show_fw_version(struct device *dev,
1118ee959b00STony Jones 				      struct device_attribute *attr, char *buf)
1119ede1e6f8SHighPoint Linux Team {
1120ee959b00STony Jones 	struct Scsi_Host *host = class_to_shost(dev);
1121ede1e6f8SHighPoint Linux Team 	struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
1122ede1e6f8SHighPoint Linux Team 
1123ede1e6f8SHighPoint Linux Team 	return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n",
1124ede1e6f8SHighPoint Linux Team 				hba->firmware_version >> 24,
1125ede1e6f8SHighPoint Linux Team 				(hba->firmware_version >> 16) & 0xff,
1126ede1e6f8SHighPoint Linux Team 				(hba->firmware_version >> 8) & 0xff,
1127ede1e6f8SHighPoint Linux Team 				hba->firmware_version & 0xff);
1128ede1e6f8SHighPoint Linux Team }
1129ede1e6f8SHighPoint Linux Team 
1130ee959b00STony Jones static struct device_attribute hptiop_attr_version = {
1131ede1e6f8SHighPoint Linux Team 	.attr = {
1132ede1e6f8SHighPoint Linux Team 		.name = "driver-version",
1133ede1e6f8SHighPoint Linux Team 		.mode = S_IRUGO,
1134ede1e6f8SHighPoint Linux Team 	},
1135ede1e6f8SHighPoint Linux Team 	.show = hptiop_show_version,
1136ede1e6f8SHighPoint Linux Team };
1137ede1e6f8SHighPoint Linux Team 
1138ee959b00STony Jones static struct device_attribute hptiop_attr_fw_version = {
1139ede1e6f8SHighPoint Linux Team 	.attr = {
1140ede1e6f8SHighPoint Linux Team 		.name = "firmware-version",
1141ede1e6f8SHighPoint Linux Team 		.mode = S_IRUGO,
1142ede1e6f8SHighPoint Linux Team 	},
1143ede1e6f8SHighPoint Linux Team 	.show = hptiop_show_fw_version,
1144ede1e6f8SHighPoint Linux Team };
1145ede1e6f8SHighPoint Linux Team 
1146e8fbc28eSBart Van Assche static struct attribute *hptiop_host_attrs[] = {
1147e8fbc28eSBart Van Assche 	&hptiop_attr_version.attr,
1148e8fbc28eSBart Van Assche 	&hptiop_attr_fw_version.attr,
1149ede1e6f8SHighPoint Linux Team 	NULL
1150ede1e6f8SHighPoint Linux Team };
1151ede1e6f8SHighPoint Linux Team 
1152e8fbc28eSBart Van Assche ATTRIBUTE_GROUPS(hptiop_host);
1153e8fbc28eSBart Van Assche 
hptiop_slave_config(struct scsi_device * sdev)1154a93429c3Slinux static int hptiop_slave_config(struct scsi_device *sdev)
1155a93429c3Slinux {
1156a93429c3Slinux 	if (sdev->type == TYPE_TAPE)
1157a93429c3Slinux 		blk_queue_max_hw_sectors(sdev->request_queue, 8192);
1158a93429c3Slinux 
1159a93429c3Slinux 	return 0;
1160a93429c3Slinux }
1161a93429c3Slinux 
11629194970bSBart Van Assche static const struct scsi_host_template driver_template = {
1163ede1e6f8SHighPoint Linux Team 	.module                     = THIS_MODULE,
1164ede1e6f8SHighPoint Linux Team 	.name                       = driver_name,
1165ede1e6f8SHighPoint Linux Team 	.queuecommand               = hptiop_queuecommand,
1166aceb2948SHannes Reinecke 	.eh_host_reset_handler      = hptiop_reset,
1167ede1e6f8SHighPoint Linux Team 	.info                       = hptiop_info,
1168ede1e6f8SHighPoint Linux Team 	.emulated                   = 0,
1169ede1e6f8SHighPoint Linux Team 	.proc_name                  = driver_name,
1170e8fbc28eSBart Van Assche 	.shost_groups		    = hptiop_host_groups,
1171a93429c3Slinux 	.slave_configure            = hptiop_slave_config,
1172ede1e6f8SHighPoint Linux Team 	.this_id                    = -1,
1173ede1e6f8SHighPoint Linux Team 	.change_queue_depth         = hptiop_adjust_disk_queue_depth,
11745c113eb3SBart Van Assche 	.cmd_size		    = sizeof(struct hpt_cmd_priv),
1175ede1e6f8SHighPoint Linux Team };
1176ede1e6f8SHighPoint Linux Team 
hptiop_internal_memalloc_itl(struct hptiop_hba * hba)1177286aa031SHighPoint Linux Team static int hptiop_internal_memalloc_itl(struct hptiop_hba *hba)
1178286aa031SHighPoint Linux Team {
1179286aa031SHighPoint Linux Team 	return 0;
1180286aa031SHighPoint Linux Team }
1181286aa031SHighPoint Linux Team 
hptiop_internal_memalloc_mv(struct hptiop_hba * hba)118200f59701SHighPoint Linux Team static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba)
118300f59701SHighPoint Linux Team {
118400f59701SHighPoint Linux Team 	hba->u.mv.internal_req = dma_alloc_coherent(&hba->pcidev->dev,
118500f59701SHighPoint Linux Team 			0x800, &hba->u.mv.internal_req_phy, GFP_KERNEL);
118600f59701SHighPoint Linux Team 	if (hba->u.mv.internal_req)
118700f59701SHighPoint Linux Team 		return 0;
118800f59701SHighPoint Linux Team 	else
118900f59701SHighPoint Linux Team 		return -1;
119000f59701SHighPoint Linux Team }
119100f59701SHighPoint Linux Team 
hptiop_internal_memalloc_mvfrey(struct hptiop_hba * hba)1192286aa031SHighPoint Linux Team static int hptiop_internal_memalloc_mvfrey(struct hptiop_hba *hba)
1193286aa031SHighPoint Linux Team {
1194286aa031SHighPoint Linux Team 	u32 list_count = readl(&hba->u.mvfrey.mu->inbound_conf_ctl);
1195286aa031SHighPoint Linux Team 	char *p;
1196286aa031SHighPoint Linux Team 	dma_addr_t phy;
1197286aa031SHighPoint Linux Team 
1198286aa031SHighPoint Linux Team 	BUG_ON(hba->max_request_size == 0);
1199286aa031SHighPoint Linux Team 
1200286aa031SHighPoint Linux Team 	if (list_count == 0) {
1201286aa031SHighPoint Linux Team 		BUG_ON(1);
1202286aa031SHighPoint Linux Team 		return -1;
1203286aa031SHighPoint Linux Team 	}
1204286aa031SHighPoint Linux Team 
1205286aa031SHighPoint Linux Team 	list_count >>= 16;
1206286aa031SHighPoint Linux Team 
1207286aa031SHighPoint Linux Team 	hba->u.mvfrey.list_count = list_count;
1208286aa031SHighPoint Linux Team 	hba->u.mvfrey.internal_mem_size = 0x800 +
1209286aa031SHighPoint Linux Team 			list_count * sizeof(struct mvfrey_inlist_entry) +
1210286aa031SHighPoint Linux Team 			list_count * sizeof(struct mvfrey_outlist_entry) +
1211286aa031SHighPoint Linux Team 			sizeof(int);
1212286aa031SHighPoint Linux Team 
1213286aa031SHighPoint Linux Team 	p = dma_alloc_coherent(&hba->pcidev->dev,
1214286aa031SHighPoint Linux Team 			hba->u.mvfrey.internal_mem_size, &phy, GFP_KERNEL);
1215286aa031SHighPoint Linux Team 	if (!p)
1216286aa031SHighPoint Linux Team 		return -1;
1217286aa031SHighPoint Linux Team 
1218286aa031SHighPoint Linux Team 	hba->u.mvfrey.internal_req.req_virt = p;
1219286aa031SHighPoint Linux Team 	hba->u.mvfrey.internal_req.req_shifted_phy = phy >> 5;
1220286aa031SHighPoint Linux Team 	hba->u.mvfrey.internal_req.scp = NULL;
1221286aa031SHighPoint Linux Team 	hba->u.mvfrey.internal_req.next = NULL;
1222286aa031SHighPoint Linux Team 
1223286aa031SHighPoint Linux Team 	p += 0x800;
1224286aa031SHighPoint Linux Team 	phy += 0x800;
1225286aa031SHighPoint Linux Team 
1226286aa031SHighPoint Linux Team 	hba->u.mvfrey.inlist = (struct mvfrey_inlist_entry *)p;
1227286aa031SHighPoint Linux Team 	hba->u.mvfrey.inlist_phy = phy;
1228286aa031SHighPoint Linux Team 
1229286aa031SHighPoint Linux Team 	p += list_count * sizeof(struct mvfrey_inlist_entry);
1230286aa031SHighPoint Linux Team 	phy += list_count * sizeof(struct mvfrey_inlist_entry);
1231286aa031SHighPoint Linux Team 
1232286aa031SHighPoint Linux Team 	hba->u.mvfrey.outlist = (struct mvfrey_outlist_entry *)p;
1233286aa031SHighPoint Linux Team 	hba->u.mvfrey.outlist_phy = phy;
1234286aa031SHighPoint Linux Team 
1235286aa031SHighPoint Linux Team 	p += list_count * sizeof(struct mvfrey_outlist_entry);
1236286aa031SHighPoint Linux Team 	phy += list_count * sizeof(struct mvfrey_outlist_entry);
1237286aa031SHighPoint Linux Team 
1238286aa031SHighPoint Linux Team 	hba->u.mvfrey.outlist_cptr = (__le32 *)p;
1239286aa031SHighPoint Linux Team 	hba->u.mvfrey.outlist_cptr_phy = phy;
1240286aa031SHighPoint Linux Team 
1241286aa031SHighPoint Linux Team 	return 0;
1242286aa031SHighPoint Linux Team }
1243286aa031SHighPoint Linux Team 
hptiop_internal_memfree_itl(struct hptiop_hba * hba)1244286aa031SHighPoint Linux Team static int hptiop_internal_memfree_itl(struct hptiop_hba *hba)
1245286aa031SHighPoint Linux Team {
1246286aa031SHighPoint Linux Team 	return 0;
1247286aa031SHighPoint Linux Team }
1248286aa031SHighPoint Linux Team 
hptiop_internal_memfree_mv(struct hptiop_hba * hba)124900f59701SHighPoint Linux Team static int hptiop_internal_memfree_mv(struct hptiop_hba *hba)
125000f59701SHighPoint Linux Team {
125100f59701SHighPoint Linux Team 	if (hba->u.mv.internal_req) {
125200f59701SHighPoint Linux Team 		dma_free_coherent(&hba->pcidev->dev, 0x800,
125300f59701SHighPoint Linux Team 			hba->u.mv.internal_req, hba->u.mv.internal_req_phy);
125400f59701SHighPoint Linux Team 		return 0;
125500f59701SHighPoint Linux Team 	} else
125600f59701SHighPoint Linux Team 		return -1;
125700f59701SHighPoint Linux Team }
125800f59701SHighPoint Linux Team 
hptiop_internal_memfree_mvfrey(struct hptiop_hba * hba)1259286aa031SHighPoint Linux Team static int hptiop_internal_memfree_mvfrey(struct hptiop_hba *hba)
1260286aa031SHighPoint Linux Team {
1261286aa031SHighPoint Linux Team 	if (hba->u.mvfrey.internal_req.req_virt) {
1262286aa031SHighPoint Linux Team 		dma_free_coherent(&hba->pcidev->dev,
1263286aa031SHighPoint Linux Team 			hba->u.mvfrey.internal_mem_size,
1264286aa031SHighPoint Linux Team 			hba->u.mvfrey.internal_req.req_virt,
1265286aa031SHighPoint Linux Team 			(dma_addr_t)
1266286aa031SHighPoint Linux Team 			hba->u.mvfrey.internal_req.req_shifted_phy << 5);
1267286aa031SHighPoint Linux Team 		return 0;
1268286aa031SHighPoint Linux Team 	} else
1269286aa031SHighPoint Linux Team 		return -1;
1270286aa031SHighPoint Linux Team }
1271286aa031SHighPoint Linux Team 
hptiop_probe(struct pci_dev * pcidev,const struct pci_device_id * id)12726f039790SGreg Kroah-Hartman static int hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id)
1273ede1e6f8SHighPoint Linux Team {
1274ede1e6f8SHighPoint Linux Team 	struct Scsi_Host *host = NULL;
1275ede1e6f8SHighPoint Linux Team 	struct hptiop_hba *hba;
127623f0bb47SHighPoint Linux Team 	struct hptiop_adapter_ops *iop_ops;
1277ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_get_config iop_config;
1278ede1e6f8SHighPoint Linux Team 	struct hpt_iop_request_set_config set_config;
1279ede1e6f8SHighPoint Linux Team 	dma_addr_t start_phy;
1280ede1e6f8SHighPoint Linux Team 	void *start_virt;
1281ede1e6f8SHighPoint Linux Team 	u32 offset, i, req_size;
12823e344b6cSHannes Reinecke 	int rc;
1283ede1e6f8SHighPoint Linux Team 
1284ede1e6f8SHighPoint Linux Team 	dprintk("hptiop_probe(%p)\n", pcidev);
1285ede1e6f8SHighPoint Linux Team 
1286ede1e6f8SHighPoint Linux Team 	if (pci_enable_device(pcidev)) {
1287ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "hptiop: fail to enable pci device\n");
1288ede1e6f8SHighPoint Linux Team 		return -ENODEV;
1289ede1e6f8SHighPoint Linux Team 	}
1290ede1e6f8SHighPoint Linux Team 
1291ede1e6f8SHighPoint Linux Team 	printk(KERN_INFO "adapter at PCI %d:%d:%d, IRQ %d\n",
1292ede1e6f8SHighPoint Linux Team 		pcidev->bus->number, pcidev->devfn >> 3, pcidev->devfn & 7,
1293ede1e6f8SHighPoint Linux Team 		pcidev->irq);
1294ede1e6f8SHighPoint Linux Team 
1295ede1e6f8SHighPoint Linux Team 	pci_set_master(pcidev);
1296ede1e6f8SHighPoint Linux Team 
1297ede1e6f8SHighPoint Linux Team 	/* Enable 64bit DMA if possible */
129823f0bb47SHighPoint Linux Team 	iop_ops = (struct hptiop_adapter_ops *)id->driver_data;
12993e344b6cSHannes Reinecke 	rc = dma_set_mask(&pcidev->dev,
13003e344b6cSHannes Reinecke 			  DMA_BIT_MASK(iop_ops->hw_dma_bit_mask));
13013e344b6cSHannes Reinecke 	if (rc)
13023e344b6cSHannes Reinecke 		rc = dma_set_mask(&pcidev->dev, DMA_BIT_MASK(32));
13033e344b6cSHannes Reinecke 
13043e344b6cSHannes Reinecke 	if (rc) {
1305ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "hptiop: fail to set dma_mask\n");
1306ede1e6f8SHighPoint Linux Team 		goto disable_pci_device;
1307ede1e6f8SHighPoint Linux Team 	}
1308ede1e6f8SHighPoint Linux Team 
1309ede1e6f8SHighPoint Linux Team 	if (pci_request_regions(pcidev, driver_name)) {
1310ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "hptiop: pci_request_regions failed\n");
1311ede1e6f8SHighPoint Linux Team 		goto disable_pci_device;
1312ede1e6f8SHighPoint Linux Team 	}
1313ede1e6f8SHighPoint Linux Team 
1314ede1e6f8SHighPoint Linux Team 	host = scsi_host_alloc(&driver_template, sizeof(struct hptiop_hba));
1315ede1e6f8SHighPoint Linux Team 	if (!host) {
1316ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "hptiop: fail to alloc scsi host\n");
1317ede1e6f8SHighPoint Linux Team 		goto free_pci_regions;
1318ede1e6f8SHighPoint Linux Team 	}
1319ede1e6f8SHighPoint Linux Team 
1320ede1e6f8SHighPoint Linux Team 	hba = (struct hptiop_hba *)host->hostdata;
1321a93429c3Slinux 	memset(hba, 0, sizeof(struct hptiop_hba));
1322ede1e6f8SHighPoint Linux Team 
132323f0bb47SHighPoint Linux Team 	hba->ops = iop_ops;
1324ede1e6f8SHighPoint Linux Team 	hba->pcidev = pcidev;
1325ede1e6f8SHighPoint Linux Team 	hba->host = host;
1326ede1e6f8SHighPoint Linux Team 	hba->initialized = 0;
1327db9b6e89SHighPoint Linux Team 	hba->iopintf_v2 = 0;
1328ede1e6f8SHighPoint Linux Team 
1329ede1e6f8SHighPoint Linux Team 	atomic_set(&hba->resetting, 0);
1330ede1e6f8SHighPoint Linux Team 	atomic_set(&hba->reset_count, 0);
1331ede1e6f8SHighPoint Linux Team 
1332ede1e6f8SHighPoint Linux Team 	init_waitqueue_head(&hba->reset_wq);
1333ede1e6f8SHighPoint Linux Team 	init_waitqueue_head(&hba->ioctl_wq);
1334ede1e6f8SHighPoint Linux Team 
1335a93429c3Slinux 	host->max_lun = 128;
1336ede1e6f8SHighPoint Linux Team 	host->max_channel = 0;
1337ede1e6f8SHighPoint Linux Team 	host->io_port = 0;
1338ede1e6f8SHighPoint Linux Team 	host->n_io_port = 0;
1339ede1e6f8SHighPoint Linux Team 	host->irq = pcidev->irq;
1340ede1e6f8SHighPoint Linux Team 
134100f59701SHighPoint Linux Team 	if (hba->ops->map_pci_bar(hba))
1342ede1e6f8SHighPoint Linux Team 		goto free_scsi_host;
1343ede1e6f8SHighPoint Linux Team 
134400f59701SHighPoint Linux Team 	if (hba->ops->iop_wait_ready(hba, 20000)) {
1345ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: firmware not ready\n",
1346ede1e6f8SHighPoint Linux Team 				hba->host->host_no);
1347ede1e6f8SHighPoint Linux Team 		goto unmap_pci_bar;
1348ede1e6f8SHighPoint Linux Team 	}
1349ede1e6f8SHighPoint Linux Team 
1350286aa031SHighPoint Linux Team 	if (hba->ops->family == MV_BASED_IOP) {
135100f59701SHighPoint Linux Team 		if (hba->ops->internal_memalloc(hba)) {
135200f59701SHighPoint Linux Team 			printk(KERN_ERR "scsi%d: internal_memalloc failed\n",
135300f59701SHighPoint Linux Team 				hba->host->host_no);
135400f59701SHighPoint Linux Team 			goto unmap_pci_bar;
135500f59701SHighPoint Linux Team 		}
135600f59701SHighPoint Linux Team 	}
135700f59701SHighPoint Linux Team 
135800f59701SHighPoint Linux Team 	if (hba->ops->get_config(hba, &iop_config)) {
1359ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: get config failed\n",
1360ede1e6f8SHighPoint Linux Team 				hba->host->host_no);
1361ede1e6f8SHighPoint Linux Team 		goto unmap_pci_bar;
1362ede1e6f8SHighPoint Linux Team 	}
1363ede1e6f8SHighPoint Linux Team 
1364ede1e6f8SHighPoint Linux Team 	hba->max_requests = min(le32_to_cpu(iop_config.max_requests),
1365ede1e6f8SHighPoint Linux Team 				HPTIOP_MAX_REQUESTS);
1366ede1e6f8SHighPoint Linux Team 	hba->max_devices = le32_to_cpu(iop_config.max_devices);
1367ede1e6f8SHighPoint Linux Team 	hba->max_request_size = le32_to_cpu(iop_config.request_size);
1368ede1e6f8SHighPoint Linux Team 	hba->max_sg_descriptors = le32_to_cpu(iop_config.max_sg_count);
1369ede1e6f8SHighPoint Linux Team 	hba->firmware_version = le32_to_cpu(iop_config.firmware_version);
1370db9b6e89SHighPoint Linux Team 	hba->interface_version = le32_to_cpu(iop_config.interface_version);
1371ede1e6f8SHighPoint Linux Team 	hba->sdram_size = le32_to_cpu(iop_config.sdram_size);
1372ede1e6f8SHighPoint Linux Team 
1373286aa031SHighPoint Linux Team 	if (hba->ops->family == MVFREY_BASED_IOP) {
1374286aa031SHighPoint Linux Team 		if (hba->ops->internal_memalloc(hba)) {
1375286aa031SHighPoint Linux Team 			printk(KERN_ERR "scsi%d: internal_memalloc failed\n",
1376286aa031SHighPoint Linux Team 				hba->host->host_no);
1377286aa031SHighPoint Linux Team 			goto unmap_pci_bar;
1378286aa031SHighPoint Linux Team 		}
1379286aa031SHighPoint Linux Team 		if (hba->ops->reset_comm(hba)) {
1380286aa031SHighPoint Linux Team 			printk(KERN_ERR "scsi%d: reset comm failed\n",
1381286aa031SHighPoint Linux Team 					hba->host->host_no);
1382286aa031SHighPoint Linux Team 			goto unmap_pci_bar;
1383286aa031SHighPoint Linux Team 		}
1384286aa031SHighPoint Linux Team 	}
1385286aa031SHighPoint Linux Team 
1386db9b6e89SHighPoint Linux Team 	if (hba->firmware_version > 0x01020000 ||
1387db9b6e89SHighPoint Linux Team 			hba->interface_version > 0x01020000)
1388db9b6e89SHighPoint Linux Team 		hba->iopintf_v2 = 1;
1389db9b6e89SHighPoint Linux Team 
1390ede1e6f8SHighPoint Linux Team 	host->max_sectors = le32_to_cpu(iop_config.data_transfer_length) >> 9;
1391ede1e6f8SHighPoint Linux Team 	host->max_id = le32_to_cpu(iop_config.max_devices);
1392ede1e6f8SHighPoint Linux Team 	host->sg_tablesize = le32_to_cpu(iop_config.max_sg_count);
1393ede1e6f8SHighPoint Linux Team 	host->can_queue = le32_to_cpu(iop_config.max_requests);
1394ede1e6f8SHighPoint Linux Team 	host->cmd_per_lun = le32_to_cpu(iop_config.max_requests);
1395ede1e6f8SHighPoint Linux Team 	host->max_cmd_len = 16;
1396ede1e6f8SHighPoint Linux Team 
1397*d67790ddSKees Cook 	req_size = struct_size_t(struct hpt_iop_request_scsi_command,
13985b12a568SGustavo A. R. Silva 				 sg_list, hba->max_sg_descriptors);
1399db9b6e89SHighPoint Linux Team 	if ((req_size & 0x1f) != 0)
1400db9b6e89SHighPoint Linux Team 		req_size = (req_size + 0x1f) & ~0x1f;
1401db9b6e89SHighPoint Linux Team 
1402db9b6e89SHighPoint Linux Team 	memset(&set_config, 0, sizeof(struct hpt_iop_request_set_config));
1403ede1e6f8SHighPoint Linux Team 	set_config.iop_id = cpu_to_le32(host->host_no);
1404db9b6e89SHighPoint Linux Team 	set_config.vbus_id = cpu_to_le16(host->host_no);
1405db9b6e89SHighPoint Linux Team 	set_config.max_host_request_size = cpu_to_le16(req_size);
1406ede1e6f8SHighPoint Linux Team 
140700f59701SHighPoint Linux Team 	if (hba->ops->set_config(hba, &set_config)) {
1408ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: set config failed\n",
1409ede1e6f8SHighPoint Linux Team 				hba->host->host_no);
1410ede1e6f8SHighPoint Linux Team 		goto unmap_pci_bar;
1411ede1e6f8SHighPoint Linux Team 	}
1412ede1e6f8SHighPoint Linux Team 
1413ede1e6f8SHighPoint Linux Team 	pci_set_drvdata(pcidev, host);
1414ede1e6f8SHighPoint Linux Team 
14151d6f359aSThomas Gleixner 	if (request_irq(pcidev->irq, hptiop_intr, IRQF_SHARED,
1416ede1e6f8SHighPoint Linux Team 					driver_name, hba)) {
1417ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: request irq %d failed\n",
1418ede1e6f8SHighPoint Linux Team 					hba->host->host_no, pcidev->irq);
14193e74051bSChristoph Hellwig 		goto unmap_pci_bar;
1420ede1e6f8SHighPoint Linux Team 	}
1421ede1e6f8SHighPoint Linux Team 
1422ede1e6f8SHighPoint Linux Team 	/* Allocate request mem */
1423ede1e6f8SHighPoint Linux Team 
1424ede1e6f8SHighPoint Linux Team 	dprintk("req_size=%d, max_requests=%d\n", req_size, hba->max_requests);
1425ede1e6f8SHighPoint Linux Team 
1426ede1e6f8SHighPoint Linux Team 	hba->req_size = req_size;
1427a93429c3Slinux 	hba->req_list = NULL;
1428a93429c3Slinux 
1429a93429c3Slinux 	for (i = 0; i < hba->max_requests; i++) {
1430ede1e6f8SHighPoint Linux Team 		start_virt = dma_alloc_coherent(&pcidev->dev,
1431a93429c3Slinux 					hba->req_size + 0x20,
1432ede1e6f8SHighPoint Linux Team 					&start_phy, GFP_KERNEL);
1433ede1e6f8SHighPoint Linux Team 
1434ede1e6f8SHighPoint Linux Team 		if (!start_virt) {
1435ede1e6f8SHighPoint Linux Team 			printk(KERN_ERR "scsi%d: fail to alloc request mem\n",
1436ede1e6f8SHighPoint Linux Team 						hba->host->host_no);
1437a93429c3Slinux 			goto free_request_mem;
1438ede1e6f8SHighPoint Linux Team 		}
1439ede1e6f8SHighPoint Linux Team 
1440a93429c3Slinux 		hba->dma_coherent[i] = start_virt;
1441a93429c3Slinux 		hba->dma_coherent_handle[i] = start_phy;
1442ede1e6f8SHighPoint Linux Team 
1443286aa031SHighPoint Linux Team 		if ((start_phy & 0x1f) != 0) {
1444ede1e6f8SHighPoint Linux Team 			offset = ((start_phy + 0x1f) & ~0x1f) - start_phy;
1445ede1e6f8SHighPoint Linux Team 			start_phy += offset;
1446ede1e6f8SHighPoint Linux Team 			start_virt += offset;
1447ede1e6f8SHighPoint Linux Team 		}
1448ede1e6f8SHighPoint Linux Team 
1449ede1e6f8SHighPoint Linux Team 		hba->reqs[i].next = NULL;
1450ede1e6f8SHighPoint Linux Team 		hba->reqs[i].req_virt = start_virt;
1451ede1e6f8SHighPoint Linux Team 		hba->reqs[i].req_shifted_phy = start_phy >> 5;
1452ede1e6f8SHighPoint Linux Team 		hba->reqs[i].index = i;
1453ede1e6f8SHighPoint Linux Team 		free_req(hba, &hba->reqs[i]);
1454ede1e6f8SHighPoint Linux Team 	}
1455ede1e6f8SHighPoint Linux Team 
1456ede1e6f8SHighPoint Linux Team 	/* Enable Interrupt and start background task */
1457ede1e6f8SHighPoint Linux Team 	if (hptiop_initialize_iop(hba))
1458ede1e6f8SHighPoint Linux Team 		goto free_request_mem;
1459ede1e6f8SHighPoint Linux Team 
14603e74051bSChristoph Hellwig 	if (scsi_add_host(host, &pcidev->dev)) {
14613e74051bSChristoph Hellwig 		printk(KERN_ERR "scsi%d: scsi_add_host failed\n",
14623e74051bSChristoph Hellwig 					hba->host->host_no);
14633e74051bSChristoph Hellwig 		goto free_request_mem;
14643e74051bSChristoph Hellwig 	}
14653e74051bSChristoph Hellwig 
1466ede1e6f8SHighPoint Linux Team 	scsi_scan_host(host);
1467ede1e6f8SHighPoint Linux Team 
1468ede1e6f8SHighPoint Linux Team 	dprintk("scsi%d: hptiop_probe successfully\n", hba->host->host_no);
1469ede1e6f8SHighPoint Linux Team 	return 0;
1470ede1e6f8SHighPoint Linux Team 
1471ede1e6f8SHighPoint Linux Team free_request_mem:
1472a93429c3Slinux 	for (i = 0; i < hba->max_requests; i++) {
1473a93429c3Slinux 		if (hba->dma_coherent[i] && hba->dma_coherent_handle[i])
1474ede1e6f8SHighPoint Linux Team 			dma_free_coherent(&hba->pcidev->dev,
1475a93429c3Slinux 					hba->req_size + 0x20,
1476a93429c3Slinux 					hba->dma_coherent[i],
1477a93429c3Slinux 					hba->dma_coherent_handle[i]);
1478a93429c3Slinux 		else
1479a93429c3Slinux 			break;
1480a93429c3Slinux 	}
1481ede1e6f8SHighPoint Linux Team 
1482ede1e6f8SHighPoint Linux Team 	free_irq(hba->pcidev->irq, hba);
1483ede1e6f8SHighPoint Linux Team 
1484ede1e6f8SHighPoint Linux Team unmap_pci_bar:
148500f59701SHighPoint Linux Team 	hba->ops->internal_memfree(hba);
1486ede1e6f8SHighPoint Linux Team 
148700f59701SHighPoint Linux Team 	hba->ops->unmap_pci_bar(hba);
1488ede1e6f8SHighPoint Linux Team 
1489ede1e6f8SHighPoint Linux Team free_scsi_host:
1490ede1e6f8SHighPoint Linux Team 	scsi_host_put(host);
1491ede1e6f8SHighPoint Linux Team 
149200f59701SHighPoint Linux Team free_pci_regions:
149300f59701SHighPoint Linux Team 	pci_release_regions(pcidev);
149400f59701SHighPoint Linux Team 
1495ede1e6f8SHighPoint Linux Team disable_pci_device:
1496ede1e6f8SHighPoint Linux Team 	pci_disable_device(pcidev);
1497ede1e6f8SHighPoint Linux Team 
14981db90ea2SJulia Lawall 	dprintk("scsi%d: hptiop_probe fail\n", host ? host->host_no : 0);
1499ede1e6f8SHighPoint Linux Team 	return -ENODEV;
1500ede1e6f8SHighPoint Linux Team }
1501ede1e6f8SHighPoint Linux Team 
hptiop_shutdown(struct pci_dev * pcidev)1502ede1e6f8SHighPoint Linux Team static void hptiop_shutdown(struct pci_dev *pcidev)
1503ede1e6f8SHighPoint Linux Team {
1504ede1e6f8SHighPoint Linux Team 	struct Scsi_Host *host = pci_get_drvdata(pcidev);
1505ede1e6f8SHighPoint Linux Team 	struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
1506ede1e6f8SHighPoint Linux Team 
1507ede1e6f8SHighPoint Linux Team 	dprintk("hptiop_shutdown(%p)\n", hba);
1508ede1e6f8SHighPoint Linux Team 
1509ede1e6f8SHighPoint Linux Team 	/* stop the iop */
1510ede1e6f8SHighPoint Linux Team 	if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_SHUTDOWN, 60000))
1511ede1e6f8SHighPoint Linux Team 		printk(KERN_ERR "scsi%d: shutdown the iop timeout\n",
1512ede1e6f8SHighPoint Linux Team 					hba->host->host_no);
1513ede1e6f8SHighPoint Linux Team 
1514ede1e6f8SHighPoint Linux Team 	/* disable all outbound interrupts */
151500f59701SHighPoint Linux Team 	hba->ops->disable_intr(hba);
151600f59701SHighPoint Linux Team }
151700f59701SHighPoint Linux Team 
hptiop_disable_intr_itl(struct hptiop_hba * hba)151800f59701SHighPoint Linux Team static void hptiop_disable_intr_itl(struct hptiop_hba *hba)
151900f59701SHighPoint Linux Team {
152000f59701SHighPoint Linux Team 	u32 int_mask;
152100f59701SHighPoint Linux Team 
152200f59701SHighPoint Linux Team 	int_mask = readl(&hba->u.itl.iop->outbound_intmask);
1523ede1e6f8SHighPoint Linux Team 	writel(int_mask |
1524ede1e6f8SHighPoint Linux Team 		IOPMU_OUTBOUND_INT_MSG0 | IOPMU_OUTBOUND_INT_POSTQUEUE,
152500f59701SHighPoint Linux Team 		&hba->u.itl.iop->outbound_intmask);
152600f59701SHighPoint Linux Team 	readl(&hba->u.itl.iop->outbound_intmask);
152700f59701SHighPoint Linux Team }
152800f59701SHighPoint Linux Team 
hptiop_disable_intr_mv(struct hptiop_hba * hba)152900f59701SHighPoint Linux Team static void hptiop_disable_intr_mv(struct hptiop_hba *hba)
153000f59701SHighPoint Linux Team {
153100f59701SHighPoint Linux Team 	writel(0, &hba->u.mv.regs->outbound_intmask);
153200f59701SHighPoint Linux Team 	readl(&hba->u.mv.regs->outbound_intmask);
1533ede1e6f8SHighPoint Linux Team }
1534ede1e6f8SHighPoint Linux Team 
hptiop_disable_intr_mvfrey(struct hptiop_hba * hba)1535286aa031SHighPoint Linux Team static void hptiop_disable_intr_mvfrey(struct hptiop_hba *hba)
1536286aa031SHighPoint Linux Team {
1537286aa031SHighPoint Linux Team 	writel(0, &(hba->u.mvfrey.mu->f0_doorbell_enable));
1538286aa031SHighPoint Linux Team 	readl(&(hba->u.mvfrey.mu->f0_doorbell_enable));
1539286aa031SHighPoint Linux Team 	writel(0, &(hba->u.mvfrey.mu->isr_enable));
1540286aa031SHighPoint Linux Team 	readl(&(hba->u.mvfrey.mu->isr_enable));
1541286aa031SHighPoint Linux Team 	writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
1542286aa031SHighPoint Linux Team 	readl(&(hba->u.mvfrey.mu->pcie_f0_int_enable));
1543286aa031SHighPoint Linux Team }
1544286aa031SHighPoint Linux Team 
hptiop_remove(struct pci_dev * pcidev)1545ede1e6f8SHighPoint Linux Team static void hptiop_remove(struct pci_dev *pcidev)
1546ede1e6f8SHighPoint Linux Team {
1547ede1e6f8SHighPoint Linux Team 	struct Scsi_Host *host = pci_get_drvdata(pcidev);
1548ede1e6f8SHighPoint Linux Team 	struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
1549a93429c3Slinux 	u32 i;
1550ede1e6f8SHighPoint Linux Team 
1551ede1e6f8SHighPoint Linux Team 	dprintk("scsi%d: hptiop_remove\n", hba->host->host_no);
1552ede1e6f8SHighPoint Linux Team 
15534f2ddba3SHighPoint Linux Team 	scsi_remove_host(host);
15544f2ddba3SHighPoint Linux Team 
1555ede1e6f8SHighPoint Linux Team 	hptiop_shutdown(pcidev);
1556ede1e6f8SHighPoint Linux Team 
1557ede1e6f8SHighPoint Linux Team 	free_irq(hba->pcidev->irq, hba);
1558ede1e6f8SHighPoint Linux Team 
1559a93429c3Slinux 	for (i = 0; i < hba->max_requests; i++) {
1560a93429c3Slinux 		if (hba->dma_coherent[i] && hba->dma_coherent_handle[i])
1561ede1e6f8SHighPoint Linux Team 			dma_free_coherent(&hba->pcidev->dev,
1562a93429c3Slinux 					hba->req_size + 0x20,
1563a93429c3Slinux 					hba->dma_coherent[i],
1564a93429c3Slinux 					hba->dma_coherent_handle[i]);
1565a93429c3Slinux 		else
1566a93429c3Slinux 			break;
1567a93429c3Slinux 	}
1568ede1e6f8SHighPoint Linux Team 
156900f59701SHighPoint Linux Team 	hba->ops->internal_memfree(hba);
157000f59701SHighPoint Linux Team 
157100f59701SHighPoint Linux Team 	hba->ops->unmap_pci_bar(hba);
1572ede1e6f8SHighPoint Linux Team 
1573ede1e6f8SHighPoint Linux Team 	pci_release_regions(hba->pcidev);
1574ede1e6f8SHighPoint Linux Team 	pci_set_drvdata(hba->pcidev, NULL);
1575ede1e6f8SHighPoint Linux Team 	pci_disable_device(hba->pcidev);
1576ede1e6f8SHighPoint Linux Team 
1577ede1e6f8SHighPoint Linux Team 	scsi_host_put(host);
1578ede1e6f8SHighPoint Linux Team }
1579ede1e6f8SHighPoint Linux Team 
158000f59701SHighPoint Linux Team static struct hptiop_adapter_ops hptiop_itl_ops = {
1581286aa031SHighPoint Linux Team 	.family            = INTEL_BASED_IOP,
158200f59701SHighPoint Linux Team 	.iop_wait_ready    = iop_wait_ready_itl,
1583286aa031SHighPoint Linux Team 	.internal_memalloc = hptiop_internal_memalloc_itl,
1584286aa031SHighPoint Linux Team 	.internal_memfree  = hptiop_internal_memfree_itl,
158500f59701SHighPoint Linux Team 	.map_pci_bar       = hptiop_map_pci_bar_itl,
158600f59701SHighPoint Linux Team 	.unmap_pci_bar     = hptiop_unmap_pci_bar_itl,
158700f59701SHighPoint Linux Team 	.enable_intr       = hptiop_enable_intr_itl,
158800f59701SHighPoint Linux Team 	.disable_intr      = hptiop_disable_intr_itl,
158900f59701SHighPoint Linux Team 	.get_config        = iop_get_config_itl,
159000f59701SHighPoint Linux Team 	.set_config        = iop_set_config_itl,
159100f59701SHighPoint Linux Team 	.iop_intr          = iop_intr_itl,
159200f59701SHighPoint Linux Team 	.post_msg          = hptiop_post_msg_itl,
159300f59701SHighPoint Linux Team 	.post_req          = hptiop_post_req_itl,
159423f0bb47SHighPoint Linux Team 	.hw_dma_bit_mask   = 64,
1595286aa031SHighPoint Linux Team 	.reset_comm        = hptiop_reset_comm_itl,
1596286aa031SHighPoint Linux Team 	.host_phy_flag     = cpu_to_le64(0),
159700f59701SHighPoint Linux Team };
159800f59701SHighPoint Linux Team 
159900f59701SHighPoint Linux Team static struct hptiop_adapter_ops hptiop_mv_ops = {
1600286aa031SHighPoint Linux Team 	.family            = MV_BASED_IOP,
160100f59701SHighPoint Linux Team 	.iop_wait_ready    = iop_wait_ready_mv,
160200f59701SHighPoint Linux Team 	.internal_memalloc = hptiop_internal_memalloc_mv,
160300f59701SHighPoint Linux Team 	.internal_memfree  = hptiop_internal_memfree_mv,
160400f59701SHighPoint Linux Team 	.map_pci_bar       = hptiop_map_pci_bar_mv,
160500f59701SHighPoint Linux Team 	.unmap_pci_bar     = hptiop_unmap_pci_bar_mv,
160600f59701SHighPoint Linux Team 	.enable_intr       = hptiop_enable_intr_mv,
160700f59701SHighPoint Linux Team 	.disable_intr      = hptiop_disable_intr_mv,
160800f59701SHighPoint Linux Team 	.get_config        = iop_get_config_mv,
160900f59701SHighPoint Linux Team 	.set_config        = iop_set_config_mv,
161000f59701SHighPoint Linux Team 	.iop_intr          = iop_intr_mv,
161100f59701SHighPoint Linux Team 	.post_msg          = hptiop_post_msg_mv,
161200f59701SHighPoint Linux Team 	.post_req          = hptiop_post_req_mv,
161323f0bb47SHighPoint Linux Team 	.hw_dma_bit_mask   = 33,
1614286aa031SHighPoint Linux Team 	.reset_comm        = hptiop_reset_comm_mv,
1615286aa031SHighPoint Linux Team 	.host_phy_flag     = cpu_to_le64(0),
1616286aa031SHighPoint Linux Team };
1617286aa031SHighPoint Linux Team 
1618286aa031SHighPoint Linux Team static struct hptiop_adapter_ops hptiop_mvfrey_ops = {
1619286aa031SHighPoint Linux Team 	.family            = MVFREY_BASED_IOP,
1620286aa031SHighPoint Linux Team 	.iop_wait_ready    = iop_wait_ready_mvfrey,
1621286aa031SHighPoint Linux Team 	.internal_memalloc = hptiop_internal_memalloc_mvfrey,
1622286aa031SHighPoint Linux Team 	.internal_memfree  = hptiop_internal_memfree_mvfrey,
1623286aa031SHighPoint Linux Team 	.map_pci_bar       = hptiop_map_pci_bar_mvfrey,
1624286aa031SHighPoint Linux Team 	.unmap_pci_bar     = hptiop_unmap_pci_bar_mvfrey,
1625286aa031SHighPoint Linux Team 	.enable_intr       = hptiop_enable_intr_mvfrey,
1626286aa031SHighPoint Linux Team 	.disable_intr      = hptiop_disable_intr_mvfrey,
1627286aa031SHighPoint Linux Team 	.get_config        = iop_get_config_mvfrey,
1628286aa031SHighPoint Linux Team 	.set_config        = iop_set_config_mvfrey,
1629286aa031SHighPoint Linux Team 	.iop_intr          = iop_intr_mvfrey,
1630286aa031SHighPoint Linux Team 	.post_msg          = hptiop_post_msg_mvfrey,
1631286aa031SHighPoint Linux Team 	.post_req          = hptiop_post_req_mvfrey,
1632286aa031SHighPoint Linux Team 	.hw_dma_bit_mask   = 64,
1633286aa031SHighPoint Linux Team 	.reset_comm        = hptiop_reset_comm_mvfrey,
1634286aa031SHighPoint Linux Team 	.host_phy_flag     = cpu_to_le64(1),
163500f59701SHighPoint Linux Team };
163600f59701SHighPoint Linux Team 
1637ede1e6f8SHighPoint Linux Team static struct pci_device_id hptiop_id_table[] = {
163800f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3220), (kernel_ulong_t)&hptiop_itl_ops },
163900f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3320), (kernel_ulong_t)&hptiop_itl_ops },
16403bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3410), (kernel_ulong_t)&hptiop_itl_ops },
164100f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3510), (kernel_ulong_t)&hptiop_itl_ops },
164200f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3511), (kernel_ulong_t)&hptiop_itl_ops },
16433bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3520), (kernel_ulong_t)&hptiop_itl_ops },
164400f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3521), (kernel_ulong_t)&hptiop_itl_ops },
164500f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3522), (kernel_ulong_t)&hptiop_itl_ops },
1646dd07428bSHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3530), (kernel_ulong_t)&hptiop_itl_ops },
16473bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3540), (kernel_ulong_t)&hptiop_itl_ops },
1648dd07428bSHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3560), (kernel_ulong_t)&hptiop_itl_ops },
1649dd07428bSHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4210), (kernel_ulong_t)&hptiop_itl_ops },
1650dd07428bSHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4211), (kernel_ulong_t)&hptiop_itl_ops },
1651dd07428bSHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4310), (kernel_ulong_t)&hptiop_itl_ops },
1652dd07428bSHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4311), (kernel_ulong_t)&hptiop_itl_ops },
16533bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4320), (kernel_ulong_t)&hptiop_itl_ops },
16543bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4321), (kernel_ulong_t)&hptiop_itl_ops },
16553bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4322), (kernel_ulong_t)&hptiop_itl_ops },
16563bfc13c2SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4400), (kernel_ulong_t)&hptiop_itl_ops },
165700f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3120), (kernel_ulong_t)&hptiop_mv_ops },
165800f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3122), (kernel_ulong_t)&hptiop_mv_ops },
165900f59701SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops },
1660286aa031SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4520), (kernel_ulong_t)&hptiop_mvfrey_ops },
1661286aa031SHighPoint Linux Team 	{ PCI_VDEVICE(TTI, 0x4522), (kernel_ulong_t)&hptiop_mvfrey_ops },
1662a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3610), (kernel_ulong_t)&hptiop_mvfrey_ops },
1663a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3611), (kernel_ulong_t)&hptiop_mvfrey_ops },
1664a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3620), (kernel_ulong_t)&hptiop_mvfrey_ops },
1665a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3622), (kernel_ulong_t)&hptiop_mvfrey_ops },
1666a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3640), (kernel_ulong_t)&hptiop_mvfrey_ops },
1667a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3660), (kernel_ulong_t)&hptiop_mvfrey_ops },
1668a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3680), (kernel_ulong_t)&hptiop_mvfrey_ops },
1669a93429c3Slinux 	{ PCI_VDEVICE(TTI, 0x3690), (kernel_ulong_t)&hptiop_mvfrey_ops },
1670ede1e6f8SHighPoint Linux Team 	{},
1671ede1e6f8SHighPoint Linux Team };
1672ede1e6f8SHighPoint Linux Team 
1673ede1e6f8SHighPoint Linux Team MODULE_DEVICE_TABLE(pci, hptiop_id_table);
1674ede1e6f8SHighPoint Linux Team 
1675ede1e6f8SHighPoint Linux Team static struct pci_driver hptiop_pci_driver = {
1676ede1e6f8SHighPoint Linux Team 	.name       = driver_name,
1677ede1e6f8SHighPoint Linux Team 	.id_table   = hptiop_id_table,
1678ede1e6f8SHighPoint Linux Team 	.probe      = hptiop_probe,
1679ede1e6f8SHighPoint Linux Team 	.remove     = hptiop_remove,
1680ede1e6f8SHighPoint Linux Team 	.shutdown   = hptiop_shutdown,
1681ede1e6f8SHighPoint Linux Team };
1682ede1e6f8SHighPoint Linux Team 
hptiop_module_init(void)1683ede1e6f8SHighPoint Linux Team static int __init hptiop_module_init(void)
1684ede1e6f8SHighPoint Linux Team {
1685ede1e6f8SHighPoint Linux Team 	printk(KERN_INFO "%s %s\n", driver_name_long, driver_ver);
16863e74051bSChristoph Hellwig 	return pci_register_driver(&hptiop_pci_driver);
1687ede1e6f8SHighPoint Linux Team }
1688ede1e6f8SHighPoint Linux Team 
hptiop_module_exit(void)1689ede1e6f8SHighPoint Linux Team static void __exit hptiop_module_exit(void)
1690ede1e6f8SHighPoint Linux Team {
1691ede1e6f8SHighPoint Linux Team 	pci_unregister_driver(&hptiop_pci_driver);
1692ede1e6f8SHighPoint Linux Team }
1693ede1e6f8SHighPoint Linux Team 
1694ede1e6f8SHighPoint Linux Team 
1695ede1e6f8SHighPoint Linux Team module_init(hptiop_module_init);
1696ede1e6f8SHighPoint Linux Team module_exit(hptiop_module_exit);
1697ede1e6f8SHighPoint Linux Team 
1698ede1e6f8SHighPoint Linux Team MODULE_LICENSE("GPL");
1699db9b6e89SHighPoint Linux Team 
1700