blob: 8b0bcf2f5af2b7f6a0b6794714ed9c53cb818bb7 [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.
Rudolf Marek70e14592013-07-25 22:58:56 +000016 */
17
18#if defined(__i386__) || defined(__x86_64__)
19
20#include "flash.h"
21#include "programmer.h"
22#include "hwaccess.h"
23#include "spi.h"
24
25/* same as serverengines */
26static void enter_conf_mode_ec(uint16_t port)
27{
28 OUTB(0x5a, port);
29}
30
31static void exit_conf_mode_ec(uint16_t port)
32{
33 OUTB(0xa5, port);
34}
35
36static uint16_t get_sio_port(struct pci_dev *dev)
37{
38 uint16_t ec_port;
39
40 if (!dev) {
41 return 0;
42 }
43
44 ec_port = pci_read_word(dev, 0xa4);
45
46 /* EcPortActive? */
47 if (!(ec_port & 0x1))
48 return 0;
49
50 ec_port &= ~0x1;
51
52 return ec_port;
53}
54
55/* Wait for up to 10 ms for a response. */
56static int mbox_wait_ack(uint16_t mbox_port)
57{
58 int i = 10;
59 while (sio_read(mbox_port, 0x82) != 0xfa) {
60 if (--i == 0) {
61 msg_pwarn("IMC MBOX: Timeout!\n");
62 return 1;
63 }
64 programmer_delay(1000);
65 }
66 return 0;
67}
68
69static uint16_t mbox_get_port(uint16_t sio_port)
70{
71 uint16_t mbox_port;
72
73 enter_conf_mode_ec(sio_port);
74
75 /* Go to LDN 9, mailbox */
76 sio_write(sio_port, 7, 9);
77
78 /* MBOX inactive? */
79 if ((sio_read(sio_port, 0x30) & 1) == 0) {
80 exit_conf_mode_ec(sio_port);
81 return 0;
82 }
83
84 mbox_port = sio_read(sio_port, 0x60) << 8;
85 mbox_port |= sio_read(sio_port, 0x61);
86
87 exit_conf_mode_ec(sio_port);
88 return mbox_port;
89}
90
91/* Returns negative values when IMC is inactive, positive values on errors */
92static int imc_send_cmd(struct pci_dev *dev, uint8_t cmd)
93{
94 uint16_t sio_port;
95 uint16_t mbox_port;
96
97 /* IntegratedEcPresent? */
98 if (!(pci_read_byte(dev, 0x40) & (1 << 7)))
99 return -1;
100
101 sio_port = get_sio_port(dev);
102 if (!sio_port)
103 return -1;
104
105 msg_pdbg2("IMC SIO is at 0x%x.\n", sio_port);
106 mbox_port = mbox_get_port(sio_port);
107 if (!mbox_port)
108 return -1;
109 msg_pdbg2("IMC MBOX is at 0x%x.\n", mbox_port);
110
111 sio_write(mbox_port, 0x82, 0x0);
112 sio_write(mbox_port, 0x83, cmd);
113 sio_write(mbox_port, 0x84, 0x0);
114 /* trigger transfer 0x96 with subcommand cmd */
115 sio_write(mbox_port, 0x80, 0x96);
116
117 return mbox_wait_ack(mbox_port);
118}
119
120static int imc_resume(void *data)
121{
122 struct pci_dev *dev = data;
123 int ret = imc_send_cmd(dev, 0xb5);
124
125 if (ret != 0)
126 msg_pinfo("Resuming IMC failed)\n");
127 else
128 msg_pdbg2("IMC resumed.\n");
129 return ret;
130}
131
132int amd_imc_shutdown(struct pci_dev *dev)
133{
134 /* Try to put IMC to sleep */
135 int ret = imc_send_cmd(dev, 0xb4);
136
137 /* No IMC activity detectable, assume we are fine */
138 if (ret < 0) {
139 msg_pdbg2("No IMC found.\n");
140 return 0;
141 }
142
143 if (ret != 0) {
144 msg_perr("Shutting down IMC failed.\n");
145 return ret;
146 }
147 msg_pdbg2("Shutting down IMC successful.\n");
148
149 if (register_shutdown(imc_resume, dev))
150 return 1;
151
152 return ret;
153}
154
155#endif