From 9569c40668290408eac447f9be99dab603c8e34c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 5 Mar 2015 12:25:26 -0700 Subject: [PATCH] dm: sandbox: pci: Add PCI support for sandbox Add the required header information, device tree nodes and I/O accessor functions to support PCI on sandbox. All devices are emulated by drivers which can be added as required for testing or development. Signed-off-by: Simon Glass --- arch/sandbox/Kconfig | 7 ++ arch/sandbox/cpu/cpu.c | 41 ++++++- arch/sandbox/dts/sandbox.dts | 20 ++++ arch/sandbox/include/asm/io.h | 16 ++- arch/sandbox/include/asm/processor.h | 12 ++ arch/sandbox/include/asm/test.h | 7 +- arch/sandbox/include/asm/u-boot-sandbox.h | 48 ++++++++ arch/sandbox/lib/Makefile | 2 +- arch/sandbox/lib/pci_io.c | 138 ++++++++++++++++++++++ 9 files changed, 284 insertions(+), 7 deletions(-) create mode 100644 arch/sandbox/include/asm/processor.h create mode 100644 arch/sandbox/lib/pci_io.c diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 2098b9c323..477a20a820 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -34,4 +34,11 @@ config DM_I2C config DM_TEST default y +config PCI + bool "PCI support" + help + Enable support for PCI (Peripheral Interconnect Bus), a type of bus + used on some devices to allow the CPU to communicate with its + peripherals. + endmenu diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index 1aa397c5e7..007ae86034 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -2,7 +2,7 @@ * Copyright (c) 2011 The Chromium OS Authors. * SPDX-License-Identifier: GPL-2.0+ */ - +#define DEBUG #include #include #include @@ -10,6 +10,15 @@ DECLARE_GLOBAL_DATA_PTR; +/* Enable access to PCI memory with map_sysmem() */ +static bool enable_pci_map; + +#ifdef CONFIG_PCI +/* Last device that was mapped into memory, and length of mapping */ +static struct udevice *map_dev; +unsigned long map_len; +#endif + void reset_cpu(ulong ignored) { if (state_uninit()) @@ -59,9 +68,39 @@ int cleanup_before_linux(void) void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) { +#ifdef CONFIG_PCI + unsigned long plen = len; + void *ptr; + + map_dev = NULL; + if (enable_pci_map && !pci_map_physmem(paddr, &len, &map_dev, &ptr)) { + if (plen != len) { + printf("%s: Warning: partial map at %x, wanted %lx, got %lx\n", + __func__, paddr, len, plen); + } + map_len = len; + return ptr; + } +#endif + return (void *)(gd->arch.ram_buf + paddr); } +void unmap_physmem(const void *vaddr, unsigned long flags) +{ +#ifdef CONFIG_PCI + if (map_dev) { + pci_unmap_physmem(vaddr, map_len, map_dev); + map_dev = NULL; + } +#endif +} + +void sandbox_set_enable_pci_map(int enable) +{ + enable_pci_map = enable; +} + phys_addr_t map_to_sysmem(const void *ptr) { return (u8 *)ptr - gd->arch.ram_buf; diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index d090ba8e6a..42a1f21e12 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -4,6 +4,10 @@ #address-cells = <1>; #size-cells = <1>; + aliases { + pci0 = &pci; + }; + chosen { stdout-path = "/serial"; }; @@ -181,4 +185,20 @@ }; }; + pci: pci-controller { + compatible = "sandbox,pci"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000 + 0x01000000 0 0x20000000 0x20000000 0 0x2000>; + pci@1f,0 { + compatible = "pci-generic"; + reg = <0xf800 0 0 0 0>; + emul@1f,0 { + compatible = "sandbox,swap-case"; + }; + }; + }; + }; diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h index 895fcb872f..5b87fde116 100644 --- a/arch/sandbox/include/asm/io.h +++ b/arch/sandbox/include/asm/io.h @@ -22,10 +22,7 @@ void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags); /* * Take down a mapping set up by map_physmem(). */ -static inline void unmap_physmem(void *vaddr, unsigned long flags) -{ - -} +void unmap_physmem(const void *vaddr, unsigned long flags); /* For sandbox, we want addresses to point into our RAM buffer */ static inline void *map_sysmem(phys_addr_t paddr, unsigned long len) @@ -33,8 +30,10 @@ static inline void *map_sysmem(phys_addr_t paddr, unsigned long len) return map_physmem(paddr, len, MAP_WRBACK); } +/* Remove a previous mapping */ static inline void unmap_sysmem(const void *vaddr) { + unmap_physmem(vaddr, MAP_WRBACK); } /* Map from a pointer to our RAM buffer */ @@ -48,6 +47,15 @@ phys_addr_t map_to_sysmem(const void *ptr); #define writew(v, addr) #define writel(v, addr) +/* I/O access functions */ +int inl(unsigned int addr); +int inw(unsigned int addr); +int inb(unsigned int addr); + +void outl(unsigned int value, unsigned int addr); +void outw(unsigned int value, unsigned int addr); +void outb(unsigned int value, unsigned int addr); + #include #endif diff --git a/arch/sandbox/include/asm/processor.h b/arch/sandbox/include/asm/processor.h new file mode 100644 index 0000000000..3c1794e92d --- /dev/null +++ b/arch/sandbox/include/asm/processor.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_PROCESSOR_H +#define _ASM_PROCESSOR_H + +/* This file is required for PCI */ + +#endif diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index 25a0c85971..8e490e96d7 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -10,7 +10,12 @@ #define __ASM_TEST_H /* The sandbox driver always permits an I2C device with this address */ -#define SANDBOX_I2C_TEST_ADDR 0x59 +#define SANDBOX_I2C_TEST_ADDR 0x59 + +#define SANDBOX_PCI_VENDOR_ID 0x1234 +#define SANDBOX_PCI_DEVICE_ID 0x5678 +#define SANDBOX_PCI_CLASS_CODE PCI_CLASS_CODE_COMM +#define SANDBOX_PCI_CLASS_SUB_CODE PCI_CLASS_SUB_CODE_COMM_SERIAL enum sandbox_i2c_eeprom_test_mode { SIE_TEST_MODE_NONE, diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h index 770ab5c9cc..d5b9361683 100644 --- a/arch/sandbox/include/asm/u-boot-sandbox.h +++ b/arch/sandbox/include/asm/u-boot-sandbox.h @@ -27,4 +27,52 @@ int cleanup_before_linux(void); /* drivers/video/sandbox_sdl.c */ int sandbox_lcd_sdl_early_init(void); +/** + * pci_map_physmem() - map a PCI device into memory + * + * This is used on sandbox to map a device into memory so that it can be + * used with normal memory access. After this call, some part of the device's + * internal structure becomes visible. + * + * This function is normally called from sandbox's map_sysmem() automatically. + * + * @paddr: Physical memory address, normally corresponding to a PCI BAR + * @lenp: On entry, the size of the area to map, On exit it is updated + * to the size actually mapped, which may be less if the device + * has less space + * @devp: Returns the device which mapped into this space + * @ptrp: Returns a pointer to the mapped address. The device's space + * can be accessed as @lenp bytes starting here + * @return 0 if OK, -ve on error + */ +int pci_map_physmem(phys_addr_t paddr, unsigned long *lenp, + struct udevice **devp, void **ptrp); + +/** + * pci_unmap_physmem() - undo a memory mapping + * + * This must be called after pci_map_physmem() to undo the mapping. + * + * @paddr: Physical memory address, as passed to pci_map_physmem() + * @len: Size of area mapped, as returned by pci_map_physmem() + * @dev: Device to unmap, as returned by pci_map_physmem() + * @return 0 if OK, -ve on error + */ +int pci_unmap_physmem(const void *addr, unsigned long len, + struct udevice *dev); + +/** + * sandbox_set_enable_pci_map() - Enable / disable PCI address mapping + * + * Since address mapping involves calling every driver, provide a way to + * enable and disable this. It can be handled automatically by the emulator + * uclass, which knows if any emulators are currently active. + * + * If this is disabled, pci_map_physmem() will not be called from + * map_sysmem(). + * + * @enable: 0 to disable, 1 to enable + */ +void sandbox_set_enable_pci_map(int enable); + #endif /* _U_BOOT_SANDBOX_H_ */ diff --git a/arch/sandbox/lib/Makefile b/arch/sandbox/lib/Makefile index 4c1a38d6bc..75b135cfc0 100644 --- a/arch/sandbox/lib/Makefile +++ b/arch/sandbox/lib/Makefile @@ -7,5 +7,5 @@ # SPDX-License-Identifier: GPL-2.0+ # - obj-y += interrupts.o +obj-$(CONFIG_PCI) += pci_io.o diff --git a/arch/sandbox/lib/pci_io.c b/arch/sandbox/lib/pci_io.c new file mode 100644 index 0000000000..0de124f315 --- /dev/null +++ b/arch/sandbox/lib/pci_io.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * IO space access commands. + */ + +#include +#include +#include +#include + +int pci_map_physmem(phys_addr_t paddr, unsigned long *lenp, + struct udevice **devp, void **ptrp) +{ + struct udevice *dev; + int ret; + + *ptrp = 0; + for (uclass_first_device(UCLASS_PCI_EMUL, &dev); + dev; + uclass_next_device(&dev)) { + struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev); + + if (!ops || !ops->map_physmem) + continue; + ret = (ops->map_physmem)(dev, paddr, lenp, ptrp); + if (ret) + continue; + *devp = dev; + return 0; + } + + debug("%s: failed: addr=%x\n", __func__, paddr); + return -ENOSYS; +} + +int pci_unmap_physmem(const void *vaddr, unsigned long len, + struct udevice *dev) +{ + struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev); + + if (!ops || !ops->unmap_physmem) + return -ENOSYS; + return (ops->unmap_physmem)(dev, vaddr, len); +} + +static int pci_io_read(unsigned int addr, ulong *valuep, pci_size_t size) +{ + struct udevice *dev; + int ret; + + *valuep = pci_get_ff(size); + for (uclass_first_device(UCLASS_PCI_EMUL, &dev); + dev; + uclass_next_device(&dev)) { + struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev); + + if (ops && ops->read_io) { + ret = (ops->read_io)(dev, addr, valuep, size); + if (!ret) + return 0; + } + } + + debug("%s: failed: addr=%x\n", __func__, addr); + return -ENOSYS; +} + +static int pci_io_write(unsigned int addr, ulong value, pci_size_t size) +{ + struct udevice *dev; + int ret; + + for (uclass_first_device(UCLASS_PCI_EMUL, &dev); + dev; + uclass_next_device(&dev)) { + struct dm_pci_emul_ops *ops = pci_get_emul_ops(dev); + + if (ops && ops->write_io) { + ret = (ops->write_io)(dev, addr, value, size); + if (!ret) + return 0; + } + } + + debug("%s: failed: addr=%x, value=%lx\n", __func__, addr, value); + return -ENOSYS; +} + +int inl(unsigned int addr) +{ + unsigned long value; + int ret; + + ret = pci_io_read(addr, &value, PCI_SIZE_32); + + return ret ? 0 : value; +} + +int inw(unsigned int addr) +{ + unsigned long value; + int ret; + + ret = pci_io_read(addr, &value, PCI_SIZE_16); + + return ret ? 0 : value; +} + +int inb(unsigned int addr) +{ + unsigned long value; + int ret; + + ret = pci_io_read(addr, &value, PCI_SIZE_8); + + return ret ? 0 : value; +} + +void outl(unsigned int value, unsigned int addr) +{ + pci_io_write(addr, value, PCI_SIZE_32); +} + +void outw(unsigned int value, unsigned int addr) +{ + pci_io_write(addr, value, PCI_SIZE_16); +} + +void outb(unsigned int value, unsigned int addr) +{ + pci_io_write(addr, value, PCI_SIZE_8); +} -- 2.30.2