diff options
| author | Simon Glass <sjg@chromium.org> | 2015-03-05 12:25:26 -0700 | 
|---|---|---|
| committer | Simon Glass <sjg@chromium.org> | 2015-04-16 19:27:43 -0600 | 
| commit | 9569c40668290408eac447f9be99dab603c8e34c (patch) | |
| tree | f476d89aaed8887ebca97da08da07abc2c455fe3 | |
| parent | ff3e077bd23c37c83d01aad105e528194e33d75e (diff) | |
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 <sjg@chromium.org>
| -rw-r--r-- | arch/sandbox/Kconfig | 7 | ||||
| -rw-r--r-- | arch/sandbox/cpu/cpu.c | 41 | ||||
| -rw-r--r-- | arch/sandbox/dts/sandbox.dts | 20 | ||||
| -rw-r--r-- | arch/sandbox/include/asm/io.h | 16 | ||||
| -rw-r--r-- | arch/sandbox/include/asm/processor.h | 12 | ||||
| -rw-r--r-- | arch/sandbox/include/asm/test.h | 7 | ||||
| -rw-r--r-- | arch/sandbox/include/asm/u-boot-sandbox.h | 48 | ||||
| -rw-r--r-- | arch/sandbox/lib/Makefile | 2 | ||||
| -rw-r--r-- | arch/sandbox/lib/pci_io.c | 138 | 
9 files changed, 284 insertions, 7 deletions
| diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 2098b9c323b..477a20a820c 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 1aa397c5e77..007ae860346 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 <common.h>  #include <dm/root.h>  #include <os.h> @@ -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 d090ba8e6ab..42a1f21e121 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 895fcb872f6..5b87fde1161 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 <iotrace.h>  #endif diff --git a/arch/sandbox/include/asm/processor.h b/arch/sandbox/include/asm/processor.h new file mode 100644 index 00000000000..3c1794e92d2 --- /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 25a0c859714..8e490e96d7a 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 770ab5c9cc0..d5b93616839 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 4c1a38d6bcb..75b135cfc0e 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 00000000000..0de124f315f --- /dev/null +++ b/arch/sandbox/lib/pci_io.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier:     GPL-2.0+ + */ + +/* + * IO space access commands. + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <asm/io.h> + +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); +} | 
