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 HAVE_FSP_BIN | ||||
| 	select CPU_INTEL_FIRMWARE_INTERFACE_TABLE | ||||
| 	select SOC_INTEL_COMMON | ||||
| 	select SOC_INTEL_COMMON_BLOCK | ||||
| 	select SOC_INTEL_COMMON_BLOCK_IMC | ||||
|  | ||||
| config VBOOT | ||||
| 	select VBOOT_STARTS_IN_ROMSTAGE | ||||
|   | ||||
		Reference in New Issue
	
	Block a user