common/block/imc: Add Integrated Memory Controller (IMC) driver
IMC is found on certain Xeon processors. On such platforms SPDs are not connected to SMBus on PCH but to dedicated IMC-owned pins. The purpose of this driver is to expose access to the i2c/smbus controller associated with IMC. Datasheet used: Intel Xeon Processor D-1500 Product Family, Volume 2, reference 332051-001 This driver is largely based on i2c-imc.c Linux driver. https://lwn.net/Articles/685475/ TEST=single/double reads and single writes on Xeon-D1500. Hardware: Open Compute Project Monolake platform. Signed-off-by: Andrey Petrov <anpetrov@fb.com> Change-Id: Idbcda1c2273b9a5721fcd9470b4de182192779e7 Reviewed-on: https://review.coreboot.org/c/coreboot/+/34678 Reviewed-by: David Hendricks <david.hendricks@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
		
				
					committed by
					
						 Martin Roth
						Martin Roth
					
				
			
			
				
	
			
			
			
						parent
						
							c0193c9237
						
					
				
				
					commit
					f377fafd94
				
			
							
								
								
									
										7
									
								
								src/soc/intel/common/block/imc/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/soc/intel/common/block/imc/Kconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | config SOC_INTEL_COMMON_BLOCK_IMC | ||||||
|  | 	bool | ||||||
|  | 	depends on MMCONF_SUPPORT | ||||||
|  | 	default n | ||||||
|  | 	help | ||||||
|  | 	  Driver for communication with Integrated Memory Controller that is found on | ||||||
|  | 	  some Xeon server processors. | ||||||
							
								
								
									
										9
									
								
								src/soc/intel/common/block/imc/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/soc/intel/common/block/imc/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | ifeq ($(CONFIG_SOC_INTEL_COMMON_BLOCK_IMC),y) | ||||||
|  |  | ||||||
|  | bootblock-y += imc.c | ||||||
|  | romstage-y += imc.c | ||||||
|  | verstage-y += imc.c | ||||||
|  | postcar-y += imc.c | ||||||
|  | ramstage-y += imc.c | ||||||
|  |  | ||||||
|  | endif | ||||||
							
								
								
									
										171
									
								
								src/soc/intel/common/block/imc/imc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/soc/intel/common/block/imc/imc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | |||||||
|  | /* | ||||||
|  |  * This file is part of the coreboot project. | ||||||
|  |  * | ||||||
|  |  * Copyright 2019 Facebook, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; version 2 of the License. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Please note: the driver uses MMIO PCIe register access. IO based access will | ||||||
|  |  * not work. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <console/console.h> | ||||||
|  | #include <delay.h> | ||||||
|  | #include <device/pci_ops.h> | ||||||
|  | #include <intelblocks/imc.h> | ||||||
|  | #include <soc/pci_devs.h> | ||||||
|  | #include <timer.h> | ||||||
|  |  | ||||||
|  | #define IMC_SMBUS_TIMEOUT_MS           100 | ||||||
|  | #define IMC_SMBCNTL_DTI_TSOD           0x3 | ||||||
|  | #define IMC_SMBCNTL_DTI_EEPROM         0xa | ||||||
|  | #define IMC_SMBCNTL_DTI_WP_EEPROM      0x6 | ||||||
|  |  | ||||||
|  | #define SMBSTAT(i)                     (0x180 + 0x10 * i) | ||||||
|  | #define SMBCMD(i)                      (0x184 + 0x10 * i) | ||||||
|  | #define SMBCNTL(i)                     (0x188 + 0x10 * i) | ||||||
|  |  | ||||||
|  | #define SMBSTAT_RDO                    (1u << 31)  /* Read Data Valid */ | ||||||
|  | #define SMBSTAT_WOD                    (1u << 30)  /* Write Operation Done */ | ||||||
|  | #define SMBSTAT_SBE                    (1u << 29)  /* SMBus Error */ | ||||||
|  | #define SMBSTAT_SMB_BUSY               (1u << 28)  /* SMBus Busy State */ | ||||||
|  |  | ||||||
|  | #define SMBCMD_TRIGGER                 (1u << 31)  /* CMD Trigger */ | ||||||
|  | #define SMBCMD_PNTR_SEL                (1u << 30)  /* HW polls TSOD with pointer */ | ||||||
|  | #define SMBCMD_WORD_ACCESS             (1u << 29)  /* word (vs byte) access */ | ||||||
|  | #define SMBCMD_TYPE_MASK               (3u << 27)  /* Mask for access type */ | ||||||
|  | #define SMBCMD_TYPE_READ               (0u << 27)  /* Read */ | ||||||
|  | #define SMBCMD_TYPE_WRITE              (1u << 27)  /* Write */ | ||||||
|  | #define SMBCMD_TYPE_PNTR_WRITE         (3u << 27)  /* Write to pointer */ | ||||||
|  | #define SMBCMD_SA_MASK                 (7u << 24)  /* Slave Address high bits */ | ||||||
|  | #define SMBCMD_SA_SHIFT                24 | ||||||
|  | #define SMBCMD_BA_MASK                 0xff0000    /* Bus Txn address */ | ||||||
|  | #define SMBCMD_BA_SHIFT                16 | ||||||
|  | #define SMBCMD_WDATA_MASK              0xffff      /* data to write */ | ||||||
|  |  | ||||||
|  | #define SMBCNTL_DTI_MASK               0xf0000000  /* Slave Address low bits */ | ||||||
|  | #define SMBCNTL_DTI_SHIFT              28          /* Slave Address low bits */ | ||||||
|  | #define SMBCNTL_CKOVRD                 (1u << 27)  /* # Clock Override */ | ||||||
|  | #define SMBCNTL_DIS_WRT                (1u << 26)  /* Disable Write (sadly) */ | ||||||
|  | #define SMBCNTL_SOFT_RST               (1u << 10)  /* Soft Reset */ | ||||||
|  | #define SMBCNTL_TSOD_POLL_EN           (1u << 8)   /* TSOD Polling Enable */ | ||||||
|  |  | ||||||
|  | static bool poll_ready(pci_devfn_t dev, enum memory_controller_id mcid, uint32_t *status) | ||||||
|  | { | ||||||
|  | 	struct stopwatch sw; | ||||||
|  |  | ||||||
|  | 	stopwatch_init_msecs_expire(&sw, IMC_SMBUS_TIMEOUT_MS); | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		*status = pci_mmio_read_config32(dev, SMBSTAT(mcid)); | ||||||
|  | 		if (!(*status & SMBSTAT_SMB_BUSY)) | ||||||
|  | 			break; | ||||||
|  | 	} while (!stopwatch_expired(&sw)); | ||||||
|  |  | ||||||
|  | 	return (!(*status & SMBSTAT_SMB_BUSY)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool claim_controller(pci_devfn_t dev, enum memory_controller_id mcid) | ||||||
|  | { | ||||||
|  | 	uint32_t cntl, status; | ||||||
|  |  | ||||||
|  | 	cntl = pci_mmio_read_config32(dev, SMBCNTL(mcid)); | ||||||
|  | 	cntl &= ~SMBCNTL_TSOD_POLL_EN; | ||||||
|  | 	cntl &= ~SMBCNTL_DIS_WRT; | ||||||
|  | 	pci_mmio_write_config32(dev, SMBCNTL(mcid), cntl); | ||||||
|  |  | ||||||
|  | 	return poll_ready(dev, mcid, &status); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void release_controller(pci_devfn_t dev, enum memory_controller_id mcid) | ||||||
|  | { | ||||||
|  | 	uint32_t cntl, status; | ||||||
|  |  | ||||||
|  | 	cntl = pci_mmio_read_config32(dev, SMBCNTL(mcid)); | ||||||
|  | 	cntl |= SMBCNTL_TSOD_POLL_EN; | ||||||
|  | 	pci_mmio_write_config32(dev, SMBCNTL(mcid), cntl); | ||||||
|  |  | ||||||
|  | 	poll_ready(dev, mcid, &status); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int imc_smbus_spd_xfer(pci_devfn_t dev, uint8_t slave_addr, uint8_t bus_addr, | ||||||
|  | 		       enum device_type_id dti, enum access_width width, | ||||||
|  | 		       enum memory_controller_id mcid, enum smbus_command cmd, void *data) | ||||||
|  | { | ||||||
|  | 	int ret = -1; | ||||||
|  | 	uint32_t cmdbits = 0, stat = 0, cntlbits = 0, data_mask = 0; | ||||||
|  | 	uint16_t wdata = 0, rdata = 0; | ||||||
|  |  | ||||||
|  | 	/* slaves addresses are 7 bits length */ | ||||||
|  | 	if (slave_addr > (1 << 7) - 1) { | ||||||
|  | 		printk(BIOS_ERR, "invalid SMBus address, aborting xfer\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!claim_controller(dev, mcid)) { | ||||||
|  | 		printk(BIOS_ERR, "ayee! couldn't claim controller, giving up xfer\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmdbits = (slave_addr << SMBCMD_SA_SHIFT); | ||||||
|  | 	cmdbits |= (bus_addr << SMBCMD_BA_SHIFT); | ||||||
|  |  | ||||||
|  | 	if (cmd == IMC_WRITE) { | ||||||
|  | 		wdata = (width == IMC_DATA_BYTE ? read8(data) : cpu_to_be16(read16(data))); | ||||||
|  | 		cmdbits |= (SMBCMD_TYPE_WRITE | wdata); | ||||||
|  | 	} else { | ||||||
|  | 		cmdbits |= SMBCMD_TYPE_READ; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (width == IMC_DATA_WORD) { | ||||||
|  | 		cmdbits |= SMBCMD_WORD_ACCESS; | ||||||
|  | 		data_mask = 0xffff; | ||||||
|  | 	} else { | ||||||
|  | 		data_mask = 0xff; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cntlbits = pci_mmio_read_config32(dev, SMBCNTL(mcid)); | ||||||
|  | 	cntlbits &= ~SMBCNTL_DTI_MASK; | ||||||
|  | 	cntlbits |= (dti << SMBCNTL_DTI_SHIFT); | ||||||
|  |  | ||||||
|  | 	pci_mmio_write_config32(dev, SMBCNTL(mcid), cntlbits); | ||||||
|  |  | ||||||
|  | 	/* Pull the trigger */ | ||||||
|  | 	cmdbits |= SMBCMD_TRIGGER; | ||||||
|  | 	pci_mmio_write_config32(dev, SMBCMD(mcid), cmdbits); | ||||||
|  |  | ||||||
|  | 	if (!poll_ready(dev, mcid, &stat)) { | ||||||
|  | 		printk(BIOS_ERR, "IMC xfer failed for slave %x", slave_addr); | ||||||
|  | 		ret = -1; | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (stat & SMBSTAT_SBE) { | ||||||
|  | 		ret = -1; | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (cmd == IMC_READ) { | ||||||
|  | 		rdata = stat & data_mask; | ||||||
|  | 		if (width == IMC_DATA_WORD) | ||||||
|  | 			write16(data, cpu_to_be16(rdata)); | ||||||
|  | 		else | ||||||
|  | 			write8(data, rdata); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ret = 0; | ||||||
|  | cleanup: | ||||||
|  | 	release_controller(dev, SMBSTAT(mcid)); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								src/soc/intel/common/block/include/intelblocks/imc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/soc/intel/common/block/include/intelblocks/imc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * This file is part of the coreboot project. | ||||||
|  |  * | ||||||
|  |  * Copyright 2019 Facebook, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; version 2 of the License. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <device/pci.h> | ||||||
|  | #include <stdint.h> | ||||||
|  |  | ||||||
|  | #ifndef SOC_INTEL_COMMON_BLOCK_IMC_H | ||||||
|  | #define SOC_INTEL_COMMON_BLOCK_IMC_H | ||||||
|  |  | ||||||
|  | enum smbus_command { IMC_READ, IMC_WRITE }; | ||||||
|  |  | ||||||
|  | enum access_width { IMC_DATA_BYTE, IMC_DATA_WORD }; | ||||||
|  |  | ||||||
|  | enum memory_controller_id { IMC_CONTROLLER_ID0 = 0, IMC_CONTROLLER_ID1 }; | ||||||
|  |  | ||||||
|  | enum device_type_id { | ||||||
|  | 	IMC_DEVICE_TSOD = 0x3, | ||||||
|  | 	IMC_DEVICE_WP_EEPROM = 0x6, | ||||||
|  | 	IMC_DEVICE_EEPROM = 0xa | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Initiate SMBus/I2C transaction to DIMM EEPROM */ | ||||||
|  | int imc_smbus_spd_xfer(pci_devfn_t dev, uint8_t slave_addr, uint8_t bus_addr, | ||||||
|  | 		       enum device_type_id dti, enum access_width width, | ||||||
|  | 		       enum memory_controller_id mcid, enum smbus_command cmd, void *data); | ||||||
|  | #endif | ||||||
| @@ -26,6 +26,9 @@ config CPU_SPECIFIC_OPTIONS | |||||||
| 	select TSC_CONSTANT_RATE | 	select TSC_CONSTANT_RATE | ||||||
| 	select HAVE_FSP_BIN | 	select HAVE_FSP_BIN | ||||||
| 	select CPU_INTEL_FIRMWARE_INTERFACE_TABLE | 	select CPU_INTEL_FIRMWARE_INTERFACE_TABLE | ||||||
|  | 	select SOC_INTEL_COMMON | ||||||
|  | 	select SOC_INTEL_COMMON_BLOCK | ||||||
|  | 	select SOC_INTEL_COMMON_BLOCK_IMC | ||||||
|  |  | ||||||
| config VBOOT | config VBOOT | ||||||
| 	select VBOOT_STARTS_IN_ROMSTAGE | 	select VBOOT_STARTS_IN_ROMSTAGE | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user