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