| // SPDX-License-Identifier: MIT |
| /* |
| * Copyright © 2024 Intel Corporation |
| */ |
| |
| #include <linux/pci.h> |
| |
| #include <drm/drm_device.h> |
| |
| #include "i915_reg.h" |
| |
| #include "intel_rom.h" |
| #include "intel_uncore.h" |
| |
| struct intel_rom { |
| /* for PCI ROM */ |
| struct pci_dev *pdev; |
| void __iomem *oprom; |
| |
| /* for SPI */ |
| struct intel_uncore *uncore; |
| loff_t offset; |
| |
| size_t size; |
| |
| u32 (*read32)(struct intel_rom *rom, loff_t offset); |
| u16 (*read16)(struct intel_rom *rom, loff_t offset); |
| void (*read_block)(struct intel_rom *rom, void *data, loff_t offset, size_t size); |
| void (*free)(struct intel_rom *rom); |
| }; |
| |
| static u32 spi_read32(struct intel_rom *rom, loff_t offset) |
| { |
| intel_uncore_write(rom->uncore, PRIMARY_SPI_ADDRESS, |
| rom->offset + offset); |
| |
| return intel_uncore_read(rom->uncore, PRIMARY_SPI_TRIGGER); |
| } |
| |
| static u16 spi_read16(struct intel_rom *rom, loff_t offset) |
| { |
| return spi_read32(rom, offset) & 0xffff; |
| } |
| |
| struct intel_rom *intel_rom_spi(struct drm_device *drm) |
| { |
| struct intel_rom *rom; |
| u32 static_region; |
| |
| rom = kzalloc_obj(*rom); |
| if (!rom) |
| return NULL; |
| |
| rom->uncore = to_intel_uncore(drm); |
| |
| static_region = intel_uncore_read(rom->uncore, SPI_STATIC_REGIONS); |
| static_region &= OPTIONROM_SPI_REGIONID_MASK; |
| intel_uncore_write(rom->uncore, PRIMARY_SPI_REGIONID, static_region); |
| |
| rom->offset = intel_uncore_read(rom->uncore, OROM_OFFSET) & OROM_OFFSET_MASK; |
| |
| rom->size = 0x200000; |
| |
| rom->read32 = spi_read32; |
| rom->read16 = spi_read16; |
| |
| return rom; |
| } |
| |
| static u32 pci_read32(struct intel_rom *rom, loff_t offset) |
| { |
| return ioread32(rom->oprom + offset); |
| } |
| |
| static u16 pci_read16(struct intel_rom *rom, loff_t offset) |
| { |
| return ioread16(rom->oprom + offset); |
| } |
| |
| static void pci_read_block(struct intel_rom *rom, void *data, |
| loff_t offset, size_t size) |
| { |
| memcpy_fromio(data, rom->oprom + offset, size); |
| } |
| |
| static void pci_free(struct intel_rom *rom) |
| { |
| pci_unmap_rom(rom->pdev, rom->oprom); |
| } |
| |
| struct intel_rom *intel_rom_pci(struct drm_device *drm) |
| { |
| struct intel_rom *rom; |
| |
| rom = kzalloc_obj(*rom); |
| if (!rom) |
| return NULL; |
| |
| rom->pdev = to_pci_dev(drm->dev); |
| |
| rom->oprom = pci_map_rom(rom->pdev, &rom->size); |
| if (!rom->oprom) { |
| kfree(rom); |
| return NULL; |
| } |
| |
| rom->read32 = pci_read32; |
| rom->read16 = pci_read16; |
| rom->read_block = pci_read_block; |
| rom->free = pci_free; |
| |
| return rom; |
| } |
| |
| u32 intel_rom_read32(struct intel_rom *rom, loff_t offset) |
| { |
| return rom->read32(rom, offset); |
| } |
| |
| u16 intel_rom_read16(struct intel_rom *rom, loff_t offset) |
| { |
| return rom->read16(rom, offset); |
| } |
| |
| void intel_rom_read_block(struct intel_rom *rom, void *data, |
| loff_t offset, size_t size) |
| { |
| u32 *ptr = data; |
| loff_t index; |
| |
| if (rom->read_block) { |
| rom->read_block(rom, data, offset, size); |
| return; |
| } |
| |
| for (index = 0; index < size; index += 4) |
| *ptr++ = rom->read32(rom, offset + index); |
| } |
| |
| loff_t intel_rom_find(struct intel_rom *rom, u32 needle) |
| { |
| loff_t offset; |
| |
| for (offset = 0; offset < rom->size; offset += 4) { |
| if (rom->read32(rom, offset) == needle) |
| return offset; |
| } |
| |
| return -ENOENT; |
| } |
| |
| size_t intel_rom_size(struct intel_rom *rom) |
| { |
| return rom->size; |
| } |
| |
| void intel_rom_free(struct intel_rom *rom) |
| { |
| if (rom && rom->free) |
| rom->free(rom); |
| |
| kfree(rom); |
| } |