blob: b05390c3f3e28fced9472653660935d2d2c29db3 [file] [log] [blame]
Rudolf Marek70e14592013-07-25 22:58:56 +00001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2013 Rudolf Marek <r.marek@assembler.cz>
5 * Copyright (C) 2013 Stefan Tauner
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#if defined(__i386__) || defined(__x86_64__)
23
24#include "flash.h"
25#include "programmer.h"
26#include "hwaccess.h"
27#include "spi.h"
28
29/* same as serverengines */
30static void enter_conf_mode_ec(uint16_t port)
31{
32 OUTB(0x5a, port);
33}
34
35static void exit_conf_mode_ec(uint16_t port)
36{
37 OUTB(0xa5, port);
38}
39
40static uint16_t get_sio_port(struct pci_dev *dev)
41{
42 uint16_t ec_port;
43
44 if (!dev) {
45 return 0;
46 }
47
48 ec_port = pci_read_word(dev, 0xa4);
49
50 /* EcPortActive? */
51 if (!(ec_port & 0x1))
52 return 0;
53
54 ec_port &= ~0x1;
55
56 return ec_port;
57}
58
59/* Wait for up to 10 ms for a response. */
60static int mbox_wait_ack(uint16_t mbox_port)
61{
62 int i = 10;
63 while (sio_read(mbox_port, 0x82) != 0xfa) {
64 if (--i == 0) {
65 msg_pwarn("IMC MBOX: Timeout!\n");
66 return 1;
67 }
68 programmer_delay(1000);
69 }
70 return 0;
71}
72
73static uint16_t mbox_get_port(uint16_t sio_port)
74{
75 uint16_t mbox_port;
76
77 enter_conf_mode_ec(sio_port);
78
79 /* Go to LDN 9, mailbox */
80 sio_write(sio_port, 7, 9);
81
82 /* MBOX inactive? */
83 if ((sio_read(sio_port, 0x30) & 1) == 0) {
84 exit_conf_mode_ec(sio_port);
85 return 0;
86 }
87
88 mbox_port = sio_read(sio_port, 0x60) << 8;
89 mbox_port |= sio_read(sio_port, 0x61);
90
91 exit_conf_mode_ec(sio_port);
92 return mbox_port;
93}
94
95/* Returns negative values when IMC is inactive, positive values on errors */
96static int imc_send_cmd(struct pci_dev *dev, uint8_t cmd)
97{
98 uint16_t sio_port;
99 uint16_t mbox_port;
100
101 /* IntegratedEcPresent? */
102 if (!(pci_read_byte(dev, 0x40) & (1 << 7)))
103 return -1;
104
105 sio_port = get_sio_port(dev);
106 if (!sio_port)
107 return -1;
108
109 msg_pdbg2("IMC SIO is at 0x%x.\n", sio_port);
110 mbox_port = mbox_get_port(sio_port);
111 if (!mbox_port)
112 return -1;
113 msg_pdbg2("IMC MBOX is at 0x%x.\n", mbox_port);
114
115 sio_write(mbox_port, 0x82, 0x0);
116 sio_write(mbox_port, 0x83, cmd);
117 sio_write(mbox_port, 0x84, 0x0);
118 /* trigger transfer 0x96 with subcommand cmd */
119 sio_write(mbox_port, 0x80, 0x96);
120
121 return mbox_wait_ack(mbox_port);
122}
123
124static int imc_resume(void *data)
125{
126 struct pci_dev *dev = data;
127 int ret = imc_send_cmd(dev, 0xb5);
128
129 if (ret != 0)
130 msg_pinfo("Resuming IMC failed)\n");
131 else
132 msg_pdbg2("IMC resumed.\n");
133 return ret;
134}
135
136int amd_imc_shutdown(struct pci_dev *dev)
137{
138 /* Try to put IMC to sleep */
139 int ret = imc_send_cmd(dev, 0xb4);
140
141 /* No IMC activity detectable, assume we are fine */
142 if (ret < 0) {
143 msg_pdbg2("No IMC found.\n");
144 return 0;
145 }
146
147 if (ret != 0) {
148 msg_perr("Shutting down IMC failed.\n");
149 return ret;
150 }
151 msg_pdbg2("Shutting down IMC successful.\n");
152
153 if (register_shutdown(imc_resume, dev))
154 return 1;
155
156 return ret;
157}
158
159#endif