Add sb800 spi support.
[coreboot.git] / src / southbridge / amd / cimx / sb800 / spi.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2012 Advanced Micro Devices, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  */
19 #include <console/console.h>
20 #include <arch/io.h>
21 #include <device/device.h>
22 #include "SBPLATFORM.h"
23
24
25 void execute_command(volatile u8 * spi_address)
26 {
27         *(spi_address + 2) |= 1;
28 }
29
30 void wait4command_complete(volatile u8 * spi_address)
31 {
32         while (*(spi_address + 2) & 1)
33                 printk(BIOS_DEBUG, "wait4CommandComplete\n");
34 }
35
36 void reset_internal_fifo_pointer(volatile u8 * spi_address)
37 {
38         u8 val;
39
40         do {
41                 *(spi_address + 2) |= 0x10;
42                 val = *(spi_address + 0xd);
43         } while (val & 0x7);
44 }
45
46 u8 read_spi_status(volatile u8 * spi_address)
47 {
48         u8 val;
49         *spi_address = 0x05;
50         *(spi_address + 1) = 0x11;
51         reset_internal_fifo_pointer(spi_address);
52         *(spi_address + 0xC) = 0x0;     /* dummy */
53         reset_internal_fifo_pointer(spi_address);
54         execute_command(spi_address);
55         wait4command_complete(spi_address);
56         reset_internal_fifo_pointer(spi_address);
57         val = *(spi_address + 0xC);
58         val = *(spi_address + 0xC);
59         return val;
60 }
61
62 void wait4flashpart_ready(volatile u8 * spi_address)
63 {
64         while (read_spi_status(spi_address) & 1) ;
65 }
66
67 void write_spi_status(volatile u8 * spi_address, u8 status)
68 {
69         *spi_address = 0x50;    /* EWSR */
70         *(spi_address + 1) = 0; /* RxByte=TxByte=0 */
71         execute_command(spi_address);
72         wait4command_complete(spi_address);
73
74         *spi_address = 0x01;    /* WRSR */
75         *(spi_address + 1) = 0x01;
76         reset_internal_fifo_pointer(spi_address);
77         *(spi_address + 0xC) = status;
78         reset_internal_fifo_pointer(spi_address);
79         execute_command(spi_address);
80         wait4command_complete(spi_address);
81         wait4flashpart_ready(spi_address);
82
83         read_spi_status(spi_address);
84 }
85
86 void read_spi_id(volatile u8 * spi_address)
87 {
88         u8 mid = 0, did = 0;
89         *spi_address = 0x90;
90         *(spi_address + 1) = 0x23;      /* RxByte=2, TxByte=3 */
91         reset_internal_fifo_pointer(spi_address);
92         *(spi_address + 0xC) = 0;
93         *(spi_address + 0xC) = 0;
94         *(spi_address + 0xC) = 0;
95         reset_internal_fifo_pointer(spi_address);
96         execute_command(spi_address);
97         wait4command_complete(spi_address);
98         reset_internal_fifo_pointer(spi_address);
99         mid = *(spi_address + 0xC);
100         printk(BIOS_DEBUG, "mid=%x, did=%x\n", mid, did);
101         mid = *(spi_address + 0xC);
102         printk(BIOS_DEBUG, "mid=%x, did=%x\n", mid, did);
103         mid = *(spi_address + 0xC);
104         printk(BIOS_DEBUG, "mid=%x, did=%x\n", mid, did);
105
106         mid = *(spi_address + 0xC);
107         did = *(spi_address + 0xC);
108         printk(BIOS_DEBUG, "mid=%x, did=%x\n", mid, did);
109 }
110
111 void spi_write_enable(volatile u8 * spi_address)
112 {
113         *spi_address = 0x06;    /* Write Enable */
114         *(spi_address + 1) = 0x0;       /* RxByte=0, TxByte=0 */
115         execute_command(spi_address);
116         wait4command_complete(spi_address);
117 }
118
119 void sector_erase_spi(volatile u8 * spi_address, u32 address)
120 {
121         spi_write_enable(spi_address);
122         *spi_address = 0x20;
123         *(spi_address + 1) = 0x03;      /* RxByte=0, TxByte=3 */
124
125         reset_internal_fifo_pointer(spi_address);
126         *(spi_address + 0xC) = (address >> 16) & 0xFF;
127         *(spi_address + 0xC) = (address >> 8) & 0xFF;
128         *(spi_address + 0xC) = (address >> 0) & 0xFF;
129         reset_internal_fifo_pointer(spi_address);
130         execute_command(spi_address);
131         wait4command_complete(spi_address);
132         wait4flashpart_ready(spi_address);
133 }
134
135 void chip_erase_spi(volatile u8 * spi_address)
136 {
137         spi_write_enable(spi_address);
138         *spi_address = 0xC7;
139         *(spi_address + 1) = 0x00;
140         execute_command(spi_address);
141         wait4command_complete(spi_address);
142         wait4flashpart_ready(spi_address);
143 }
144
145 void byte_program(volatile u8 * spi_address, u32 address, u32 data)
146 {
147         spi_write_enable(spi_address);
148         *spi_address = 0x02;
149         *(spi_address + 1) = 0x0 << 4 | 4;
150         reset_internal_fifo_pointer(spi_address);
151         *(spi_address + 0xC) = (address >> 16) & 0xFF;
152         *(spi_address + 0xC) = (address >> 8) & 0xFF;
153         *(spi_address + 0xC) = (address >> 0) & 0xFF;
154         *(spi_address + 0xC) = data & 0xFF;
155         reset_internal_fifo_pointer(spi_address);
156         execute_command(spi_address);
157         wait4command_complete(spi_address);
158         wait4flashpart_ready(spi_address);
159 }
160
161 void dword_noneAAI_program(volatile u8 * spi_address, u32 address, u32 data)
162 {
163         u8 i;
164         /*
165          * printk(BIOS_SPEW, "%s: addr=%x, data=%x\n", __func__, address, data);
166          */
167         for (i = 0; i < 4; i++) {
168                 spi_write_enable(spi_address);
169                 *spi_address = 0x02;
170                 *(spi_address + 1) = 0x0 << 4 | 4;
171                 reset_internal_fifo_pointer(spi_address);
172                 *(spi_address + 0xC) = (address >> 16) & 0xFF;
173                 *(spi_address + 0xC) = (address >> 8) & 0xFF;
174                 *(spi_address + 0xC) = (address >> 0) & 0xFF;
175                 *(spi_address + 0xC) = data & 0xFF;
176                 data >>= 8;
177                 address++;
178                 reset_internal_fifo_pointer(spi_address);
179                 execute_command(spi_address);
180                 wait4command_complete(spi_address);
181                 wait4flashpart_ready(spi_address);
182         }
183 }
184
185 void dword_program(volatile u8 * spi_address, u32 address, u32 data)
186 {
187         spi_write_enable(spi_address);
188         *spi_address = 0x02;
189         *(spi_address + 1) = 0x0 << 4 | 7;
190         reset_internal_fifo_pointer(spi_address);
191         *(spi_address + 0xC) = (address >> 16) & 0xFF;
192         *(spi_address + 0xC) = (address >> 8) & 0xFF;
193         *(spi_address + 0xC) = (address >> 0) & 0xFF;
194         *(spi_address + 0xC) = data & 0xFF;
195         *(spi_address + 0xC) = (data >> 8) & 0xFF;
196         *(spi_address + 0xC) = (data >> 16) & 0xFF;
197         *(spi_address + 0xC) = (data >> 24) & 0xFF;
198         reset_internal_fifo_pointer(spi_address);
199         execute_command(spi_address);
200         wait4command_complete(spi_address);
201         wait4flashpart_ready(spi_address);
202 }
203
204 void direct_byte_program(volatile u8 * spi_address, volatile u32 * address, u32 data)
205 {
206         spi_write_enable(spi_address);
207         *address = data;
208         wait4flashpart_ready(spi_address);
209 }