1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/bsd/cb_err.h>
4 #include <console/console.h>
6 #include <device/mmio.h>
7 #include <device/pci_ops.h>
8 #include <intelblocks/imc.h>
9 #include <soc/pci_devs.h>
15 #define IMC_SMBUS_TIMEOUT_MS 100
17 #define SMB_CMD_CFG 0x80
18 #define SMB_CKOVRD BIT(29)
19 #define SMB_DIS_WRT BIT(28)
20 #define SMB_SOFT_RST BIT(24)
21 #define SMB_TSOD_POLL_EN BIT(20)
22 #define SMB_CMD_TRIGGER BIT(19)
23 #define SMB_WORD_ACCESS BIT(17)
24 #define SMB_WRT_READ (0 << 15)
25 #define SMB_WRT_WRITE BIT(15)
26 #define SMB_DTI_MASK (7 << 11)
27 #define SMB_CMD_SA_SHIFT 8
28 #define SMB_CMD_BA_SHIFT 0
29 #define SMB_CMD_DTI_SHIFT 11
30 #define SMB_STATUS_CFG 0x84
31 #define SMB_SBE BIT(1)
32 #define SMB_BUSY BIT(0)
33 #define SMB_DATA_CFG 0x88
34 #define SMB_PERIOD_CFG 0x90
35 #define SMB_CLOCK_PERIOD_400K 250 /* Clock period for 400K. */
36 #define SMB_CLOCK_OFFSET_400K 35 /* Clock offset for 400K. */
38 static void imc_spd_smbus_reset(pci_devfn_t dev
)
42 cmd
= pci_read_config32(dev
, SMB_CMD_CFG
);
45 pci_write_config32(dev
, SMB_CMD_CFG
, cmd
);
47 mdelay(35); /* See description of `SMB_CKOVRD` field. */
49 cmd
= pci_read_config32(dev
, SMB_CMD_CFG
);
52 pci_write_config32(dev
, SMB_CMD_CFG
, cmd
);
55 void imc_spd_smbus_init(pci_devfn_t dev
)
58 if (pci_read_config16(dev
, 0) == 0xffff) {
60 "IMC SMBUS controller PCI: %02x:%02x:%02x.%01x isn't present!\n",
61 PCI_DEV2SEG(dev
), PCI_DEV2BUS(dev
), PCI_SLOT(PCI_DEV2DEVFN(dev
)),
62 PCI_FUNC(PCI_DEV2DEVFN(dev
)));
66 /* Set SMB CLOCK to 400K to detect DIMM SPDs. */
67 pci_write_config32(dev
, SMB_PERIOD_CFG
,
68 (SMB_CLOCK_OFFSET_400K
<< 16) | SMB_CLOCK_PERIOD_400K
);
70 /* Reset the bus if the first access is busy. */
71 status
= pci_read_config32(dev
, SMB_STATUS_CFG
);
72 if (status
& SMB_BUSY
)
73 imc_spd_smbus_reset(dev
);
75 /* Disable TSOD polling. */
76 cmd
= pci_read_config32(dev
, SMB_CMD_CFG
);
77 cmd
&= ~SMB_TSOD_POLL_EN
;
78 pci_write_config32(dev
, SMB_CMD_CFG
, cmd
);
81 static bool poll_ready(pci_devfn_t dev
, uint32_t *status
)
85 stopwatch_init_msecs_expire(&sw
, IMC_SMBUS_TIMEOUT_MS
);
88 *status
= pci_read_config32(dev
, SMB_STATUS_CFG
);
89 if ((*status
& SMB_BUSY
) == 0)
91 } while (!stopwatch_expired(&sw
));
96 static bool claim_controller(pci_devfn_t dev
)
100 cmd
= pci_read_config32(dev
, SMB_CMD_CFG
);
101 cmd
&= ~SMB_TSOD_POLL_EN
;
103 pci_write_config32(dev
, SMB_CMD_CFG
, cmd
);
105 return poll_ready(dev
, &status
);
108 static bool release_controller(pci_devfn_t dev
)
110 uint32_t cmd
, status
;
112 cmd
= pci_read_config32(dev
, SMB_CMD_CFG
);
113 cmd
|= SMB_TSOD_POLL_EN
;
114 pci_write_config32(dev
, SMB_CMD_CFG
, cmd
);
116 return poll_ready(dev
, &status
);
119 int imc_smbus_spd_xfer(pci_devfn_t dev
, uint8_t slave_addr
, uint8_t bus_addr
,
120 enum device_type_id dti
, enum access_width width
,
121 enum memory_controller_id mcid
, enum smbus_command cmd
, void *data
)
124 uint32_t cmdbits
, stat
, databits
, data_mask
;
125 uint16_t wdata
= 0, rdata
= 0;
127 /* Slaves addresses are 3 bits length, and bus address is 8 bits length. */
128 if (slave_addr
> (1 << 7) - 1) {
129 printk(BIOS_ERR
, "Invalid SMBus slave 0x%02x\n", slave_addr
);
133 if (!claim_controller(dev
)) {
134 printk(BIOS_ERR
, "Claim controller failed!\n");
138 cmdbits
= slave_addr
<< SMB_CMD_SA_SHIFT
;
139 cmdbits
|= bus_addr
<< SMB_CMD_BA_SHIFT
;
141 if (cmd
== IMC_WRITE
) {
142 databits
= pci_read_config32(dev
, SMB_DATA_CFG
);
143 wdata
= (width
== IMC_DATA_BYTE
? read8(data
) : cpu_to_be16(read16(data
)));
144 databits
|= (wdata
<< 16);
145 pci_write_config32(dev
, SMB_DATA_CFG
, databits
);
147 cmdbits
|= SMB_WRT_WRITE
;
148 cmdbits
&= ~SMB_DIS_WRT
;
150 cmdbits
|= SMB_WRT_READ
;
153 if (width
== IMC_DATA_WORD
) {
154 cmdbits
|= SMB_WORD_ACCESS
;
160 cmdbits
&= ~SMB_DTI_MASK
;
161 cmdbits
|= dti
<< SMB_CMD_DTI_SHIFT
;
162 cmdbits
|= SMB_CKOVRD
;
164 /* Pull the trigger */
165 cmdbits
|= SMB_CMD_TRIGGER
;
166 pci_write_config32(dev
, SMB_CMD_CFG
, cmdbits
);
168 if (!poll_ready(dev
, &stat
)) {
169 printk(BIOS_ERR
, "IMC transfer didn't finished for slave 0x%02x\n", slave_addr
);
174 if (stat
& SMB_SBE
) {
175 printk(BIOS_ERR
, "IMC SMBUS SBE for slave 0x%02x\n", slave_addr
);
180 if (cmd
== IMC_READ
) {
181 databits
= pci_read_config32(dev
, SMB_DATA_CFG
);
182 rdata
= databits
& data_mask
;
183 if (width
== IMC_DATA_WORD
)
184 write16(data
, be16_to_cpu(rdata
));
192 if (!release_controller(dev
)) {
193 printk(BIOS_ERR
, "Release controller failed!\n");