summaryrefslogtreecommitdiff
path: root/drivers/ata/mvsata_ide.c
blob: 41f9a91617d4ec7a51dc2624685130e288a133fc (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2010 Albert ARIBAUD <albert.u.boot@aribaud.net>
 *
 * Written-by: Albert ARIBAUD <albert.u.boot@aribaud.net>
 */

#include <common.h>
#include <asm/io.h>
#include <linux/delay.h>

#if defined(CONFIG_ARCH_ORION5X)
#include <asm/arch/orion5x.h>
#elif defined(CONFIG_ARCH_KIRKWOOD)
#include <asm/arch/soc.h>
#elif defined(CONFIG_ARCH_MVEBU)
#include <linux/mbus.h>
#endif

/* SATA port registers */
struct mvsata_port_registers {
	u32 reserved0[10];
	u32 edma_cmd;
	u32 reserved1[181];
	/* offset 0x300 : ATA Interface registers */
	u32 sstatus;
	u32 serror;
	u32 scontrol;
	u32 ltmode;
	u32 phymode3;
	u32 phymode4;
	u32 reserved2[5];
	u32 phymode1;
	u32 phymode2;
	u32 bist_cr;
	u32 bist_dw1;
	u32 bist_dw2;
	u32 serrorintrmask;
};

/*
 * Sanity checks:
 * - to compile at all, we need CONFIG_SYS_ATA_BASE_ADDR.
 * - for ide_preinit to make sense, we need at least one of
 *   CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET;
 * - for ide_preinit to be called, we need CONFIG_IDE_PREINIT.
 * Fail with an explanation message if these conditions are not met.
 * This is particularly important for CONFIG_IDE_PREINIT, because
 * its lack would not cause a build error.
 */

#if !defined(CONFIG_SYS_ATA_BASE_ADDR)
#error CONFIG_SYS_ATA_BASE_ADDR must be defined
#elif !defined(CONFIG_SYS_ATA_IDE0_OFFSET) \
   && !defined(CONFIG_SYS_ATA_IDE1_OFFSET)
#error CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET \
   must be defined
#elif !defined(CONFIG_IDE_PREINIT)
#error CONFIG_IDE_PREINIT must be defined
#endif

/*
 * Masks and values for SControl DETection and Interface Power Management,
 * and for SStatus DETection.
 */

#define MVSATA_EDMA_CMD_ATA_RST		0x00000004
#define MVSATA_SCONTROL_DET_MASK		0x0000000F
#define MVSATA_SCONTROL_DET_NONE		0x00000000
#define MVSATA_SCONTROL_DET_INIT		0x00000001
#define MVSATA_SCONTROL_IPM_MASK		0x00000F00
#define MVSATA_SCONTROL_IPM_NO_LP_ALLOWED	0x00000300
#define MVSATA_SCONTROL_MASK \
	(MVSATA_SCONTROL_DET_MASK|MVSATA_SCONTROL_IPM_MASK)
#define MVSATA_PORT_INIT \
	(MVSATA_SCONTROL_DET_INIT|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
#define MVSATA_PORT_USE \
	(MVSATA_SCONTROL_DET_NONE|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
#define MVSATA_SSTATUS_DET_MASK			0x0000000F
#define MVSATA_SSTATUS_DET_DEVCOMM		0x00000003

/*
 * Status codes to return to client callers. Currently, callers ignore
 * exact value and only care for zero or nonzero, so no need to make this
 * public, it is only #define'd for clarity.
 * If/when standard negative codes are implemented in U-Boot, then these
 * #defines should be moved to, or replaced by ones from, the common list
 * of status codes.
 */

#define MVSATA_STATUS_OK	0
#define MVSATA_STATUS_TIMEOUT	-1

/*
 * Registers for SATA MBUS memory windows
 */

#define MVSATA_WIN_CONTROL(w)	(MVEBU_AXP_SATA_BASE + 0x30 + ((w) << 4))
#define MVSATA_WIN_BASE(w)	(MVEBU_AXP_SATA_BASE + 0x34 + ((w) << 4))

/*
 * Initialize SATA memory windows for Armada XP
 */

#ifdef CONFIG_ARCH_MVEBU
static void mvsata_ide_conf_mbus_windows(void)
{
	const struct mbus_dram_target_info *dram;
	int i;

	dram = mvebu_mbus_dram_info();

	/* Disable windows, Set Size/Base to 0  */
	for (i = 0; i < 4; i++) {
		writel(0, MVSATA_WIN_CONTROL(i));
		writel(0, MVSATA_WIN_BASE(i));
	}

	for (i = 0; i < dram->num_cs; i++) {
		const struct mbus_dram_window *cs = dram->cs + i;
		writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
				(dram->mbus_dram_target_id << 4) | 1,
				MVSATA_WIN_CONTROL(i));
		writel(cs->base & 0xffff0000, MVSATA_WIN_BASE(i));
	}
}
#endif

/*
 * Initialize one MVSATAHC port: set SControl's IPM to "always active"
 * and DET to "reset", then wait for SStatus's DET to become "device and
 * comm ok" (or time out after 50 us if no device), then set SControl's
 * DET back to "no action".
 */

static int mvsata_ide_initialize_port(struct mvsata_port_registers *port)
{
	u32 control;
	u32 status;
	u32 timeleft = 10000; /* wait at most 10 ms for SATA reset to complete */

	/* Hard reset */
	writel(MVSATA_EDMA_CMD_ATA_RST, &port->edma_cmd);
	udelay(25); /* taken from original marvell port */
	writel(0, &port->edma_cmd);

	/* Set control IPM to 3 (no low power) and DET to 1 (initialize) */
	control = readl(&port->scontrol);
	control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_INIT;
	writel(control, &port->scontrol);
	/* Toggle control DET back to 0 (normal operation) */
	control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_USE;
	writel(control, &port->scontrol);
	/* wait for status DET to become 3 (device and communication OK) */
	while (--timeleft) {
		status = readl(&port->sstatus) & MVSATA_SSTATUS_DET_MASK;
		if (status == MVSATA_SSTATUS_DET_DEVCOMM)
			break;
		udelay(1);
	}
	/* return success or time-out error depending on time left */
	if (!timeleft)
		return MVSATA_STATUS_TIMEOUT;
	return MVSATA_STATUS_OK;
}

/*
 * ide_preinit() will be called by ide_init in cmd_ide.c and will
 * reset the MVSTATHC ports needed by the board.
 */

int ide_preinit(void)
{
	int ret = MVSATA_STATUS_TIMEOUT;
	int status;

#ifdef CONFIG_ARCH_MVEBU
	mvsata_ide_conf_mbus_windows();
#endif

	/* Enable ATA port 0 (could be SATA port 0 or 1) if declared */
#if defined(CONFIG_SYS_ATA_IDE0_OFFSET)
	status = mvsata_ide_initialize_port(
		(struct mvsata_port_registers *)
		(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE0_OFFSET));
	if (status == MVSATA_STATUS_OK)
		ret = MVSATA_STATUS_OK;
#endif
	/* Enable ATA port 1 (could be SATA port 0 or 1) if declared */
#if defined(CONFIG_SYS_ATA_IDE1_OFFSET)
	status = mvsata_ide_initialize_port(
		(struct mvsata_port_registers *)
		(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE1_OFFSET));
	if (status == MVSATA_STATUS_OK)
		ret = MVSATA_STATUS_OK;
#endif
	/* Return success if at least one port initialization succeeded */
	return ret;
}