summaryrefslogtreecommitdiff
path: root/drivers/scsi/mvsas/mv_sas.h
blob: d6b19dc80bee522a934bb1bb64dd56c1ecad0c51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/*
 * Marvell 88SE64xx/88SE94xx main function head file
 *
 * Copyright 2007 Red Hat, Inc.
 * Copyright 2008 Marvell. <kewei@marvell.com>
 * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
 *
 * This file is licensed under GPLv2.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
*/

#ifndef _MV_SAS_H_
#define _MV_SAS_H_

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/unaligned.h>
#include <scsi/libsas.h>
#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/sas_ata.h>
#include "mv_defs.h"

#define DRV_NAME		"mvsas"
#define DRV_VERSION		"0.8.16"
#define MVS_ID_NOT_MAPPED	0x7f
#define WIDE_PORT_MAX_PHY		4
#define mv_printk(fmt, arg ...)	\
	printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
#ifdef MV_DEBUG
#define mv_dprintk(format, arg...)	\
	printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg)
#else
#define mv_dprintk(format, arg...)
#endif
#define MV_MAX_U32			0xffffffff

extern int interrupt_coalescing;
extern struct mvs_tgt_initiator mvs_tgt;
extern struct mvs_info *tgt_mvi;
extern const struct mvs_dispatch mvs_64xx_dispatch;
extern const struct mvs_dispatch mvs_94xx_dispatch;
extern struct kmem_cache *mvs_task_list_cache;

#define DEV_IS_EXPANDER(type)	\
	((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE))

#define bit(n) ((u64)1 << n)

#define for_each_phy(__lseq_mask, __mc, __lseq)			\
	for ((__mc) = (__lseq_mask), (__lseq) = 0;		\
					(__mc) != 0 ;		\
					(++__lseq), (__mc) >>= 1)

#define MVS_PHY_ID (1U << sas_phy->id)
#define MV_INIT_DELAYED_WORK(w, f, d)	INIT_DELAYED_WORK(w, f)
#define UNASSOC_D2H_FIS(id)		\
	((void *) mvi->rx_fis + 0x100 * id)
#define SATA_RECEIVED_FIS_LIST(reg_set)	\
	((void *) mvi->rx_fis + mvi->chip->fis_offs + 0x100 * reg_set)
#define SATA_RECEIVED_SDB_FIS(reg_set)	\
	(SATA_RECEIVED_FIS_LIST(reg_set) + 0x58)
#define SATA_RECEIVED_D2H_FIS(reg_set)	\
	(SATA_RECEIVED_FIS_LIST(reg_set) + 0x40)
#define SATA_RECEIVED_PIO_FIS(reg_set)	\
	(SATA_RECEIVED_FIS_LIST(reg_set) + 0x20)
#define SATA_RECEIVED_DMA_FIS(reg_set)	\
	(SATA_RECEIVED_FIS_LIST(reg_set) + 0x00)

enum dev_status {
	MVS_DEV_NORMAL = 0x0,
	MVS_DEV_EH	= 0x1,
};

enum dev_reset {
	MVS_SOFT_RESET	= 0,
	MVS_HARD_RESET	= 1,
	MVS_PHY_TUNE	= 2,
};

struct mvs_info;

struct mvs_dispatch {
	char *name;
	int (*chip_init)(struct mvs_info *mvi);
	int (*spi_init)(struct mvs_info *mvi);
	int (*chip_ioremap)(struct mvs_info *mvi);
	void (*chip_iounmap)(struct mvs_info *mvi);
	irqreturn_t (*isr)(struct mvs_info *mvi, int irq, u32 stat);
	u32 (*isr_status)(struct mvs_info *mvi, int irq);
	void (*interrupt_enable)(struct mvs_info *mvi);
	void (*interrupt_disable)(struct mvs_info *mvi);

	u32 (*read_phy_ctl)(struct mvs_info *mvi, u32 port);
	void (*write_phy_ctl)(struct mvs_info *mvi, u32 port, u32 val);

	u32 (*read_port_cfg_data)(struct mvs_info *mvi, u32 port);
	void (*write_port_cfg_data)(struct mvs_info *mvi, u32 port, u32 val);
	void (*write_port_cfg_addr)(struct mvs_info *mvi, u32 port, u32 addr);

	u32 (*read_port_vsr_data)(struct mvs_info *mvi, u32 port);
	void (*write_port_vsr_data)(struct mvs_info *mvi, u32 port, u32 val);
	void (*write_port_vsr_addr)(struct mvs_info *mvi, u32 port, u32 addr);

	u32 (*read_port_irq_stat)(struct mvs_info *mvi, u32 port);
	void (*write_port_irq_stat)(struct mvs_info *mvi, u32 port, u32 val);

	u32 (*read_port_irq_mask)(struct mvs_info *mvi, u32 port);
	void (*write_port_irq_mask)(struct mvs_info *mvi, u32 port, u32 val);

	void (*command_active)(struct mvs_info *mvi, u32 slot_idx);
	void (*clear_srs_irq)(struct mvs_info *mvi, u8 reg_set, u8 clear_all);
	void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type,
				u32 tfs);
	void (*start_delivery)(struct mvs_info *mvi, u32 tx);
	u32 (*rx_update)(struct mvs_info *mvi);
	void (*int_full)(struct mvs_info *mvi);
	u8 (*assign_reg_set)(struct mvs_info *mvi, u8 *tfs);
	void (*free_reg_set)(struct mvs_info *mvi, u8 *tfs);
	u32 (*prd_size)(void);
	u32 (*prd_count)(void);
	void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
	void (*detect_porttype)(struct mvs_info *mvi, int i);
	int (*oob_done)(struct mvs_info *mvi, int i);
	void (*fix_phy_info)(struct mvs_info *mvi, int i,
				struct sas_identify_frame *id);
	void (*phy_work_around)(struct mvs_info *mvi, int i);
	void (*phy_set_link_rate)(struct mvs_info *mvi, u32 phy_id,
				struct sas_phy_linkrates *rates);
	u32 (*phy_max_link_rate)(void);
	void (*phy_disable)(struct mvs_info *mvi, u32 phy_id);
	void (*phy_enable)(struct mvs_info *mvi, u32 phy_id);
	void (*phy_reset)(struct mvs_info *mvi, u32 phy_id, int hard);
	void (*stp_reset)(struct mvs_info *mvi, u32 phy_id);
	void (*clear_active_cmds)(struct mvs_info *mvi);
	u32 (*spi_read_data)(struct mvs_info *mvi);
	void (*spi_write_data)(struct mvs_info *mvi, u32 data);
	int (*spi_buildcmd)(struct mvs_info *mvi,
						u32      *dwCmd,
						u8       cmd,
						u8       read,
						u8       length,
						u32      addr
						);
	int (*spi_issuecmd)(struct mvs_info *mvi, u32 cmd);
	int (*spi_waitdataready)(struct mvs_info *mvi, u32 timeout);
	void (*dma_fix)(struct mvs_info *mvi, u32 phy_mask,
				int buf_len, int from, void *prd);
	void (*tune_interrupt)(struct mvs_info *mvi, u32 time);
	void (*non_spec_ncq_error)(struct mvs_info *mvi);

};

struct mvs_chip_info {
	u32 		n_host;
	u32 		n_phy;
	u32 		fis_offs;
	u32 		fis_count;
	u32 		srs_sz;
	u32		sg_width;
	u32 		slot_width;
	const struct mvs_dispatch *dispatch;
};
#define MVS_MAX_SG		(1U << mvi->chip->sg_width)
#define MVS_CHIP_SLOT_SZ	(1U << mvi->chip->slot_width)
#define MVS_RX_FISL_SZ		\
	(mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))
#define MVS_CHIP_DISP		(mvi->chip->dispatch)

struct mvs_err_info {
	__le32			flags;
	__le32			flags2;
};

struct mvs_cmd_hdr {
	__le32			flags;	/* PRD tbl len; SAS, SATA ctl */
	__le32			lens;	/* cmd, max resp frame len */
	__le32			tags;	/* targ port xfer tag; tag */
	__le32			data_len;	/* data xfer len */
	__le64			cmd_tbl;  	/* command table address */
	__le64			open_frame;	/* open addr frame address */
	__le64			status_buf;	/* status buffer address */
	__le64			prd_tbl;		/* PRD tbl address */
	__le32			reserved[4];
};

struct mvs_port {
	struct asd_sas_port	sas_port;
	u8			port_attached;
	u8			wide_port_phymap;
	struct list_head	list;
};

struct mvs_phy {
	struct mvs_info 		*mvi;
	struct mvs_port		*port;
	struct asd_sas_phy	sas_phy;
	struct sas_identify	identify;
	struct scsi_device	*sdev;
	struct timer_list timer;
	u64		dev_sas_addr;
	u64		att_dev_sas_addr;
	u32		att_dev_info;
	u32		dev_info;
	u32		phy_type;
	u32		phy_status;
	u32		irq_status;
	u32		frame_rcvd_size;
	u8		frame_rcvd[32];
	u8		phy_attached;
	u8		phy_mode;
	u8		reserved[2];
	u32		phy_event;
	enum sas_linkrate	minimum_linkrate;
	enum sas_linkrate	maximum_linkrate;
};

struct mvs_device {
	struct list_head		dev_entry;
	enum sas_device_type dev_type;
	struct mvs_info *mvi_info;
	struct domain_device *sas_device;
	struct timer_list timer;
	u32 attached_phy;
	u32 device_id;
	u32 running_req;
	u8 taskfileset;
	u8 dev_status;
	u16 reserved;
};

/* Generate  PHY tunning parameters */
struct phy_tuning {
	/* 1 bit,  transmitter emphasis enable	*/
	u8	trans_emp_en:1;
	/* 4 bits, transmitter emphasis amplitude */
	u8	trans_emp_amp:4;
	/* 3 bits, reserved space */
	u8	Reserved_2bit_1:3;
	/* 5 bits, transmitter amplitude */
	u8	trans_amp:5;
	/* 2 bits, transmitter amplitude adjust */
	u8	trans_amp_adj:2;
	/* 1 bit, reserved space */
	u8	resv_2bit_2:1;
	/* 2 bytes, reserved space */
	u8	reserved[2];
};

struct ffe_control {
	/* 4 bits,  FFE Capacitor Select  (value range 0~F)  */
	u8 ffe_cap_sel:4;
	/* 3 bits,  FFE Resistor Select (value range 0~7) */
	u8 ffe_rss_sel:3;
	/* 1 bit reserve*/
	u8 reserved:1;
};

/*
 * HBA_Info_Page is saved in Flash/NVRAM, total 256 bytes.
 * The data area is valid only Signature="MRVL".
 * If any member fills with 0xFF, the member is invalid.
 */
struct hba_info_page {
	/* Dword 0 */
	/* 4 bytes, structure signature,should be "MRVL" at first initial */
	u8 signature[4];

	/* Dword 1-13 */
	u32 reserved1[13];

	/* Dword 14-29 */
	/* 64 bytes, SAS address for each port */
	u64 sas_addr[8];

	/* Dword 30-31 */
	/* 8 bytes for vanir 8 port PHY FFE seeting
	 * BIT 0~3 : FFE Capacitor select(value range 0~F)
	 * BIT 4~6 : FFE Resistor select(value range 0~7)
	 * BIT 7: reserve.
	 */

	struct ffe_control  ffe_ctl[8];
	/* Dword 32 -43 */
	u32 reserved2[12];

	/* Dword 44-45 */
	/* 8 bytes,  0:  1.5G, 1: 3.0G, should be 0x01 at first initial */
	u8 phy_rate[8];

	/* Dword 46-53 */
	/* 32 bytes, PHY tuning parameters for each PHY*/
	struct phy_tuning   phy_tuning[8];

	/* Dword 54-63 */
	u32 reserved3[10];
};	/* total 256 bytes */

struct mvs_slot_info {
	struct list_head entry;
	union {
		struct sas_task *task;
		void *tdata;
	};
	u32 n_elem;
	u32 tx;
	u32 slot_tag;

	/* DMA buffer for storing cmd tbl, open addr frame, status buffer,
	 * and PRD table
	 */
	void *buf;
	dma_addr_t buf_dma;
	void *response;
	struct mvs_port *port;
	struct mvs_device	*device;
	void *open_frame;
};

struct mvs_info {
	unsigned long flags;

	/* host-wide lock */
	spinlock_t lock;

	/* our device */
	struct pci_dev *pdev;
	struct device *dev;

	/* enhanced mode registers */
	void __iomem *regs;

	/* peripheral or soc registers */
	void __iomem *regs_ex;
	u8 sas_addr[SAS_ADDR_SIZE];

	/* SCSI/SAS glue */
	struct sas_ha_struct *sas;
	struct Scsi_Host *shost;

	/* TX (delivery) DMA ring */
	__le32 *tx;
	dma_addr_t tx_dma;

	/* cached next-producer idx */
	u32 tx_prod;

	/* RX (completion) DMA ring */
	__le32	*rx;
	dma_addr_t rx_dma;

	/* RX consumer idx */
	u32 rx_cons;

	/* RX'd FIS area */
	__le32 *rx_fis;
	dma_addr_t rx_fis_dma;

	/* DMA command header slots */
	struct mvs_cmd_hdr *slot;
	dma_addr_t slot_dma;

	u32 chip_id;
	const struct mvs_chip_info *chip;

	int tags_num;
	unsigned long *tags;
	/* further per-slot information */
	struct mvs_phy phy[MVS_MAX_PHYS];
	struct mvs_port port[MVS_MAX_PHYS];
	u32 id;
	u64 sata_reg_set;
	struct list_head *hba_list;
	struct list_head soc_entry;
	struct list_head wq_list;
	unsigned long instance;
	u16 flashid;
	u32 flashsize;
	u32 flashsectSize;

	void *addon;
	struct hba_info_page hba_info_param;
	struct mvs_device	devices[MVS_MAX_DEVICES];
	void *bulk_buffer;
	dma_addr_t bulk_buffer_dma;
	void *bulk_buffer1;
	dma_addr_t bulk_buffer_dma1;
#define TRASH_BUCKET_SIZE    	0x20000
	void *dma_pool;
	struct mvs_slot_info slot_info[0];
};

struct mvs_prv_info{
	u8 n_host;
	u8 n_phy;
	u8 scan_finished;
	u8 reserve;
	struct mvs_info *mvi[2];
	struct tasklet_struct mv_tasklet;
};

struct mvs_wq {
	struct delayed_work work_q;
	struct mvs_info *mvi;
	void *data;
	int handler;
	struct list_head entry;
};

struct mvs_task_exec_info {
	struct sas_task *task;
	struct mvs_cmd_hdr *hdr;
	struct mvs_port *port;
	u32 tag;
	int n_elem;
};

struct mvs_task_list {
	struct sas_task *task;
	struct list_head list;
};


/******************** function prototype *********************/
void mvs_get_sas_addr(void *buf, u32 buflen);
void mvs_tag_clear(struct mvs_info *mvi, u32 tag);
void mvs_tag_free(struct mvs_info *mvi, u32 tag);
void mvs_tag_set(struct mvs_info *mvi, unsigned int tag);
int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out);
void mvs_tag_init(struct mvs_info *mvi);
void mvs_iounmap(void __iomem *regs);
int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex);
void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard);
int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
			void *funcdata);
void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo,
		      u32 off_hi, u64 sas_addr);
void mvs_scan_start(struct Scsi_Host *shost);
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
int mvs_queue_command(struct sas_task *task, const int num,
			gfp_t gfp_flags);
int mvs_abort_task(struct sas_task *task);
int mvs_abort_task_set(struct domain_device *dev, u8 *lun);
int mvs_clear_aca(struct domain_device *dev, u8 *lun);
int mvs_clear_task_set(struct domain_device *dev, u8 * lun);
void mvs_port_formed(struct asd_sas_phy *sas_phy);
void mvs_port_deformed(struct asd_sas_phy *sas_phy);
int mvs_dev_found(struct domain_device *dev);
void mvs_dev_gone(struct domain_device *dev);
int mvs_lu_reset(struct domain_device *dev, u8 *lun);
int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags);
int mvs_I_T_nexus_reset(struct domain_device *dev);
int mvs_query_task(struct sas_task *task);
void mvs_release_task(struct mvs_info *mvi,
			struct domain_device *dev);
void mvs_do_release_task(struct mvs_info *mvi, int phy_no,
			struct domain_device *dev);
void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi, u8 reg_set);
#endif