summaryrefslogtreecommitdiff
path: root/drivers/block/mxc_ata.c
blob: 842a63309d59734c34376d5e9d00c3da340d4878 (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
/*
 * Freescale iMX51 ATA driver
 *
 * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
 *
 * Based on code by:
 *	Mahesh Mahadevan <mahesh.mahadevan@freescale.com>
 *
 * Based on code from original FSL ATA driver, which is
 * part of eCos, the Embedded Configurable Operating System.
 * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
 *
 * 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; either version 2 of
 * the License, or (at your option) any later version.
 *
 * 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
 */

#include <common.h>
#include <command.h>
#include <config.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <ide.h>

#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>

/* MXC ATA register offsets */
struct mxc_ata_config_regs {
	u8	time_off;	/* 0x00 */
	u8	time_on;
	u8	time_1;
	u8	time_2w;
	u8	time_2r;
	u8	time_ax;
	u8	time_pio_rdx;
	u8	time_4;
	u8	time_9;
	u8	time_m;
	u8	time_jn;
	u8	time_d;
	u8	time_k;
	u8	time_ack;
	u8	time_env;
	u8	time_udma_rdx;
	u8	time_zah;	/* 0x10 */
	u8	time_mlix;
	u8	time_dvh;
	u8	time_dzfs;
	u8	time_dvs;
	u8	time_cvh;
	u8	time_ss;
	u8	time_cyc;
	u32	fifo_data_32;	/* 0x18 */
	u32	fifo_data_16;
	u32	fifo_fill;
	u32	ata_control;
	u32	interrupt_pending;
	u32	interrupt_enable;
	u32	interrupt_clear;
	u32	fifo_alarm;
};

struct mxc_data_hdd_regs {
	u32	drive_data;	/* 0xa0 */
	u32	drive_features;
	u32	drive_sector_count;
	u32	drive_sector_num;
	u32	drive_cyl_low;
	u32	drive_cyl_high;
	u32	drive_dev_head;
	u32	command;
	u32	status;
	u32	alt_status;
};

/* PIO timing table */
#define	NR_PIO_SPECS	5
static uint16_t pio_t0[NR_PIO_SPECS]	= { 600, 383, 240, 180, 120 };
static uint16_t pio_t1[NR_PIO_SPECS]	= { 70,  50,  30,  30,  25 };
static uint16_t pio_t2_8[NR_PIO_SPECS]	= { 290, 290, 290, 80,  70 };
static uint16_t pio_t2_16[NR_PIO_SPECS]	= { 165, 125, 100, 80,  70 };
static uint16_t pio_t2i[NR_PIO_SPECS]	= { 40,  0,   0,   0,   0 };
static uint16_t pio_t4[NR_PIO_SPECS]	= { 30,  20,  15,  10,  10 };
static uint16_t pio_t9[NR_PIO_SPECS]	= { 20,  15,  10,  10,  10 };
static uint16_t pio_tA[NR_PIO_SPECS]	= { 50,  50,  50,  50,  50 };

#define	REG2OFF(reg)	((((uint32_t)reg) & 0x3) * 8)
static void set_ata_bus_timing(unsigned char mode)
{
	uint32_t val;
	uint32_t T = 1000000000 / mxc_get_clock(MXC_IPG_CLK);

	struct mxc_ata_config_regs *ata_regs;
	ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR;

	if (mode >= NR_PIO_SPECS)
		return;

	/* Write TIME_OFF/ON/1/2W */
	val =	(3 << REG2OFF(&ata_regs->time_off)) |
		(3 << REG2OFF(&ata_regs->time_on)) |
		(((pio_t1[mode] + T) / T) << REG2OFF(&ata_regs->time_1)) |
		(((pio_t2_8[mode] + T) / T) << REG2OFF(&ata_regs->time_2w));
	writel(val, &ata_regs->time_off);

	/* Write TIME_2R/AX/RDX/4 */
	val =	(((pio_t2_8[mode] + T) / T) << REG2OFF(&ata_regs->time_2r)) |
		(((pio_tA[mode] + T) / T + 2) << REG2OFF(&ata_regs->time_ax)) |
		(1 << REG2OFF(&ata_regs->time_pio_rdx)) |
		(((pio_t4[mode] + T) / T) << REG2OFF(&ata_regs->time_4));
	writel(val, &ata_regs->time_2r);

	/* Write TIME_9 ; the rest of timing registers is irrelevant for PIO */
	val =	(((pio_t9[mode] + T) / T) << REG2OFF(&ata_regs->time_9));
	writel(val, &ata_regs->time_9);
}

int ide_preinit(void)
{
	struct mxc_ata_config_regs *ata_regs;
	ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR;

	/* 46.3.3.4 @ FSL iMX51 manual */
	/* FIFO normal op., drive reset */
	writel(0x80, &ata_regs->ata_control);
	/* FIFO normal op., drive not reset */
	writel(0xc0, &ata_regs->ata_control);

	/* Configure the PIO timing */
	set_ata_bus_timing(CONFIG_MXC_ATA_PIO_MODE);

	/* 46.3.3.4 @ FSL iMX51 manual */
	/* Drive not reset, IORDY handshake */
	writel(0x41, &ata_regs->ata_control);

	return 0;
}