diff options
author | Tom Rini <trini@konsulko.com> | 2022-10-26 15:24:59 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-10-26 15:24:59 -0400 |
commit | 8bc87a4c55a1723728374a5643f13bced37dad6b (patch) | |
tree | b4ace98fe43682d20639adab97778dca140149c2 | |
parent | b487387226d49ba2f39757269fb95ea398e8f384 (diff) | |
parent | 9b0b5648d6e4d89aa594022e48894e811c250d5f (diff) | |
download | u-boot-8bc87a4c55a1723728374a5643f13bced37dad6b.zip u-boot-8bc87a4c55a1723728374a5643f13bced37dad6b.tar.gz u-boot-8bc87a4c55a1723728374a5643f13bced37dad6b.tar.bz2 |
Merge branch '2022-10-26-assorted-fixes-and-updates'
- Reduce memory usage in SPL in some cases, clarify some standalone API
license issues, fix a Kconfig dependency, pin to a specific version of
python setuptools for now, fix a signing problem in mkimage and add a
memory uclass.
-rw-r--r-- | Licenses/Exceptions | 12 | ||||
-rw-r--r-- | arch/sandbox/dts/test.dts | 4 | ||||
-rw-r--r-- | boot/image-fit-sig.c | 8 | ||||
-rw-r--r-- | common/spl/Kconfig | 7 | ||||
-rw-r--r-- | common/spl/spl_legacy.c | 19 | ||||
-rw-r--r-- | common/spl/spl_nand.c | 2 | ||||
-rw-r--r-- | common/spl/spl_nor.c | 6 | ||||
-rw-r--r-- | doc/device-tree-bindings/memory/ti,gpmc-child.yaml | 252 | ||||
-rw-r--r-- | doc/device-tree-bindings/memory/ti,gpmc.yaml | 190 | ||||
-rw-r--r-- | drivers/crypto/hash/Kconfig | 3 | ||||
-rw-r--r-- | drivers/memory/Kconfig | 36 | ||||
-rw-r--r-- | drivers/memory/Makefile | 3 | ||||
-rw-r--r-- | drivers/memory/memory-sandbox.c | 18 | ||||
-rw-r--r-- | drivers/memory/memory-uclass.c | 13 | ||||
-rw-r--r-- | drivers/memory/ti-gpmc.c | 1240 | ||||
-rw-r--r-- | drivers/memory/ti-gpmc.h | 298 | ||||
-rw-r--r-- | examples/standalone/hello_world.c | 1 | ||||
-rw-r--r-- | include/dm/uclass-id.h | 1 | ||||
-rw-r--r-- | include/linux/mtd/omap_gpmc.h | 3 | ||||
-rw-r--r-- | include/spl.h | 7 | ||||
-rw-r--r-- | scripts/Makefile.spl | 1 | ||||
-rw-r--r-- | test/dm/Makefile | 1 | ||||
-rw-r--r-- | test/dm/memory.c | 21 | ||||
-rw-r--r-- | test/py/requirements.txt | 1 | ||||
-rw-r--r-- | tools/image-host.c | 7 |
25 files changed, 2128 insertions, 26 deletions
diff --git a/Licenses/Exceptions b/Licenses/Exceptions index c9b3cd9..4f241f4 100644 --- a/Licenses/Exceptions +++ b/Licenses/Exceptions @@ -7,9 +7,13 @@ use U-Boot services by means of the jump table provided by U-Boot exactly for this purpose - this is merely considered normal use of U-Boot, and does *not* fall under the heading of "derived work". - The header files "include/image.h" and "arch/*/include/asm/u-boot.h" -define interfaces to U-Boot. Including these (unmodified) header -files in another file is considered normal use of U-Boot, and does -*not* fall under the heading of "derived work". +The following files define interfaces to U-Boot: + * include/image.h + * include/export.h + * arch/*/include/asm/u-boot.h + * examples/standalone/stubs.c + +Including these (unmodified) files in another file is considered normal +use of U-Boot, and does *not* fall under the heading of "derived work". -- Wolfgang Denk diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 70e27cd..db72c64 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -932,6 +932,10 @@ }; }; + memory-controller { + compatible = "sandbox,memory"; + }; + misc-test { #address-cells = <1>; #size-cells = <1>; diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index a461d59..1236989 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -260,10 +260,10 @@ static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset, char **err_msgp) { static char * const exc_prop[] = { - "data", - "data-size", - "data-position", - "data-offset" + FIT_DATA_PROP, + FIT_DATA_SIZE_PROP, + FIT_DATA_POSITION_PROP, + FIT_DATA_OFFSET_PROP, }; const char *prop, *end, *name; diff --git a/common/spl/Kconfig b/common/spl/Kconfig index b1b9e09..b738c74 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -796,6 +796,13 @@ config SPL_DM_MAILBOX this option to build the drivers in drivers/mailbox as part of SPL build. +config SPL_MEMORY + bool "Support Memory controller drivers" + help + Enable support for Memory Controller drivers within SPL. + These devices provide Memory bus interface to various devices like + SRAM, Ethernet adapters, FPGAs, etc. + config SPL_MMC bool "Support MMC" depends on MMC diff --git a/common/spl/spl_legacy.c b/common/spl/spl_legacy.c index b3624df..4c7f446 100644 --- a/common/spl/spl_legacy.c +++ b/common/spl/spl_legacy.c @@ -77,32 +77,29 @@ static inline int spl_image_get_comp(const struct legacy_img_hdr *hdr) int spl_load_legacy_img(struct spl_image_info *spl_image, struct spl_boot_device *bootdev, - struct spl_load_info *load, ulong header) + struct spl_load_info *load, ulong offset, + struct legacy_img_hdr *hdr) { __maybe_unused SizeT lzma_len; __maybe_unused void *src; - struct legacy_img_hdr hdr; ulong dataptr; int ret; - /* Read header into local struct */ - load->read(load, header, sizeof(hdr), &hdr); - /* * If the payload is compressed, the decompressed data should be * directly write to its load address. */ - if (spl_image_get_comp(&hdr) != IH_COMP_NONE) + if (spl_image_get_comp(hdr) != IH_COMP_NONE) spl_image->flags |= SPL_COPY_PAYLOAD_ONLY; - ret = spl_parse_image_header(spl_image, bootdev, &hdr); + ret = spl_parse_image_header(spl_image, bootdev, hdr); if (ret) return ret; /* Read image */ - switch (spl_image_get_comp(&hdr)) { + switch (spl_image_get_comp(hdr)) { case IH_COMP_NONE: - dataptr = header; + dataptr = offset; /* * Image header will be skipped only if SPL_COPY_PAYLOAD_ONLY @@ -119,7 +116,7 @@ int spl_load_legacy_img(struct spl_image_info *spl_image, lzma_len = LZMA_LEN; /* dataptr points to compressed payload */ - dataptr = header + sizeof(hdr); + dataptr = offset + sizeof(hdr); debug("LZMA: Decompressing %08lx to %08lx\n", dataptr, spl_image->load_addr); @@ -143,7 +140,7 @@ int spl_load_legacy_img(struct spl_image_info *spl_image, default: debug("Compression method %s is not supported\n", - genimg_get_comp_short_name(image_get_comp(&hdr))); + genimg_get_comp_short_name(image_get_comp(hdr))); return -EINVAL; } diff --git a/common/spl/spl_nand.c b/common/spl/spl_nand.c index a167388..4f4c00f 100644 --- a/common/spl/spl_nand.c +++ b/common/spl/spl_nand.c @@ -119,7 +119,7 @@ static int spl_nand_load_element(struct spl_image_info *spl_image, load.bl_len = 1; load.read = spl_nand_legacy_read; - return spl_load_legacy_img(spl_image, bootdev, &load, offset); + return spl_load_legacy_img(spl_image, bootdev, &load, offset, header); } else { err = spl_parse_image_header(spl_image, bootdev, header); if (err) diff --git a/common/spl/spl_nor.c b/common/spl/spl_nor.c index 281c613..eaa95fb 100644 --- a/common/spl/spl_nor.c +++ b/common/spl/spl_nor.c @@ -111,10 +111,14 @@ static int spl_nor_load_image(struct spl_image_info *spl_image, /* Legacy image handling */ if (IS_ENABLED(CONFIG_SPL_LEGACY_IMAGE_FORMAT)) { + struct legacy_img_hdr hdr; + load.bl_len = 1; load.read = spl_nor_load_read; + spl_nor_load_read(&load, spl_nor_get_uboot_base(), sizeof(hdr), &hdr); return spl_load_legacy_img(spl_image, bootdev, &load, - spl_nor_get_uboot_base()); + spl_nor_get_uboot_base(), + &hdr); } return 0; diff --git a/doc/device-tree-bindings/memory/ti,gpmc-child.yaml b/doc/device-tree-bindings/memory/ti,gpmc-child.yaml new file mode 100644 index 0000000..8e541ac --- /dev/null +++ b/doc/device-tree-bindings/memory/ti,gpmc-child.yaml @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/ti,gpmc-child.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: device tree bindings for children of the Texas Instruments GPMC + +maintainers: + - Tony Lindgren <tony@atomide.com> + - Roger Quadros <rogerq@kernel.org> + +description: + This binding is meant for the child nodes of the GPMC node. The node + represents any device connected to the GPMC bus. It may be a Flash chip, + RAM chip or Ethernet controller, etc. These properties are meant for + configuring the GPMC settings/timings and will accompany the bindings + supported by the respective device. + +properties: + reg: true + +# GPMC Timing properties for child nodes. All are optional and default to 0. + gpmc,sync-clk-ps: + description: Minimum clock period for synchronous mode + default: 0 + +# Chip-select signal timings corresponding to GPMC_CONFIG2: + gpmc,cs-on-ns: + description: Assertion time + default: 0 + + gpmc,cs-rd-off-ns: + description: Read deassertion time + default: 0 + + gpmc,cs-wr-off-ns: + description: Write deassertion time + default: 0 + +# ADV signal timings corresponding to GPMC_CONFIG3: + gpmc,adv-on-ns: + description: Assertion time + default: 0 + + gpmc,adv-rd-off-ns: + description: Read deassertion time + default: 0 + + gpmc,adv-wr-off-ns: + description: Write deassertion time + default: 0 + + gpmc,adv-aad-mux-on-ns: + description: Assertion time for AAD + default: 0 + + gpmc,adv-aad-mux-rd-off-ns: + description: Read deassertion time for AAD + default: 0 + + gpmc,adv-aad-mux-wr-off-ns: + description: Write deassertion time for AAD + default: 0 + +# WE signals timings corresponding to GPMC_CONFIG4: + gpmc,we-on-ns: + description: Assertion time + default: 0 + + gpmc,we-off-ns: + description: Deassertion time + default: 0 + +# OE signals timings corresponding to GPMC_CONFIG4: + gpmc,oe-on-ns: + description: Assertion time + default: 0 + + gpmc,oe-off-ns: + description: Deassertion time + default: 0 + + gpmc,oe-aad-mux-on-ns: + description: Assertion time for AAD + default: 0 + + gpmc,oe-aad-mux-off-ns: + description: Deassertion time for AAD + default: 0 + +# Access time and cycle time timings (in nanoseconds) corresponding to +# GPMC_CONFIG5: + gpmc,page-burst-access-ns: + description: Multiple access word delay + default: 0 + + gpmc,access-ns: + description: Start-cycle to first data valid delay + default: 0 + + gpmc,rd-cycle-ns: + description: Total read cycle time + default: 0 + + gpmc,wr-cycle-ns: + description: Total write cycle time + default: 0 + + gpmc,bus-turnaround-ns: + description: Turn-around time between successive accesses + default: 0 + + gpmc,cycle2cycle-delay-ns: + description: Delay between chip-select pulses + default: 0 + + gpmc,clk-activation-ns: + description: GPMC clock activation time + default: 0 + + gpmc,wait-monitoring-ns: + description: Start of wait monitoring with regard to valid data + default: 0 + +# Boolean timing parameters. If property is present, parameter is enabled +# otherwise disabled. + gpmc,adv-extra-delay: + description: ADV signal is delayed by half GPMC clock + type: boolean + + gpmc,cs-extra-delay: + description: CS signal is delayed by half GPMC clock + type: boolean + + gpmc,cycle2cycle-diffcsen: + description: | + Add "cycle2cycle-delay" between successive accesses + to a different CS + type: boolean + + gpmc,cycle2cycle-samecsen: + description: | + Add "cycle2cycle-delay" between successive accesses + to the same CS + type: boolean + + gpmc,oe-extra-delay: + description: OE signal is delayed by half GPMC clock + type: boolean + + gpmc,we-extra-delay: + description: WE signal is delayed by half GPMC clock + type: boolean + + gpmc,time-para-granularity: + description: Multiply all access times by 2 + type: boolean + +# The following two properties are applicable only to OMAP3+ and AM335x: + gpmc,wr-access-ns: + description: | + In synchronous write mode, for single or + burst accesses, defines the number of + GPMC_FCLK cycles from start access time + to the GPMC_CLK rising edge used by the + memory device for the first data capture. + default: 0 + + gpmc,wr-data-mux-bus-ns: + description: | + In address-data multiplex mode, specifies + the time when the first data is driven on + the address-data bus. + default: 0 + +# GPMC chip-select settings properties for child nodes. All are optional. + gpmc,burst-length: + description: Page/burst length. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 4, 8, 16] + default: 0 + + gpmc,burst-wrap: + description: Enables wrap bursting + type: boolean + + gpmc,burst-read: + description: Enables read page/burst mode + type: boolean + + gpmc,burst-write: + description: Enables write page/burst mode + type: boolean + + gpmc,device-width: + description: | + Total width of device(s) connected to a GPMC + chip-select in bytes. The GPMC supports 8-bit + and 16-bit devices and so this property must be + 1 or 2. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + default: 1 + + gpmc,mux-add-data: + description: | + Address and data multiplexing configuration. + Valid values are + 0 for Non multiplexed mode + 1 for address-address-data multiplexing mode and + 2 for address-data multiplexing mode. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + gpmc,sync-read: + description: | + Enables synchronous read. Defaults to asynchronous + is this is not set. + type: boolean + + gpmc,sync-write: + description: | + Enables synchronous writes. Defaults to asynchronous + is this is not set. + type: boolean + + gpmc,wait-pin: + description: | + Wait-pin used by client. Must be less than "gpmc,num-waitpins". + $ref: /schemas/types.yaml#/definitions/uint32 + + gpmc,wait-pin-polarity: + description: | + Set the desired polarity for the selected wait pin. + 0 for active low, 1 for active high. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + + gpmc,wait-on-read: + description: Enables wait monitoring on reads. + type: boolean + + gpmc,wait-on-write: + description: Enables wait monitoring on writes. + type: boolean + +required: + - reg + +# the GPMC child will have its own native properties +additionalProperties: true diff --git a/doc/device-tree-bindings/memory/ti,gpmc.yaml b/doc/device-tree-bindings/memory/ti,gpmc.yaml new file mode 100644 index 0000000..e188a4b --- /dev/null +++ b/doc/device-tree-bindings/memory/ti,gpmc.yaml @@ -0,0 +1,190 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/ti,gpmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments GPMC Memory Controller device-tree bindings + +maintainers: + - Tony Lindgren <tony@atomide.com> + - Roger Quadros <rogerq@kernel.org> + +description: + The GPMC is a unified memory controller dedicated for interfacing + with external memory devices like + - Asynchronous SRAM-like memories and ASICs + - Asynchronous, synchronous, and page mode burst NOR flash + - NAND flash + - Pseudo-SRAM devices + +properties: + compatible: + items: + - enum: + - ti,am3352-gpmc + - ti,am64-gpmc + - ti,omap2420-gpmc + - ti,omap2430-gpmc + - ti,omap3430-gpmc + - ti,omap4430-gpmc + + reg: + minItems: 1 + maxItems: 2 + + reg-names: + items: + - const: cfg + - const: data + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + description: | + Functional clock. Used for bus timing calculations and + GPMC configuration. + + clock-names: + items: + - const: fck + + power-domains: + maxItems: 1 + + dmas: + items: + - description: DMA channel for GPMC NAND prefetch + + dma-names: + items: + - const: rxtx + + "#address-cells": true + + "#size-cells": true + + gpmc,num-cs: + description: maximum number of supported chip-select lines. + $ref: /schemas/types.yaml#/definitions/uint32 + + gpmc,num-waitpins: + description: maximum number of supported wait pins. + $ref: /schemas/types.yaml#/definitions/uint32 + + ranges: + minItems: 1 + description: | + Must be set up to reflect the memory layout with four + integer values for each chip-select line in use, + <cs-number> 0 <physical address of mapping> <size> + items: + - description: NAND bank 0 + - description: NOR/SRAM bank 0 + - description: NOR/SRAM bank 1 + + '#interrupt-cells': + const: 2 + + interrupt-controller: + description: | + The GPMC driver implements and interrupt controller for + the NAND events "fifoevent" and "termcount" plus the + rising/falling edges on the GPMC_WAIT pins. + The interrupt number mapping is as follows + 0 - NAND_fifoevent + 1 - NAND_termcount + 2 - GPMC_WAIT0 pin edge + 3 - GPMC_WAIT1 pin edge, and so on. + + '#gpio-cells': + const: 2 + + gpio-controller: + description: | + The GPMC driver implements a GPIO controller for the + GPMC WAIT pins that can be used as general purpose inputs. + 0 maps to GPMC_WAIT0 pin. + + ti,hwmods: + description: + Name of the HWMOD associated with GPMC. This is for legacy + omap2/3 platforms only. + $ref: /schemas/types.yaml#/definitions/string + deprecated: true + + ti,no-idle-on-init: + description: + Prevent idling the module at init. This is for legacy omap2/3 + platforms only. + type: boolean + deprecated: true + +patternProperties: + "@[0-7],[a-f0-9]+$": + type: object + description: | + The child device node represents the device connected to the GPMC + bus. The device can be a NAND chip, SRAM device, NOR device + or an ASIC. + $ref: "ti,gpmc-child.yaml" + + +required: + - compatible + - reg + - gpmc,num-cs + - gpmc,num-waitpins + - "#address-cells" + - "#size-cells" + +allOf: + - if: + properties: + compatible: + contains: + const: ti,am64-gpmc + then: + required: + - reg-names + - power-domains + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/gpio/gpio.h> + + gpmc: memory-controller@50000000 { + compatible = "ti,am3352-gpmc"; + reg = <0x50000000 0x2000>; + interrupts = <100>; + clocks = <&l3s_clkctrl>; + clock-names = "fck"; + dmas = <&edma 52 0>; + dma-names = "rxtx"; + gpmc,num-cs = <8>; + gpmc,num-waitpins = <2>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + + nand@0,0 { + compatible = "ti,omap2-nand"; + reg = <0 0 4>; + interrupt-parent = <&gpmc>; + interrupts = <0 IRQ_TYPE_NONE>, /* fifoevent */ + <1 IRQ_TYPE_NONE>; /* termcount */ + ti,nand-xfer-type = "prefetch-dma"; + ti,nand-ecc-opt = "bch16"; + ti,elm-id = <&elm>; + rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 pin */ + }; + }; diff --git a/drivers/crypto/hash/Kconfig b/drivers/crypto/hash/Kconfig index bf9540e..aa355c4 100644 --- a/drivers/crypto/hash/Kconfig +++ b/drivers/crypto/hash/Kconfig @@ -10,7 +10,8 @@ config HASH_SOFTWARE depends on MD5 depends on SHA1 depends on SHA256 - depends on SHA512_ALGO + depends on SHA384 + depends on SHA512 help Enable driver for hashing operations in software. Currently it support multiple hash algorithm including CRC/MD5/SHA. diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 7271892..56b89f1 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -4,6 +4,23 @@ menu "Memory Controller drivers" +config MEMORY + bool "Enable Driver Model for Memory Controller drivers" + depends on DM + help + Enable driver model for Memory Controller devices. + These devices provide Memory bus interface to various devices like + SRAM, Ethernet adapters, FPGAs, etc. + For now this uclass has no methods yet. + +config SANDBOX_MEMORY + bool "Enable Sandbox Memory Controller driver" + depends on SANDBOX && MEMORY + help + This is a driver model based Memory Controller driver for sandbox. + Currently it is a stub only, as there are no usable uclass methods + yet. + config STM32_FMC2_EBI bool "Support for FMC2 External Bus Interface on STM32MP SoCs" depends on ARCH_STM32MP @@ -24,4 +41,23 @@ config TI_AEMIF of 256M bytes of any of these memories can be accessed at a given time via four chip selects with 64M byte access per chip select. +config TI_GPMC + bool "Texas Instruments GPMC driver" + depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 + depends on MEMORY && CLK && OF_CONTROL + help + This driver is for the General Purpose Memory Controller (GPMC) + present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows + interfacing to a variety of asynchronous as well as synchronous + memory drives like NOR, NAND, OneNAND, SRAM. + +if TI_GPMC +config TI_GPMC_DEBUG + bool "Debug Texas Instruments GPMC timings" + default n + help + Enable this to print GPMC timings before and after the GPMC registers + are programmed. This should not be left enabled on production systems. +endif + endmenu diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index fec52ef..2b196d7 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -1,3 +1,6 @@ +obj-$(CONFIG_MEMORY) += memory-uclass.o +obj-$(CONFIG_SANDBOX_MEMORY) += memory-sandbox.o obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o obj-$(CONFIG_TI_AEMIF) += ti-aemif.o +obj-$(CONFIG_TI_GPMC) += ti-gpmc.o diff --git a/drivers/memory/memory-sandbox.c b/drivers/memory/memory-sandbox.c new file mode 100644 index 0000000..f2ede50 --- /dev/null +++ b/drivers/memory/memory-sandbox.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 + * Texas Instruments Incorporated, <www.ti.com> + */ + +#include <dm.h> + +static const struct udevice_id sandbox_memory_match[] = { + { .compatible = "sandbox,memory" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sandbox_memory) = { + .name = "sandbox_memory", + .id = UCLASS_MEMORY, + .of_match = sandbox_memory_match, +}; diff --git a/drivers/memory/memory-uclass.c b/drivers/memory/memory-uclass.c new file mode 100644 index 0000000..d6d37fe --- /dev/null +++ b/drivers/memory/memory-uclass.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 + * Texas Instruments Incorporated, <www.ti.com> + */ + +#include <dm.h> + +UCLASS_DRIVER(memory) = { + .name = "memory", + .id = UCLASS_MEMORY, + .post_bind = dm_scan_fdt_dev, +}; diff --git a/drivers/memory/ti-gpmc.c b/drivers/memory/ti-gpmc.c new file mode 100644 index 0000000..f511a52 --- /dev/null +++ b/drivers/memory/ti-gpmc.c @@ -0,0 +1,1240 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments GPMC Driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <linux/mtd/omap_gpmc.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include "ti-gpmc.h" + +enum gpmc_clk_domain { + GPMC_CD_FCLK, + GPMC_CD_CLK +}; + +struct gpmc_cs_data { + const char *name; +#define GPMC_CS_RESERVED BIT(0) + u32 flags; +}; + +struct ti_gpmc { + void __iomem *base; + u32 cs_num; + u32 nr_waitpins; + struct clk *l3_clk; + u32 capability_flags; + struct resource data; +}; + +static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM]; +static unsigned int gpmc_cs_num = GPMC_CS_NUM; +static unsigned int gpmc_nr_waitpins; +static unsigned int gpmc_capability; +static void __iomem *gpmc_base; +static struct clk *gpmc_l3_clk; + +/* Public, as required by nand/raw/omap_gpmc.c */ +const struct gpmc *gpmc_cfg; + +/* + * The first 1MB of GPMC address space is typically mapped to + * the internal ROM. Never allocate the first page, to + * facilitate bug detection; even if we didn't boot from ROM. + * As GPMC minimum partition size is 16MB we can only start from + * there. + */ +#define GPMC_MEM_START 0x1000000 +#define GPMC_MEM_END 0x3FFFFFFF + +static void gpmc_write_reg(int idx, u32 val) +{ + writel_relaxed(val, gpmc_base + idx); +} + +static u32 gpmc_read_reg(int idx) +{ + return readl_relaxed(gpmc_base + idx); +} + +static void gpmc_cs_write_reg(int cs, int idx, u32 val) +{ + void __iomem *reg_addr; + + reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; + writel_relaxed(val, reg_addr); +} + +static u32 gpmc_cs_read_reg(int cs, int idx) +{ + void __iomem *reg_addr; + + reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; + return readl_relaxed(reg_addr); +} + +static unsigned long gpmc_get_fclk_period(void) +{ + unsigned long rate = clk_get_rate(gpmc_l3_clk); + + rate /= 1000; + rate = 1000000000 / rate; /* In picoseconds */ + + return rate; +} + +/** + * gpmc_get_clk_period - get period of selected clock domain in ps + * @cs: Chip Select Region. + * @cd: Clock Domain. + * + * GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup + * prior to calling this function with GPMC_CD_CLK. + */ +static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd) +{ + unsigned long tick_ps = gpmc_get_fclk_period(); + u32 l; + int div; + + switch (cd) { + case GPMC_CD_CLK: + /* get current clk divider */ + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + div = (l & 0x03) + 1; + /* get GPMC_CLK period */ + tick_ps *= div; + break; + case GPMC_CD_FCLK: + default: + break; + } + + return tick_ps; +} + +static unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs, + enum gpmc_clk_domain cd) +{ + unsigned long tick_ps; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = gpmc_get_clk_period(cs, cd); + + return (time_ns * 1000 + tick_ps - 1) / tick_ps; +} + +static unsigned int gpmc_ns_to_ticks(unsigned int time_ns) +{ + return gpmc_ns_to_clk_ticks(time_ns, /* any CS */ 0, GPMC_CD_FCLK); +} + +static unsigned int gpmc_ps_to_ticks(unsigned int time_ps) +{ + unsigned long tick_ps; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = gpmc_get_fclk_period(); + + return (time_ps + tick_ps - 1) / tick_ps; +} + +static __maybe_unused unsigned int gpmc_clk_ticks_to_ns(unsigned int ticks, int cs, + enum gpmc_clk_domain cd) +{ + return ticks * gpmc_get_clk_period(cs, cd) / 1000; +} + +static inline void gpmc_cs_modify_reg(int cs, int reg, u32 mask, bool value) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, reg); + if (value) + l |= mask; + else + l &= ~mask; + gpmc_cs_write_reg(cs, reg, l); +} + +static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p) +{ + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG1, + GPMC_CONFIG1_TIME_PARA_GRAN, + p->time_para_granularity); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG2, + GPMC_CONFIG2_CSEXTRADELAY, p->cs_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG3, + GPMC_CONFIG3_ADVEXTRADELAY, p->adv_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4, + GPMC_CONFIG4_OEEXTRADELAY, p->oe_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4, + GPMC_CONFIG4_WEEXTRADELAY, p->we_extra_delay); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6, + GPMC_CONFIG6_CYCLE2CYCLESAMECSEN, + p->cycle2cyclesamecsen); + gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6, + GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN, + p->cycle2cyclediffcsen); +} + +#if IS_ENABLED(CONFIG_TI_GPMC_DEBUG) +/** + * get_gpmc_timing_reg - read a timing parameter and print DTS settings for it. + * @cs: Chip Select Region + * @reg: GPMC_CS_CONFIGn register offset. + * @st_bit: Start Bit + * @end_bit: End Bit. Must be >= @st_bit. + * @max: Maximum parameter value (before optional @shift). + * If 0, maximum is as high as @st_bit and @end_bit allow. + * @name: DTS node name, w/o "gpmc," + * @cd: Clock Domain of timing parameter. + * @shift: Parameter value left shifts @shift, which is then printed instead of value. + * @raw: Raw Format Option. + * raw format: gpmc,name = <value> + * tick format: gpmc,name = <value> /‍* x ns -- y ns; x ticks *‍/ + * Where x ns -- y ns result in the same tick value. + * When @max is exceeded, "invalid" is printed inside comment. + * @noval: Parameter values equal to 0 are not printed. + * @return: Specified timing parameter (after optional @shift). + * + */ +static int get_gpmc_timing_reg(/* timing specifiers */ + int cs, int reg, int st_bit, int end_bit, int max, + const char *name, const enum gpmc_clk_domain cd, + /* value transform */ + int shift, + /* format specifiers */ + bool raw, bool noval) +{ + u32 l; + int nr_bits; + int mask; + bool invalid; + + l = gpmc_cs_read_reg(cs, reg); + nr_bits = end_bit - st_bit + 1; + mask = (1 << nr_bits) - 1; + l = (l >> st_bit) & mask; + if (!max) + max = mask; + invalid = l > max; + if (shift) + l = (shift << l); + if (noval && l == 0) + return 0; + if (!raw) { + /* DTS tick format for timings in ns */ + unsigned int time_ns; + unsigned int time_ns_min = 0; + + if (l) + time_ns_min = gpmc_clk_ticks_to_ns(l - 1, cs, cd) + 1; + time_ns = gpmc_clk_ticks_to_ns(l, cs, cd); + pr_info("gpmc,%s = <%u>; /* %u ns - %u ns; %i ticks%s*/\n", + name, time_ns, time_ns_min, time_ns, l, + invalid ? "; invalid " : " "); + } else { + /* raw format */ + pr_info("gpmc,%s = <%u>;%s\n", name, l, + invalid ? " /* invalid */" : ""); + } + + return l; +} + +#define GPMC_PRINT_CONFIG(cs, config) \ + pr_info("CS%i %s: 0x%08x\n", cs, #config, \ + gpmc_cs_read_reg(cs, config)) +#define GPMC_GET_RAW(reg, st, end, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, GPMC_CD_FCLK, 0, 1, 0) +#define GPMC_GET_RAW_MAX(reg, st, end, max, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), (max), field, GPMC_CD_FCLK, 0, 1, 0) +#define GPMC_GET_RAW_BOOL(reg, st, end, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, GPMC_CD_FCLK, 0, 1, 1) +#define GPMC_GET_RAW_SHIFT_MAX(reg, st, end, shift, max, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), (max), field, GPMC_CD_FCLK, (shift), 1, 1) +#define GPMC_GET_TICKS(reg, st, end, field) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, GPMC_CD_FCLK, 0, 0, 0) +#define GPMC_GET_TICKS_CD(reg, st, end, field, cd) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, (cd), 0, 0, 0) +#define GPMC_GET_TICKS_CD_MAX(reg, st, end, max, field, cd) \ + get_gpmc_timing_reg(cs, (reg), (st), (end), (max), field, (cd), 0, 0, 0) + +static void gpmc_show_regs(int cs, const char *desc) +{ + pr_info("gpmc cs%i %s:\n", cs, desc); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG1); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG2); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG3); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG4); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG5); + GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG6); +} + +/* + * Note that gpmc,wait-pin handing wrongly assumes bit 8 is available, + * see commit c9fb809. + */ +static void gpmc_cs_show_timings(int cs, const char *desc) +{ + gpmc_show_regs(cs, desc); + + pr_info("gpmc cs%i access configuration:\n", cs); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity"); + GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data"); + GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 12, 13, 1, + GPMC_CONFIG1_DEVICESIZE_MAX, "device-width"); + GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read"); + GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 23, 24, 4, + GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX, + "burst-length"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 27, 27, "sync-write"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 28, 28, "burst-write"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 29, 29, "gpmc,sync-read"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 30, 30, "burst-read"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 31, 31, "burst-wrap"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG2, 7, 7, "cs-extra-delay"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG3, 7, 7, "adv-extra-delay"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 23, 23, "we-extra-delay"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 7, 7, "oe-extra-delay"); + + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 7, 7, "cycle2cycle-samecsen"); + GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 6, 6, "cycle2cycle-diffcsen"); + + pr_info("gpmc cs%i timings configuration:\n", cs); + GPMC_GET_TICKS(GPMC_CS_CONFIG2, 0, 3, "cs-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG2, 8, 12, "cs-rd-off-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG2, 16, 20, "cs-wr-off-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 0, 3, "adv-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 8, 12, "adv-rd-off-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 16, 20, "adv-wr-off-ns"); + if (gpmc_capability & GPMC_HAS_MUX_AAD) { + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 4, 6, "adv-aad-mux-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 24, 26, + "adv-aad-mux-rd-off-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG3, 28, 30, + "adv-aad-mux-wr-off-ns"); + } + + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 0, 3, "oe-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 8, 12, "oe-off-ns"); + if (gpmc_capability & GPMC_HAS_MUX_AAD) { + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 4, 6, "oe-aad-mux-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 13, 15, "oe-aad-mux-off-ns"); + } + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 16, 19, "we-on-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG4, 24, 28, "we-off-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 0, 4, "rd-cycle-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 8, 12, "wr-cycle-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 16, 20, "access-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG5, 24, 27, "page-burst-access-ns"); + + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns"); + + GPMC_GET_TICKS_CD_MAX(GPMC_CS_CONFIG1, 18, 19, + GPMC_CONFIG1_WAITMONITORINGTIME_MAX, + "wait-monitoring-ns", GPMC_CD_CLK); + GPMC_GET_TICKS_CD_MAX(GPMC_CS_CONFIG1, 25, 26, + GPMC_CONFIG1_CLKACTIVATIONTIME_MAX, + "clk-activation-ns", GPMC_CD_FCLK); + + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns"); + GPMC_GET_TICKS(GPMC_CS_CONFIG6, 24, 28, "wr-access-ns"); +} +#else +static inline void gpmc_cs_show_timings(int cs, const char *desc) +{ +} +#endif + +/** + * set_gpmc_timing_reg - set a single timing parameter for Chip Select Region. + * Caller is expected to have initialized CONFIG1 GPMCFCLKDIVIDER + * prior to calling this function with @cd equal to GPMC_CD_CLK. + * + * @cs: Chip Select Region. + * @reg: GPMC_CS_CONFIGn register offset. + * @st_bit: Start Bit + * @end_bit: End Bit. Must be >= @st_bit. + * @max: Maximum parameter value. + * If 0, maximum is as high as @st_bit and @end_bit allow. + * @time: Timing parameter in ns. + * @cd: Timing parameter clock domain. + * @name: Timing parameter name. + * @return: 0 on success, -1 on error. + */ +static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max, + int time, enum gpmc_clk_domain cd, const char *name) +{ + u32 l; + int ticks, mask, nr_bits; + + if (time == 0) + ticks = 0; + else + ticks = gpmc_ns_to_clk_ticks(time, cs, cd); + nr_bits = end_bit - st_bit + 1; + mask = (1 << nr_bits) - 1; + + if (!max) + max = mask; + + if (ticks > max) { + pr_err("%s: GPMC CS%d: %s %d ns, %d ticks > %d ticks\n", + __func__, cs, name, time, ticks, max); + + return -1; + } + + l = gpmc_cs_read_reg(cs, reg); + if (IS_ENABLED(CONFIG_TI_GPMC_DEBUG)) { + pr_info("GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n", + cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000, + (l >> st_bit) & mask, time); + } + + l &= ~(mask << st_bit); + l |= ticks << st_bit; + gpmc_cs_write_reg(cs, reg, l); + + return 0; +} + +/** + * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME + * WAITMONITORINGTIME will be _at least_ as long as desired, i.e. + * read --> don't sample bus too early + * write --> data is longer on bus + * + * Formula: + * gpmc_clk_div + 1 = ceil(ceil(waitmonitoringtime_ns / gpmc_fclk_ns) + * / waitmonitoring_ticks) + * WAITMONITORINGTIME resulting in 0 or 1 tick with div = 1 are caught by + * div <= 0 check. + * + * @wait_monitoring: WAITMONITORINGTIME in ns. + * @return: -1 on failure to scale, else proper divider > 0. + */ +static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring) +{ + int div = gpmc_ns_to_ticks(wait_monitoring); + + div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1; + div /= GPMC_CONFIG1_WAITMONITORINGTIME_MAX; + + if (div > 4) + return -1; + if (div <= 0) + div = 1; + + return div; +} + +/** + * gpmc_calc_divider - calculate GPMC_FCLK divider for sync_clk GPMC_CLK period. + * @sync_clk: GPMC_CLK period in ps. + * @return: Returns at least 1 if GPMC_FCLK can be divided to GPMC_CLK. + * Else, returns -1. + */ +static int gpmc_calc_divider(unsigned int sync_clk) +{ + int div = gpmc_ps_to_ticks(sync_clk); + + if (div > 4) + return -1; + if (div <= 0) + div = 1; + + return div; +} + +/** + * gpmc_cs_set_timings - program timing parameters for Chip Select Region. + * @cs: Chip Select Region. + * @t: GPMC timing parameters. + * @s: GPMC timing settings. + * @return: 0 on success, -1 on error. + */ +static int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, + const struct gpmc_settings *s) +{ + int div, ret; + u32 l; + + div = gpmc_calc_divider(t->sync_clk); + if (div < 0) + return -EINVAL; + + /* + * See if we need to change the divider for waitmonitoringtime. + * + * Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for + * pure asynchronous accesses, i.e. both read and write asynchronous. + * However, only do so if WAITMONITORINGTIME is actually used, i.e. + * either WAITREADMONITORING or WAITWRITEMONITORING is set. + * + * This statement must not change div to scale async WAITMONITORINGTIME + * to protect mixed synchronous and asynchronous accesses. + * + * We raise an error later if WAITMONITORINGTIME does not fit. + */ + if (!s->sync_read && !s->sync_write && + (s->wait_on_read || s->wait_on_write) + ) { + div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring); + if (div < 0) { + pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n", + __func__, + t->wait_monitoring + ); + return -ENXIO; + } + } + + ret = 0; + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 0, 3, 0, t->cs_on, + GPMC_CD_FCLK, "cs_on"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 8, 12, 0, t->cs_rd_off, + GPMC_CD_FCLK, "cs_rd_off"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 16, 20, 0, t->cs_wr_off, + GPMC_CD_FCLK, "cs_wr_off"); + if (ret) + return -ENXIO; + + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 0, 3, 0, t->adv_on, + GPMC_CD_FCLK, "adv_on"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 8, 12, 0, t->adv_rd_off, + GPMC_CD_FCLK, "adv_rd_off"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 16, 20, 0, t->adv_wr_off, + GPMC_CD_FCLK, "adv_wr_off"); + if (ret) + return -ENXIO; + + if (gpmc_capability & GPMC_HAS_MUX_AAD) { + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 4, 6, 0, + t->adv_aad_mux_on, GPMC_CD_FCLK, + "adv_aad_mux_on"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 24, 26, 0, + t->adv_aad_mux_rd_off, GPMC_CD_FCLK, + "adv_aad_mux_rd_off"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 28, 30, 0, + t->adv_aad_mux_wr_off, GPMC_CD_FCLK, + "adv_aad_mux_wr_off"); + if (ret) + return -ENXIO; + } + + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 0, 3, 0, t->oe_on, + GPMC_CD_FCLK, "oe_on"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 8, 12, 0, t->oe_off, + GPMC_CD_FCLK, "oe_off"); + if (gpmc_capability & GPMC_HAS_MUX_AAD) { + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 4, 6, 0, + t->oe_aad_mux_on, GPMC_CD_FCLK, + "oe_aad_mux_on"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 13, 15, 0, + t->oe_aad_mux_off, GPMC_CD_FCLK, + "oe_aad_mux_off"); + } + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 16, 19, 0, t->we_on, + GPMC_CD_FCLK, "we_on"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 24, 28, 0, t->we_off, + GPMC_CD_FCLK, "we_off"); + if (ret) + return -ENXIO; + + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 0, 4, 0, t->rd_cycle, + GPMC_CD_FCLK, "rd_cycle"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 8, 12, 0, t->wr_cycle, + GPMC_CD_FCLK, "wr_cycle"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 16, 20, 0, t->access, + GPMC_CD_FCLK, "access"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 24, 27, 0, + t->page_burst_access, GPMC_CD_FCLK, + "page_burst_access"); + if (ret) + return -ENXIO; + + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 0, 3, 0, + t->bus_turnaround, GPMC_CD_FCLK, + "bus_turnaround"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 8, 11, 0, + t->cycle2cycle_delay, GPMC_CD_FCLK, + "cycle2cycle_delay"); + if (ret) + return -ENXIO; + + if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) { + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 16, 19, 0, + t->wr_data_mux_bus, GPMC_CD_FCLK, + "wr_data_mux_bus"); + if (ret) + return -ENXIO; + } + if (gpmc_capability & GPMC_HAS_WR_ACCESS) { + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 24, 28, 0, + t->wr_access, GPMC_CD_FCLK, + "wr_access"); + if (ret) + return -ENXIO; + } + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + l &= ~0x03; + l |= (div - 1); + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l); + + ret = 0; + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 18, 19, + GPMC_CONFIG1_WAITMONITORINGTIME_MAX, + t->wait_monitoring, GPMC_CD_CLK, + "wait_monitoring"); + ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 25, 26, + GPMC_CONFIG1_CLKACTIVATIONTIME_MAX, + t->clk_activation, GPMC_CD_FCLK, + "clk_activation"); + if (ret) + return -ENXIO; + + if (IS_ENABLED(CONFIG_TI_GPMC_DEBUG)) { + pr_info("GPMC CS%d CLK period is %lu ns (div %d)\n", + cs, (div * gpmc_get_fclk_period()) / 1000, div); + } + + gpmc_cs_bool_timings(cs, &t->bool_timings); + gpmc_cs_show_timings(cs, "after gpmc_set_timings"); + + return 0; +} + +static int gpmc_cs_set_memconf(int cs, resource_size_t base, u32 size) +{ + u32 l; + u32 mask; + + /* + * Ensure that base address is aligned on a + * boundary equal to or greater than size. + */ + if (base & (size - 1)) + return -EINVAL; + + base >>= GPMC_CHUNK_SHIFT; + mask = (1 << GPMC_SECTION_SHIFT) - size; + mask >>= GPMC_CHUNK_SHIFT; + mask <<= GPMC_CONFIG7_MASKADDRESS_OFFSET; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l &= ~GPMC_CONFIG7_MASK; + l |= base & GPMC_CONFIG7_BASEADDRESS_MASK; + l |= mask & GPMC_CONFIG7_MASKADDRESS_MASK; + l |= GPMC_CONFIG7_CSVALID; + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); + + return 0; +} + +static void gpmc_cs_enable_mem(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l |= GPMC_CONFIG7_CSVALID; + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); +} + +static void gpmc_cs_disable_mem(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l &= ~GPMC_CONFIG7_CSVALID; + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); +} + +static void gpmc_cs_set_reserved(int cs, int reserved) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + gpmc->flags |= GPMC_CS_RESERVED; +} + +static bool gpmc_cs_reserved(int cs) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + return gpmc->flags & GPMC_CS_RESERVED; +} + +static unsigned long gpmc_mem_align(unsigned long size) +{ + int order; + + size = (size - 1) >> (GPMC_CHUNK_SHIFT - 1); + order = GPMC_CHUNK_SHIFT - 1; + do { + size >>= 1; + order++; + } while (size); + size = 1 << order; + return size; +} + +static int gpmc_cs_request(ofnode node, int cs, struct resource *res) +{ + int r = -1; + u32 size; + resource_size_t addr_base = res->start; + + if (cs >= gpmc_cs_num) { + pr_err("%s: requested chip-select is disabled\n", __func__); + return -ENODEV; + } + + size = gpmc_mem_align(resource_size(res)); + if (size > (1 << GPMC_SECTION_SHIFT)) + return -ENOMEM; + + if (gpmc_cs_reserved(cs)) { + r = -EBUSY; + goto out; + } + + if (addr_base & (SZ_16M - 1)) { + pr_err("CS region should be aligned to 16M boundary\n"); + goto out; + } + + /* Disable CS while changing base address and size mask */ + gpmc_cs_disable_mem(cs); + + r = gpmc_cs_set_memconf(cs, addr_base, size); + if (r < 0) + goto out; + + /* Enable CS */ + gpmc_cs_enable_mem(cs); + gpmc_cs_set_reserved(cs, 1); +out: + return r; +} + +static void gpmc_cs_free(int cs) +{ + if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) { + pr_warn("Trying to free non-reserved GPMC CS%d\n", cs); + return; + } + + gpmc_cs_disable_mem(cs); + gpmc_cs_set_reserved(cs, 0); +} + +/** + * gpmc_configure - write request to configure gpmc + * @cmd: command type + * @wval: value to write + * @return status of the operation + */ +static int gpmc_configure(int cmd, int wval) +{ + u32 regval; + + switch (cmd) { + case GPMC_CONFIG_WP: + regval = gpmc_read_reg(GPMC_CONFIG); + if (wval) + regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */ + else + regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */ + gpmc_write_reg(GPMC_CONFIG, regval); + break; + + default: + pr_err("%s: command not supported\n", __func__); + return -EINVAL; + } + + return 0; +} + +/** + * gpmc_cs_program_settings - programs non-timing related settings + * @cs: GPMC chip-select to program + * @p: pointer to GPMC settings structure + * + * Programs non-timing related settings for a GPMC chip-select, such as + * bus-width, burst configuration, etc. Function should be called once + * for each chip-select that is being used and must be called before + * calling gpmc_cs_set_timings() as timing parameters in the CONFIG1 + * register will be initialised to zero by this function. Returns 0 on + * success and appropriate negative error code on failure. + */ +static int gpmc_cs_program_settings(int cs, struct gpmc_settings *p) +{ + u32 config1; + + if (!p->device_width || p->device_width > GPMC_DEVWIDTH_16BIT) { + pr_err("%s: invalid width %d!", __func__, p->device_width); + return -EINVAL; + } + + /* Address-data multiplexing not supported for NAND devices */ + if (p->device_nand && p->mux_add_data) { + pr_err("%s: invalid configuration!\n", __func__); + return -EINVAL; + } + + if (p->mux_add_data > GPMC_MUX_AD || + (p->mux_add_data == GPMC_MUX_AAD && + !(gpmc_capability & GPMC_HAS_MUX_AAD))) { + pr_err("%s: invalid multiplex configuration!\n", __func__); + return -EINVAL; + } + + /* Page/burst mode supports lengths of 4, 8 and 16 bytes */ + if (p->burst_read || p->burst_write) { + switch (p->burst_len) { + case GPMC_BURST_4: + case GPMC_BURST_8: + case GPMC_BURST_16: + break; + default: + pr_err("%s: invalid page/burst-length (%d)\n", + __func__, p->burst_len); + return -EINVAL; + } + } + + if (p->wait_pin > gpmc_nr_waitpins) { + pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin); + return -EINVAL; + } + + config1 = GPMC_CONFIG1_DEVICESIZE((p->device_width - 1)); + + if (p->sync_read) + config1 |= GPMC_CONFIG1_READTYPE_SYNC; + if (p->sync_write) + config1 |= GPMC_CONFIG1_WRITETYPE_SYNC; + if (p->wait_on_read) + config1 |= GPMC_CONFIG1_WAIT_READ_MON; + if (p->wait_on_write) + config1 |= GPMC_CONFIG1_WAIT_WRITE_MON; + if (p->wait_on_read || p->wait_on_write) + config1 |= GPMC_CONFIG1_WAIT_PIN_SEL(p->wait_pin); + if (p->device_nand) + config1 |= GPMC_CONFIG1_DEVICETYPE(GPMC_DEVICETYPE_NAND); + if (p->mux_add_data) + config1 |= GPMC_CONFIG1_MUXTYPE(p->mux_add_data); + if (p->burst_read) + config1 |= GPMC_CONFIG1_READMULTIPLE_SUPP; + if (p->burst_write) + config1 |= GPMC_CONFIG1_WRITEMULTIPLE_SUPP; + if (p->burst_read || p->burst_write) { + config1 |= GPMC_CONFIG1_PAGE_LEN(p->burst_len >> 3); + config1 |= p->burst_wrap ? GPMC_CONFIG1_WRAPBURST_SUPP : 0; + } + + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, config1); + + return 0; +} + +static void gpmc_cs_set_name(int cs, const char *name) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + gpmc->name = name; +} + +static const char *gpmc_cs_get_name(int cs) +{ + struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; + + return gpmc->name; +} + +/** + * gpmc_read_settings_dt - read gpmc settings from device-tree + * @np: pointer to device-tree node for a gpmc child device + * @p: pointer to gpmc settings structure + * + * Reads the GPMC settings for a GPMC child device from device-tree and + * stores them in the GPMC settings structure passed. The GPMC settings + * structure is initialised to zero by this function and so any + * previously stored settings will be cleared. + */ +static void gpmc_read_settings_dt(ofnode np, struct gpmc_settings *p) +{ + memset(p, 0, sizeof(struct gpmc_settings)); + + p->sync_read = ofnode_read_bool(np, "gpmc,sync-read"); + p->sync_write = ofnode_read_bool(np, "gpmc,sync-write"); + ofnode_read_u32(np, "gpmc,device-width", &p->device_width); + ofnode_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data); + + if (!ofnode_read_u32(np, "gpmc,burst-length", &p->burst_len)) { + p->burst_wrap = ofnode_read_bool(np, "gpmc,burst-wrap"); + p->burst_read = ofnode_read_bool(np, "gpmc,burst-read"); + p->burst_write = ofnode_read_bool(np, "gpmc,burst-write"); + if (!p->burst_read && !p->burst_write) + pr_warn("%s: page/burst-length set but not used!\n", + __func__); + } + + if (!ofnode_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) { + p->wait_on_read = ofnode_read_bool(np, + "gpmc,wait-on-read"); + p->wait_on_write = ofnode_read_bool(np, + "gpmc,wait-on-write"); + if (!p->wait_on_read && !p->wait_on_write) + pr_debug("%s: rd/wr wait monitoring not enabled!\n", + __func__); + } +} + +static void gpmc_read_timings_dt(ofnode np, + struct gpmc_timings *gpmc_t) +{ + struct gpmc_bool_timings *p; + + if (!gpmc_t) + return; + + memset(gpmc_t, 0, sizeof(*gpmc_t)); + + /* minimum clock period for syncronous mode */ + ofnode_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk); + + /* chip select timtings */ + ofnode_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on); + ofnode_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off); + ofnode_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off); + + /* ADV signal timings */ + ofnode_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on); + ofnode_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off); + ofnode_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off); + ofnode_read_u32(np, "gpmc,adv-aad-mux-on-ns", + &gpmc_t->adv_aad_mux_on); + ofnode_read_u32(np, "gpmc,adv-aad-mux-rd-off-ns", + &gpmc_t->adv_aad_mux_rd_off); + ofnode_read_u32(np, "gpmc,adv-aad-mux-wr-off-ns", + &gpmc_t->adv_aad_mux_wr_off); + + /* WE signal timings */ + ofnode_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on); + ofnode_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off); + + /* OE signal timings */ + ofnode_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on); + ofnode_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off); + ofnode_read_u32(np, "gpmc,oe-aad-mux-on-ns", + &gpmc_t->oe_aad_mux_on); + ofnode_read_u32(np, "gpmc,oe-aad-mux-off-ns", + &gpmc_t->oe_aad_mux_off); + + /* access and cycle timings */ + ofnode_read_u32(np, "gpmc,page-burst-access-ns", + &gpmc_t->page_burst_access); + ofnode_read_u32(np, "gpmc,access-ns", &gpmc_t->access); + ofnode_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle); + ofnode_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle); + ofnode_read_u32(np, "gpmc,bus-turnaround-ns", + &gpmc_t->bus_turnaround); + ofnode_read_u32(np, "gpmc,cycle2cycle-delay-ns", + &gpmc_t->cycle2cycle_delay); + ofnode_read_u32(np, "gpmc,wait-monitoring-ns", + &gpmc_t->wait_monitoring); + ofnode_read_u32(np, "gpmc,clk-activation-ns", + &gpmc_t->clk_activation); + + /* only applicable to OMAP3+ */ + ofnode_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access); + ofnode_read_u32(np, "gpmc,wr-data-mux-bus-ns", + &gpmc_t->wr_data_mux_bus); + + /* bool timing parameters */ + p = &gpmc_t->bool_timings; + + p->cycle2cyclediffcsen = + ofnode_read_bool(np, "gpmc,cycle2cycle-diffcsen"); + p->cycle2cyclesamecsen = + ofnode_read_bool(np, "gpmc,cycle2cycle-samecsen"); + p->we_extra_delay = ofnode_read_bool(np, "gpmc,we-extra-delay"); + p->oe_extra_delay = ofnode_read_bool(np, "gpmc,oe-extra-delay"); + p->adv_extra_delay = ofnode_read_bool(np, "gpmc,adv-extra-delay"); + p->cs_extra_delay = ofnode_read_bool(np, "gpmc,cs-extra-delay"); + p->time_para_granularity = + ofnode_read_bool(np, "gpmc,time-para-granularity"); +} + +/** + * gpmc_probe_generic_child - configures the gpmc for a child device + * @dev: pointer to gpmc platform device + * @child: pointer to device-tree node for child device + * + * Allocates and configures a GPMC chip-select for a child device. + * Returns 0 on success and appropriate negative error code on failure. + */ +static int gpmc_probe_generic_child(struct udevice *dev, + ofnode child) +{ + struct gpmc_settings gpmc_s; + struct gpmc_timings gpmc_t; + struct resource res; + const char *name; + int ret; + u32 val, cs; + + if (ofnode_read_u32(child, "reg", &cs) < 0) { + dev_err(dev, "can't get reg property of child %s\n", + ofnode_get_name(child)); + return -ENODEV; + } + + if (ofnode_read_resource(child, 0, &res) < 0) { + dev_err(dev, "%s has malformed 'reg' property\n", + ofnode_get_name(child)); + return -ENODEV; + } + + /* + * Check if we have multiple instances of the same device + * on a single chip select. If so, use the already initialized + * timings. + */ + name = gpmc_cs_get_name(cs); + if (name && !strcmp(name, ofnode_get_name(child))) + goto no_timings; + + ret = gpmc_cs_request(child, cs, &res); + if (ret < 0) { + dev_err(dev, "cannot request GPMC CS %d\n", cs); + return ret; + } + gpmc_cs_set_name(cs, ofnode_get_name(child)); + + gpmc_read_settings_dt(child, &gpmc_s); + gpmc_read_timings_dt(child, &gpmc_t); + + /* + * For some GPMC devices we still need to rely on the bootloader + * timings because the devices can be connected via FPGA. + * REVISIT: Add timing support from slls644g.pdf. + */ + if (!gpmc_t.cs_rd_off) { + pr_warn("enable GPMC debug to configure .dts timings for CS%i\n", + cs); + gpmc_cs_show_timings(cs, + "please add GPMC bootloader timings to .dts"); + goto no_timings; + } + + /* CS must be disabled while making changes to gpmc configuration */ + gpmc_cs_disable_mem(cs); + + if (!ofnode_read_u32(child, "nand-bus-width", &val)) { + /* NAND specific setup */ + ofnode_read_u32(child, "nand-bus-width", &val); + switch (val) { + case 8: + gpmc_s.device_width = GPMC_DEVWIDTH_8BIT; + break; + case 16: + gpmc_s.device_width = GPMC_DEVWIDTH_16BIT; + break; + default: + dev_err(dev, "%s: invalid 'nand-bus-width'\n", + ofnode_get_name(child)); + ret = -EINVAL; + goto err; + } + + /* disable write protect */ + gpmc_configure(GPMC_CONFIG_WP, 0); + gpmc_s.device_nand = true; + } else { + ret = ofnode_read_u32(child, "bank-width", + &gpmc_s.device_width); + if (ret < 0 && !gpmc_s.device_width) { + dev_err(dev, + "%s has no 'gpmc,device-width' property\n", + ofnode_get_name(child)); + goto err; + } + } + + gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); + + ret = gpmc_cs_program_settings(cs, &gpmc_s); + if (ret < 0) + goto err; + + ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); + if (ret) { + dev_err(dev, "failed to set gpmc timings for: %s\n", + ofnode_get_name(child)); + goto err; + } + + /* Clear limited address i.e. enable A26-A11 */ + val = gpmc_read_reg(GPMC_CONFIG); + val &= ~GPMC_CONFIG_LIMITEDADDRESS; + gpmc_write_reg(GPMC_CONFIG, val); + + /* Enable CS region */ + gpmc_cs_enable_mem(cs); + +no_timings: + + return 0; + +err: + gpmc_cs_free(cs); + + return ret; +} + +static void gpmc_probe_dt_children(struct udevice *dev) +{ + int ret; + ofnode child; + + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + ret = gpmc_probe_generic_child(dev, child); + if (ret) { + dev_err(dev, "Cannot parse child %s:%d", + ofnode_get_name(child), ret); + } + } +} + +static int gpmc_parse_dt(struct udevice *dev, struct ti_gpmc *gpmc) +{ + int ret; + u32 val; + + ret = ofnode_read_u32(dev_ofnode(dev), "gpmc,num-cs", + &val); + if (ret < 0) { + pr_err("%s: number of chip-selects not defined\n", __func__); + return ret; + } else if (val < 1) { + pr_err("%s: all chip-selects are disabled\n", __func__); + return -EINVAL; + } else if (val > GPMC_CS_NUM) { + pr_err("%s: number of supported chip-selects cannot be > %d\n", + __func__, GPMC_CS_NUM); + return -EINVAL; + } + + gpmc->cs_num = val; + gpmc_cs_num = val; + + ret = ofnode_read_u32(dev_ofnode(dev), "gpmc,num-waitpins", + &gpmc->nr_waitpins); + if (ret < 0) { + pr_err("%s: number of wait pins not found!\n", __func__); + return ret; + } + + gpmc_nr_waitpins = gpmc->nr_waitpins; + + return 0; +} + +static int gpmc_probe(struct udevice *dev) +{ + struct ti_gpmc *priv = dev_get_priv(dev); + int ret; + struct resource res; + + ret = dev_read_resource_byname(dev, "cfg", &res); + if (ret) { + /* Legacy DT */ + dev_read_resource(dev, 0, &res); + priv->base = devm_ioremap(dev, res.start, resource_size(&res)); + + priv->data.start = GPMC_MEM_START; + priv->data.end = GPMC_MEM_END; + } else { + priv->base = devm_ioremap(dev, res.start, resource_size(&res)); + ret = dev_read_resource_byname(dev, "data", &res); + if (ret) + return -ENOENT; + + priv->data = res; + } + + if (!priv->base) + return -ENOMEM; + + gpmc_cfg = (struct gpmc *)priv->base; + gpmc_base = priv->base; + + priv->l3_clk = devm_clk_get(dev, "fck"); + if (IS_ERR(priv->l3_clk)) + return PTR_ERR(priv->l3_clk); + + if (!clk_get_rate(priv->l3_clk)) + return -EINVAL; + + gpmc_l3_clk = priv->l3_clk; + + ret = gpmc_parse_dt(dev, priv); + if (ret) + return ret; + + priv->capability_flags = dev->driver->of_match->data; + gpmc_capability = priv->capability_flags; + + gpmc_probe_dt_children(dev); + + return 0; +} + +#define GPMC_DATA_REV2_4 0 +#define GPMC_DATA_REV5 (GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS) +#define GPMC_DATA_REV6 (GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS | GPMC_HAS_MUX_AAD) + +static const struct udevice_id gpmc_dt_ids[] = { + { .compatible = "ti,am64-gpmc", .data = GPMC_DATA_REV6, }, + { .compatible = "ti,am3352-gpmc", .data = GPMC_DATA_REV5, }, + { .compatible = "ti,omap2420-gpmc", .data = GPMC_DATA_REV2_4, }, + { .compatible = "ti,omap2430-gpmc", .data = GPMC_DATA_REV2_4, }, + { .compatible = "ti,omap3430-gpmc", .data = GPMC_DATA_REV5, }, + { .compatible = "ti,omap4430-gpmc", .data = GPMC_DATA_REV6, }, + { } /* sentinel */ +}; + +U_BOOT_DRIVER(ti_gpmc) = { + .name = "ti-gpmc", + .id = UCLASS_MEMORY, + .of_match = gpmc_dt_ids, + .probe = gpmc_probe, + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/memory/ti-gpmc.h b/drivers/memory/ti-gpmc.h new file mode 100644 index 0000000..90f8e65 --- /dev/null +++ b/drivers/memory/ti-gpmc.h @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Texas Instruments GPMC Driver + * + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/* GPMC register offsets */ +#define GPMC_REVISION 0x00 +#define GPMC_SYSCONFIG 0x10 +#define GPMC_SYSSTATUS 0x14 +#define GPMC_IRQSTATUS 0x18 +#define GPMC_IRQENABLE 0x1c +#define GPMC_TIMEOUT_CONTROL 0x40 +#define GPMC_ERR_ADDRESS 0x44 +#define GPMC_ERR_TYPE 0x48 +#define GPMC_CONFIG 0x50 +#define GPMC_STATUS 0x54 +#define GPMC_PREFETCH_CONFIG1 0x1e0 +#define GPMC_PREFETCH_CONFIG2 0x1e4 +#define GPMC_PREFETCH_CONTROL 0x1ec +#define GPMC_PREFETCH_STATUS 0x1f0 +#define GPMC_ECC_CONFIG 0x1f4 +#define GPMC_ECC_CONTROL 0x1f8 +#define GPMC_ECC_SIZE_CONFIG 0x1fc +#define GPMC_ECC1_RESULT 0x200 +#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_4 0x300 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_5 0x304 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_6 0x308 /* not available on OMAP2 */ + +/* GPMC ECC control settings */ +#define GPMC_ECC_CTRL_ECCCLEAR 0x100 +#define GPMC_ECC_CTRL_ECCDISABLE 0x000 +#define GPMC_ECC_CTRL_ECCREG1 0x001 +#define GPMC_ECC_CTRL_ECCREG2 0x002 +#define GPMC_ECC_CTRL_ECCREG3 0x003 +#define GPMC_ECC_CTRL_ECCREG4 0x004 +#define GPMC_ECC_CTRL_ECCREG5 0x005 +#define GPMC_ECC_CTRL_ECCREG6 0x006 +#define GPMC_ECC_CTRL_ECCREG7 0x007 +#define GPMC_ECC_CTRL_ECCREG8 0x008 +#define GPMC_ECC_CTRL_ECCREG9 0x009 + +#define GPMC_CONFIG_LIMITEDADDRESS BIT(1) + +#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS BIT(0) + +#define GPMC_CONFIG2_CSEXTRADELAY BIT(7) +#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7) +#define GPMC_CONFIG4_OEEXTRADELAY BIT(7) +#define GPMC_CONFIG4_WEEXTRADELAY BIT(23) +#define GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN BIT(6) +#define GPMC_CONFIG6_CYCLE2CYCLESAMECSEN BIT(7) + +#define GPMC_CS0_OFFSET 0x60 +#define GPMC_CS_SIZE 0x30 +#define GPMC_BCH_SIZE 0x10 + +/* + * The first 1MB of GPMC address space is typically mapped to + * the internal ROM. Never allocate the first page, to + * facilitate bug detection; even if we didn't boot from ROM. + * As GPMC minimum partition size is 16MB we can only start from + * there. + */ +#define GPMC_MEM_START 0x1000000 +#define GPMC_MEM_END 0x3FFFFFFF + +#define GPMC_CHUNK_SHIFT 24 /* 16 MB */ +#define GPMC_SECTION_SHIFT 28 /* 128 MB */ + +#define CS_NUM_SHIFT 24 +#define ENABLE_PREFETCH (0x1 << 7) +#define DMA_MPU_MODE 2 + +#define GPMC_REVISION_MAJOR(l) (((l) >> 4) & 0xf) +#define GPMC_REVISION_MINOR(l) ((l) & 0xf) + +#define GPMC_HAS_WR_ACCESS 0x1 +#define GPMC_HAS_WR_DATA_MUX_BUS 0x2 +#define GPMC_HAS_MUX_AAD 0x4 + +#define GPMC_NR_WAITPINS 4 + +#define GPMC_CS_CONFIG1 0x00 +#define GPMC_CS_CONFIG2 0x04 +#define GPMC_CS_CONFIG3 0x08 +#define GPMC_CS_CONFIG4 0x0c +#define GPMC_CS_CONFIG5 0x10 +#define GPMC_CS_CONFIG6 0x14 +#define GPMC_CS_CONFIG7 0x18 +#define GPMC_CS_NAND_COMMAND 0x1c +#define GPMC_CS_NAND_ADDRESS 0x20 +#define GPMC_CS_NAND_DATA 0x24 + +/* Control Commands */ +#define GPMC_CONFIG_RDY_BSY 0x00000001 +#define GPMC_CONFIG_DEV_SIZE 0x00000002 +#define GPMC_CONFIG_DEV_TYPE 0x00000003 + +#define GPMC_CONFIG_WP 0x00000005 + +#define GPMC_CONFIG1_WRAPBURST_SUPP BIT(31) +#define GPMC_CONFIG1_READMULTIPLE_SUPP BIT(30) +#define GPMC_CONFIG1_READTYPE_ASYNC (0 << 29) +#define GPMC_CONFIG1_READTYPE_SYNC BIT(29) +#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP BIT(28) +#define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27) +#define GPMC_CONFIG1_WRITETYPE_SYNC BIT(27) +#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) (((val) & 3) << 25) +/** CLKACTIVATIONTIME Max Ticks */ +#define GPMC_CONFIG1_CLKACTIVATIONTIME_MAX 2 +#define GPMC_CONFIG1_PAGE_LEN(val) (((val) & 3) << 23) +/** ATTACHEDDEVICEPAGELENGTH Max Value */ +#define GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX 2 +#define GPMC_CONFIG1_WAIT_READ_MON BIT(22) +#define GPMC_CONFIG1_WAIT_WRITE_MON BIT(21) +#define GPMC_CONFIG1_WAIT_MON_TIME(val) (((val) & 3) << 18) +/** WAITMONITORINGTIME Max Ticks */ +#define GPMC_CONFIG1_WAITMONITORINGTIME_MAX 2 +#define GPMC_CONFIG1_WAIT_PIN_SEL(val) (((val) & 3) << 16) +#define GPMC_CONFIG1_DEVICESIZE(val) (((val) & 3) << 12) +#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) +/** DEVICESIZE Max Value */ +#define GPMC_CONFIG1_DEVICESIZE_MAX 1 +#define GPMC_CONFIG1_DEVICETYPE(val) (((val) & 3) << 10) +#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) +#define GPMC_CONFIG1_MUXTYPE(val) (((val) & 3) << 8) +#define GPMC_CONFIG1_TIME_PARA_GRAN BIT(4) +#define GPMC_CONFIG1_FCLK_DIV(val) ((val) & 3) +#define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1)) +#define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2)) +#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3)) +#define GPMC_CONFIG7_CSVALID BIT(6) + +#define GPMC_CONFIG7_BASEADDRESS_MASK 0x3f +#define GPMC_CONFIG7_CSVALID_MASK BIT(6) +#define GPMC_CONFIG7_MASKADDRESS_OFFSET 8 +#define GPMC_CONFIG7_MASKADDRESS_MASK (0xf << GPMC_CONFIG7_MASKADDRESS_OFFSET) +/* All CONFIG7 bits except reserved bits */ +#define GPMC_CONFIG7_MASK (GPMC_CONFIG7_BASEADDRESS_MASK | \ + GPMC_CONFIG7_CSVALID_MASK | \ + GPMC_CONFIG7_MASKADDRESS_MASK) + +#define GPMC_DEVICETYPE_NOR 0 +#define GPMC_DEVICETYPE_NAND 2 +#define GPMC_CONFIG_WRITEPROTECT 0x00000010 +#define WR_RD_PIN_MONITORING 0x00600000 + +/* ECC commands */ +#define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */ +#define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */ +#define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */ + +#define GPMC_NR_NAND_IRQS 2 /* number of NAND specific IRQs */ + +/* bool type time settings */ +struct gpmc_bool_timings { + bool cycle2cyclediffcsen; + bool cycle2cyclesamecsen; + bool we_extra_delay; + bool oe_extra_delay; + bool adv_extra_delay; + bool cs_extra_delay; + bool time_para_granularity; +}; + +/* + * Note that all values in this struct are in nanoseconds except sync_clk + * (which is in picoseconds), while the register values are in gpmc_fck cycles. + */ +struct gpmc_timings { + /* Minimum clock period for synchronous mode (in picoseconds) */ + u32 sync_clk; + + /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ + u32 cs_on; /* Assertion time */ + u32 cs_rd_off; /* Read deassertion time */ + u32 cs_wr_off; /* Write deassertion time */ + + /* ADV signal timings corresponding to GPMC_CONFIG3 */ + u32 adv_on; /* Assertion time */ + u32 adv_rd_off; /* Read deassertion time */ + u32 adv_wr_off; /* Write deassertion time */ + u32 adv_aad_mux_on; /* ADV assertion time for AAD */ + u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */ + u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */ + + /* WE signals timings corresponding to GPMC_CONFIG4 */ + u32 we_on; /* WE assertion time */ + u32 we_off; /* WE deassertion time */ + + /* OE signals timings corresponding to GPMC_CONFIG4 */ + u32 oe_on; /* OE assertion time */ + u32 oe_off; /* OE deassertion time */ + u32 oe_aad_mux_on; /* OE assertion time for AAD */ + u32 oe_aad_mux_off; /* OE deassertion time for AAD */ + + /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ + u32 page_burst_access; /* Multiple access word delay */ + u32 access; /* Start-cycle to first data valid delay */ + u32 rd_cycle; /* Total read cycle time */ + u32 wr_cycle; /* Total write cycle time */ + + u32 bus_turnaround; + u32 cycle2cycle_delay; + + u32 wait_monitoring; + u32 clk_activation; + + /* The following are only on OMAP3430 */ + u32 wr_access; /* WRACCESSTIME */ + u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ + + struct gpmc_bool_timings bool_timings; +}; + +/* Device timings in picoseconds */ +struct gpmc_device_timings { + u32 t_ceasu; /* address setup to CS valid */ + u32 t_avdasu; /* address setup to ADV valid */ + /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is + * of tusb using these timings even for sync whilst + * ideally for adv_rd/(wr)_off it should have considered + * t_avdh instead. This indirectly necessitates r/w + * variations of t_avdp as it is possible to have one + * sync & other async + */ + u32 t_avdp_r; /* ADV low time (what about t_cer ?) */ + u32 t_avdp_w; + u32 t_aavdh; /* address hold time */ + u32 t_oeasu; /* address setup to OE valid */ + u32 t_aa; /* access time from ADV assertion */ + u32 t_iaa; /* initial access time */ + u32 t_oe; /* access time from OE assertion */ + u32 t_ce; /* access time from CS asertion */ + u32 t_rd_cycle; /* read cycle time */ + u32 t_cez_r; /* read CS deassertion to high Z */ + u32 t_cez_w; /* write CS deassertion to high Z */ + u32 t_oez; /* OE deassertion to high Z */ + u32 t_weasu; /* address setup to WE valid */ + u32 t_wpl; /* write assertion time */ + u32 t_wph; /* write deassertion time */ + u32 t_wr_cycle; /* write cycle time */ + + u32 clk; + u32 t_bacc; /* burst access valid clock to output delay */ + u32 t_ces; /* CS setup time to clk */ + u32 t_avds; /* ADV setup time to clk */ + u32 t_avdh; /* ADV hold time from clk */ + u32 t_ach; /* address hold time from clk */ + u32 t_rdyo; /* clk to ready valid */ + + u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */ + u32 t_ce_avd; /* CS on to ADV on delay */ + + /* XXX: check the possibility of combining + * cyc_aavhd_oe & cyc_aavdh_we + */ + u8 cyc_aavdh_oe;/* read address hold time in cycles */ + u8 cyc_aavdh_we;/* write address hold time in cycles */ + u8 cyc_oe; /* access time from OE assertion in cycles */ + u8 cyc_wpl; /* write deassertion time in cycles */ + u32 cyc_iaa; /* initial access time in cycles */ + + /* extra delays */ + bool ce_xdelay; + bool avd_xdelay; + bool oe_xdelay; + bool we_xdelay; +}; + +#define GPMC_BURST_4 4 /* 4 word burst */ +#define GPMC_BURST_8 8 /* 8 word burst */ +#define GPMC_BURST_16 16 /* 16 word burst */ +#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */ +#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */ +#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */ +#define GPMC_MUX_AD 2 /* Addr-Data multiplex */ + +struct gpmc_settings { + bool burst_wrap; /* enables wrap bursting */ + bool burst_read; /* enables read page/burst mode */ + bool burst_write; /* enables write page/burst mode */ + bool device_nand; /* device is NAND */ + bool sync_read; /* enables synchronous reads */ + bool sync_write; /* enables synchronous writes */ + bool wait_on_read; /* monitor wait on reads */ + bool wait_on_write; /* monitor wait on writes */ + u32 burst_len; /* page/burst length */ + u32 device_width; /* device bus width (8 or 16 bit) */ + u32 mux_add_data; /* multiplex address & data */ + u32 wait_pin; /* wait-pin to be used */ +}; diff --git a/examples/standalone/hello_world.c b/examples/standalone/hello_world.c index 263cd9c..27ec379 100644 --- a/examples/standalone/hello_world.c +++ b/examples/standalone/hello_world.c @@ -4,7 +4,6 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ -#include <common.h> #include <exports.h> int hello_world(int argc, char *const argv[]) diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c2b1588..4b2c323 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -77,6 +77,7 @@ enum uclass_id { UCLASS_MASS_STORAGE, /* Mass storage device */ UCLASS_MDIO, /* MDIO bus */ UCLASS_MDIO_MUX, /* MDIO MUX/switch */ + UCLASS_MEMORY, /* Memory Controller device */ UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ diff --git a/include/linux/mtd/omap_gpmc.h b/include/linux/mtd/omap_gpmc.h index 864b05e..f08e700 100644 --- a/include/linux/mtd/omap_gpmc.h +++ b/include/linux/mtd/omap_gpmc.h @@ -8,6 +8,9 @@ #ifndef __ASM_OMAP_GPMC_H #define __ASM_OMAP_GPMC_H +/* Maximum Number of Chip Selects */ +#define GPMC_CS_NUM 8 + #define GPMC_BUF_EMPTY 0 #define GPMC_BUF_FULL 1 #define GPMC_MAX_SECTORS 8 diff --git a/include/spl.h b/include/spl.h index 0fc3686..303a657 100644 --- a/include/spl.h +++ b/include/spl.h @@ -353,7 +353,8 @@ int spl_load_simple_fit(struct spl_image_info *spl_image, * spl_load_legacy_img() - Loads a legacy image from a device. * @spl_image: Image description to set up * @load: Structure containing the information required to load data. - * @header: Pointer to image header (including appended image) + * @offset: Pointer to image + * @hdr: Pointer to image header * * Reads an legacy image from the device. Loads u-boot image to * specified load address. @@ -361,7 +362,9 @@ int spl_load_simple_fit(struct spl_image_info *spl_image, */ int spl_load_legacy_img(struct spl_image_info *spl_image, struct spl_boot_device *bootdev, - struct spl_load_info *load, ulong header); + struct spl_load_info *load, ulong offset, + struct legacy_img_hdr *hdr); + /** * spl_load_imx_container() - Loads a imx container image from a device. diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 0b3a51d..15ac872 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -114,6 +114,7 @@ libs-$(CONFIG_PARTITIONS) += disk/ endif libs-y += drivers/ +libs-$(CONFIG_SPL_MEMORY) += drivers/memory/ libs-$(CONFIG_SPL_USB_GADGET) += drivers/usb/dwc3/ libs-$(CONFIG_SPL_USB_GADGET) += drivers/usb/cdns3/ libs-y += dts/ diff --git a/test/dm/Makefile b/test/dm/Makefile index 499e769..fd7d310 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MDIO) += mdio.o obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o +obj-$(CONFIG_MEMORY) += memory.o obj-$(CONFIG_MISC) += misc.o obj-$(CONFIG_DM_MMC) += mmc.o obj-$(CONFIG_CMD_MUX) += mux-cmd.o diff --git a/test/dm/memory.c b/test/dm/memory.c new file mode 100644 index 0000000..7d9500a --- /dev/null +++ b/test/dm/memory.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 + * Texas Instruments Incorporated, <www.ti.com> + */ + +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_memory(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_MEMORY, &dev)); + + return 0; +} + +DM_TEST(dm_test_memory, UT_TESTF_SCAN_FDT); diff --git a/test/py/requirements.txt b/test/py/requirements.txt index 1bf77b5..fae8b59 100644 --- a/test/py/requirements.txt +++ b/test/py/requirements.txt @@ -20,6 +20,7 @@ pytest-xdist==2.5.0 python-mimeparse==1.6.0 python-subunit==1.3.0 requests==2.25.1 +setuptools==58.3.0 six==1.12.0 testtools==2.3.0 traceback2==1.4.0 diff --git a/tools/image-host.c b/tools/image-host.c index 0bf18df..4e0512b 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -915,7 +915,12 @@ static int fit_config_get_regions(const void *fit, int conf_noffset, int *region_countp, char **region_propp, int *region_proplen) { - char * const exc_prop[] = {"data"}; + char * const exc_prop[] = { + FIT_DATA_PROP, + FIT_DATA_SIZE_PROP, + FIT_DATA_POSITION_PROP, + FIT_DATA_OFFSET_PROP, + }; struct strlist node_inc; struct image_region *region; struct fdt_region fdt_regions[100]; |