diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-12-05 11:43:31 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-12-05 11:43:31 -0800 |
| commit | ec939e4c94bd3ef2fd4f34c15f8aaf79bd0c5ee1 (patch) | |
| tree | 1d39945dbdd233d35c571a726e135fe0ae845814 /drivers/memory | |
| parent | 38206c24ab09b4f4c2a57de5c1af0bb2e69cf5b6 (diff) | |
| parent | 3f6939aec712a15152c32516c1c543a91ac1e717 (diff) | |
| download | linux-ec939e4c94bd3ef2fd4f34c15f8aaf79bd0c5ee1.tar.gz linux-ec939e4c94bd3ef2fd4f34c15f8aaf79bd0c5ee1.tar.bz2 linux-ec939e4c94bd3ef2fd4f34c15f8aaf79bd0c5ee1.zip | |
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Olof Johansson:
"Various driver updates for platforms:
- A larger set of work on Tegra 2/3 around memory controller and
regulator features, some fuse cleanups, etc..
- MMP platform drivers, in particular for USB PHY, and other smaller
additions.
- Samsung Exynos 5422 driver for DMC (dynamic memory configuration),
and ASV (adaptive voltage), allowing the platform to run at more
optimal operating points.
- Misc refactorings and support for RZ/G2N and R8A774B1 from Renesas
- Clock/reset control driver for TI/OMAP
- Meson-A1 reset controller support
- Qualcomm sdm845 and sda845 SoC IDs for socinfo"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (150 commits)
firmware: arm_scmi: Fix doorbell ring logic for !CONFIG_64BIT
soc: fsl: add RCPM driver
dt-bindings: fsl: rcpm: Add 'little-endian' and update Chassis definition
memory: tegra: Consolidate registers definition into common header
memory: tegra: Ensure timing control debug features are disabled
memory: tegra: Introduce Tegra30 EMC driver
memory: tegra: Do not handle error from wait_for_completion_timeout()
memory: tegra: Increase handshake timeout on Tegra20
memory: tegra: Print a brief info message about EMC timings
memory: tegra: Pre-configure debug register on Tegra20
memory: tegra: Include io.h instead of iopoll.h
memory: tegra: Adapt for Tegra20 clock driver changes
memory: tegra: Don't set EMC rate to maximum on probe for Tegra20
memory: tegra: Add gr2d and gr3d to DRM IOMMU group
memory: tegra: Set DMA mask based on supported address bits
soc: at91: Add Atmel SFR SN (Serial Number) support
memory: atmel-ebi: switch to SPDX license identifiers
memory: atmel-ebi: move NUM_CS definition inside EBI driver
soc: mediatek: Refactor bus protection control
soc: mediatek: Refactor sram control
...
Diffstat (limited to 'drivers/memory')
| -rw-r--r-- | drivers/memory/atmel-ebi.c | 11 | ||||
| -rw-r--r-- | drivers/memory/brcmstb_dpfe.c | 164 | ||||
| -rw-r--r-- | drivers/memory/emif.c | 5 | ||||
| -rw-r--r-- | drivers/memory/jedec_ddr.h | 61 | ||||
| -rw-r--r-- | drivers/memory/of_memory.c | 149 | ||||
| -rw-r--r-- | drivers/memory/of_memory.h | 18 | ||||
| -rw-r--r-- | drivers/memory/samsung/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/memory/samsung/Makefile | 1 | ||||
| -rw-r--r-- | drivers/memory/samsung/exynos5422-dmc.c | 1550 | ||||
| -rw-r--r-- | drivers/memory/tegra/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/memory/tegra/Makefile | 1 | ||||
| -rw-r--r-- | drivers/memory/tegra/mc.c | 52 | ||||
| -rw-r--r-- | drivers/memory/tegra/mc.h | 74 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra114.c | 10 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra124.c | 30 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra20-emc.c | 134 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra30-emc.c | 1232 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra30.c | 34 |
18 files changed, 3327 insertions, 222 deletions
diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 0322df9dc249..14386d0b5f57 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * EBI driver for Atmel chips * inspired by the fsl weim bus driver * * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> @@ -19,6 +16,8 @@ #include <linux/regmap.h> #include <soc/at91/atmel-sfr.h> +#define AT91_EBI_NUM_CS 8 + struct atmel_ebi_dev_config { int cs; struct atmel_smc_cs_conf smcconf; @@ -314,7 +313,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, if (ret) return ret; - if (cs >= AT91_MATRIX_EBI_NUM_CS || + if (cs >= AT91_EBI_NUM_CS || !(ebi->caps->available_cs & BIT(cs))) { dev_err(dev, "invalid reg property in %pOF\n", np); return -EINVAL; @@ -344,7 +343,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, apply = true; i = 0; - for_each_set_bit(cs, &cslines, AT91_MATRIX_EBI_NUM_CS) { + for_each_set_bit(cs, &cslines, AT91_EBI_NUM_CS) { ebid->configs[i].cs = cs; if (apply) { diff --git a/drivers/memory/brcmstb_dpfe.c b/drivers/memory/brcmstb_dpfe.c index 6827ed484750..82b415be18d1 100644 --- a/drivers/memory/brcmstb_dpfe.c +++ b/drivers/memory/brcmstb_dpfe.c @@ -127,7 +127,6 @@ enum dpfe_msg_fields { MSG_COMMAND, MSG_ARG_COUNT, MSG_ARG0, - MSG_CHKSUM, MSG_FIELD_MAX = 16 /* Max number of arguments */ }; @@ -180,7 +179,7 @@ struct dpfe_api { }; /* Things we need for as long as we are active. */ -struct private_data { +struct brcmstb_dpfe_priv { void __iomem *regs; void __iomem *dmem; void __iomem *imem; @@ -232,9 +231,13 @@ static struct attribute *dpfe_v3_attrs[] = { }; ATTRIBUTE_GROUPS(dpfe_v3); -/* API v2 firmware commands */ -static const struct dpfe_api dpfe_api_v2 = { - .version = 2, +/* + * Old API v2 firmware commands, as defined in the rev 0.61 specification, we + * use a version set to 1 to denote that it is not compatible with the new API + * v2 and onwards. + */ +static const struct dpfe_api dpfe_api_old_v2 = { + .version = 1, .fw_name = "dpfe.bin", .sysfs_attrs = dpfe_v2_groups, .command = { @@ -243,21 +246,42 @@ static const struct dpfe_api dpfe_api_v2 = { [MSG_COMMAND] = 1, [MSG_ARG_COUNT] = 1, [MSG_ARG0] = 1, - [MSG_CHKSUM] = 4, }, [DPFE_CMD_GET_REFRESH] = { [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, [MSG_COMMAND] = 2, [MSG_ARG_COUNT] = 1, [MSG_ARG0] = 1, - [MSG_CHKSUM] = 5, }, [DPFE_CMD_GET_VENDOR] = { [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, [MSG_COMMAND] = 2, [MSG_ARG_COUNT] = 1, [MSG_ARG0] = 2, - [MSG_CHKSUM] = 6, + }, + } +}; + +/* + * API v2 firmware commands, as defined in the rev 0.8 specification, named new + * v2 here + */ +static const struct dpfe_api dpfe_api_new_v2 = { + .version = 2, + .fw_name = NULL, /* We expect the firmware to have been downloaded! */ + .sysfs_attrs = dpfe_v2_groups, + .command = { + [DPFE_CMD_GET_INFO] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 0x101, + }, + [DPFE_CMD_GET_REFRESH] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 0x201, + }, + [DPFE_CMD_GET_VENDOR] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 0x202, }, } }; @@ -273,49 +297,51 @@ static const struct dpfe_api dpfe_api_v3 = { [MSG_COMMAND] = 0x0101, [MSG_ARG_COUNT] = 1, [MSG_ARG0] = 1, - [MSG_CHKSUM] = 0x104, }, [DPFE_CMD_GET_REFRESH] = { [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, [MSG_COMMAND] = 0x0202, [MSG_ARG_COUNT] = 0, - /* - * This is a bit ugly. Without arguments, the checksum - * follows right after the argument count and not at - * offset MSG_CHKSUM. - */ - [MSG_ARG0] = 0x203, }, /* There's no GET_VENDOR command in API v3. */ }, }; -static bool is_dcpu_enabled(void __iomem *regs) +static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv) { u32 val; - val = readl_relaxed(regs + REG_DCPU_RESET); + mutex_lock(&priv->lock); + val = readl_relaxed(priv->regs + REG_DCPU_RESET); + mutex_unlock(&priv->lock); return !(val & DCPU_RESET_MASK); } -static void __disable_dcpu(void __iomem *regs) +static void __disable_dcpu(struct brcmstb_dpfe_priv *priv) { u32 val; - if (!is_dcpu_enabled(regs)) + if (!is_dcpu_enabled(priv)) return; + mutex_lock(&priv->lock); + /* Put DCPU in reset if it's running. */ - val = readl_relaxed(regs + REG_DCPU_RESET); + val = readl_relaxed(priv->regs + REG_DCPU_RESET); val |= (1 << DCPU_RESET_SHIFT); - writel_relaxed(val, regs + REG_DCPU_RESET); + writel_relaxed(val, priv->regs + REG_DCPU_RESET); + + mutex_unlock(&priv->lock); } -static void __enable_dcpu(void __iomem *regs) +static void __enable_dcpu(struct brcmstb_dpfe_priv *priv) { + void __iomem *regs = priv->regs; u32 val; + mutex_lock(&priv->lock); + /* Clear mailbox registers. */ writel_relaxed(0, regs + REG_TO_DCPU_MBOX); writel_relaxed(0, regs + REG_TO_HOST_MBOX); @@ -329,6 +355,8 @@ static void __enable_dcpu(void __iomem *regs) val = readl_relaxed(regs + REG_DCPU_RESET); val &= ~(1 << DCPU_RESET_SHIFT); writel_relaxed(val, regs + REG_DCPU_RESET); + + mutex_unlock(&priv->lock); } static unsigned int get_msg_chksum(const u32 msg[], unsigned int max) @@ -343,7 +371,7 @@ static unsigned int get_msg_chksum(const u32 msg[], unsigned int max) return sum; } -static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, +static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response, char *buf, ssize_t *size) { unsigned int msg_type; @@ -382,7 +410,7 @@ static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, return ptr; } -static void __finalize_command(struct private_data *priv) +static void __finalize_command(struct brcmstb_dpfe_priv *priv) { unsigned int release_mbox; @@ -390,12 +418,12 @@ static void __finalize_command(struct private_data *priv) * It depends on the API version which MBOX register we have to write to * to signal we are done. */ - release_mbox = (priv->dpfe_api->version < 3) + release_mbox = (priv->dpfe_api->version < 2) ? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX; writel_relaxed(0, priv->regs + release_mbox); } -static int __send_command(struct private_data *priv, unsigned int cmd, +static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd, u32 result[]) { const u32 *msg = priv->dpfe_api->command[cmd]; @@ -421,9 +449,17 @@ static int __send_command(struct private_data *priv, unsigned int cmd, return -ETIMEDOUT; } + /* Compute checksum over the message */ + chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1; + chksum = get_msg_chksum(msg, chksum_idx); + /* Write command and arguments to message area */ - for (i = 0; i < MSG_FIELD_MAX; i++) - writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); + for (i = 0; i < MSG_FIELD_MAX; i++) { + if (i == chksum_idx) + writel_relaxed(chksum, regs + DCPU_MSG_RAM(i)); + else + writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); + } /* Tell DCPU there is a command waiting */ writel_relaxed(1, regs + REG_TO_DCPU_MBOX); @@ -517,7 +553,7 @@ static int __verify_firmware(struct init_data *init, /* Verify checksum by reading back the firmware from co-processor RAM. */ static int __verify_fw_checksum(struct init_data *init, - struct private_data *priv, + struct brcmstb_dpfe_priv *priv, const struct dpfe_firmware_header *header, u32 checksum) { @@ -571,26 +607,23 @@ static int __write_firmware(u32 __iomem *mem, const u32 *fw, return 0; } -static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, - struct init_data *init) +static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv) { const struct dpfe_firmware_header *header; unsigned int dmem_size, imem_size; - struct device *dev = &pdev->dev; + struct device *dev = priv->dev; bool is_big_endian = false; - struct private_data *priv; const struct firmware *fw; const u32 *dmem, *imem; + struct init_data init; const void *fw_blob; int ret; - priv = platform_get_drvdata(pdev); - /* * Skip downloading the firmware if the DCPU is already running and * responding to commands. */ - if (is_dcpu_enabled(priv->regs)) { + if (is_dcpu_enabled(priv)) { u32 response[MSG_FIELD_MAX]; ret = __send_command(priv, DPFE_CMD_GET_INFO, response); @@ -606,20 +639,23 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, if (!priv->dpfe_api->fw_name) return -ENODEV; - ret = request_firmware(&fw, priv->dpfe_api->fw_name, dev); - /* request_firmware() prints its own error messages. */ + ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev); + /* + * Defer the firmware download if the firmware file couldn't be found. + * The root file system may not be available yet. + */ if (ret) - return ret; + return (ret == -ENOENT) ? -EPROBE_DEFER : ret; - ret = __verify_firmware(init, fw); + ret = __verify_firmware(&init, fw); if (ret) return -EFAULT; - __disable_dcpu(priv->regs); + __disable_dcpu(priv); - is_big_endian = init->is_big_endian; - dmem_size = init->dmem_len; - imem_size = init->imem_len; + is_big_endian = init.is_big_endian; + dmem_size = init.dmem_len; + imem_size = init.imem_len; /* At the beginning of the firmware blob is a header. */ header = (struct dpfe_firmware_header *)fw->data; @@ -637,17 +673,17 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, if (ret) return ret; - ret = __verify_fw_checksum(init, priv, header, init->chksum); + ret = __verify_fw_checksum(&init, priv, header, init.chksum); if (ret) return ret; - __enable_dcpu(priv->regs); + __enable_dcpu(priv); return 0; } static ssize_t generic_show(unsigned int command, u32 response[], - struct private_data *priv, char *buf) + struct brcmstb_dpfe_priv *priv, char *buf) { int ret; @@ -665,7 +701,7 @@ static ssize_t show_info(struct device *dev, struct device_attribute *devattr, char *buf) { u32 response[MSG_FIELD_MAX]; - struct private_data *priv; + struct brcmstb_dpfe_priv *priv; unsigned int info; ssize_t ret; @@ -688,7 +724,7 @@ static ssize_t show_refresh(struct device *dev, { u32 response[MSG_FIELD_MAX]; void __iomem *info; - struct private_data *priv; + struct brcmstb_dpfe_priv *priv; u8 refresh, sr_abort, ppre, thermal_offs, tuf; u32 mr4; ssize_t ret; @@ -721,7 +757,7 @@ static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u32 response[MSG_FIELD_MAX]; - struct private_data *priv; + struct brcmstb_dpfe_priv *priv; void __iomem *info; unsigned long val; int ret; @@ -747,7 +783,7 @@ static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr, char *buf) { u32 response[MSG_FIELD_MAX]; - struct private_data *priv; + struct brcmstb_dpfe_priv *priv; void __iomem *info; ssize_t ret; u32 mr5, mr6, mr7, mr8, err; @@ -778,7 +814,7 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, char *buf) { u32 response[MSG_FIELD_MAX]; - struct private_data *priv; + struct brcmstb_dpfe_priv *priv; ssize_t ret; u32 mr4, mr5, mr6, mr7, mr8, err; @@ -800,16 +836,15 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, static int brcmstb_dpfe_resume(struct platform_device *pdev) { - struct init_data init; + struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev); - return brcmstb_dpfe_download_firmware(pdev, &init); + return brcmstb_dpfe_download_firmware(priv); } static int brcmstb_dpfe_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct private_data *priv; - struct init_data init; + struct brcmstb_dpfe_priv *priv; struct resource *res; int ret; @@ -817,6 +852,8 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->dev = dev; + mutex_init(&priv->lock); platform_set_drvdata(pdev, priv); @@ -851,9 +888,10 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) return -ENOENT; } - ret = brcmstb_dpfe_download_firmware(pdev, &init); + ret = brcmstb_dpfe_download_firmware(priv); if (ret) { - dev_err(dev, "Couldn't download firmware -- %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Couldn't download firmware -- %d\n", ret); return ret; } @@ -867,7 +905,7 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) static int brcmstb_dpfe_remove(struct platform_device *pdev) { - struct private_data *priv = dev_get_drvdata(&pdev->dev); + struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev); sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs); @@ -876,10 +914,10 @@ static int brcmstb_dpfe_remove(struct platform_device *pdev) static const struct of_device_id brcmstb_dpfe_of_match[] = { /* Use legacy API v2 for a select number of chips */ - { .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_v2 }, - { .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_v2 }, - { .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_v2 }, - { .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_v2 }, + { .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 }, + { .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 }, + { .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 }, + { .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 }, /* API v3 is the default going forward */ { .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 }, {} diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index 402c6bc8e621..9d9127bf2a59 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -1613,7 +1613,7 @@ static void emif_shutdown(struct platform_device *pdev) static int get_emif_reg_values(struct emif_data *emif, u32 freq, struct emif_regs *regs) { - u32 cs1_used, ip_rev, phy_type; + u32 ip_rev, phy_type; u32 cl, type; const struct lpddr2_timings *timings; const struct lpddr2_min_tck *min_tck; @@ -1621,7 +1621,6 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq, const struct lpddr2_addressing *addressing; struct emif_data *emif_for_calc; struct device *dev; - const struct emif_custom_configs *custom_configs; dev = emif->dev; /* @@ -1639,12 +1638,10 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq, device_info = emif_for_calc->plat_data->device_info; type = device_info->type; - cs1_used = device_info->cs1_used; ip_rev = emif_for_calc->plat_data->ip_rev; phy_type = emif_for_calc->plat_data->phy_type; min_tck = emif_for_calc->plat_data->min_tck; - custom_configs = emif_for_calc->plat_data->custom_configs; set_ddr_clk_period(freq); diff --git a/drivers/memory/jedec_ddr.h b/drivers/memory/jedec_ddr.h index 4a21b5044ff8..e59ccbd982d0 100644 --- a/drivers/memory/jedec_ddr.h +++ b/drivers/memory/jedec_ddr.h @@ -29,6 +29,7 @@ #define DDR_TYPE_LPDDR2_S4 3 #define DDR_TYPE_LPDDR2_S2 4 #define DDR_TYPE_LPDDR2_NVM 5 +#define DDR_TYPE_LPDDR3 6 /* DDR IO width */ #define DDR_IO_WIDTH_4 1 @@ -169,4 +170,64 @@ extern const struct lpddr2_timings lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; +/* + * Structure for timings for LPDDR3 based on LPDDR2 plus additional fields. + * All parameters are in pico seconds(ps) excluding max_freq, min_freq which + * are in Hz. + */ +struct lpddr3_timings { + u32 max_freq; + u32 min_freq; + u32 tRFC; + u32 tRRD; + u32 tRPab; + u32 tRPpb; + u32 tRCD; + u32 tRC; + u32 tRAS; + u32 tWTR; + u32 tWR; + u32 tRTP; + u32 tW2W_C2C; + u32 tR2R_C2C; + u32 tWL; + u32 tDQSCK; + u32 tRL; + u32 tFAW; + u32 tXSR; + u32 tXP; + u32 tCKE; + u32 tCKESR; + u32 tMRD; +}; + +/* + * Min value for some parameters in terms of number of tCK cycles(nCK) + * Please set to zero parameters that are not valid for a given memory + * type + */ +struct lpddr3_min_tck { + u32 tRFC; + u32 tRRD; + u32 tRPab; + u32 tRPpb; + u32 tRCD; + u32 tRC; + u32 tRAS; + u32 tWTR; + u32 tWR; + u32 tRTP; + u32 tW2W_C2C; + u32 tR2R_C2C; + u32 tWL; + u32 tDQSCK; + u32 tRL; + u32 tFAW; + u32 tXSR; + u32 tXP; + u32 tCKE; + u32 tCKESR; + u32 tMRD; +}; + #endif /* __JEDEC_DDR_H */ diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c index 46539b27a3fb..71f26eac7350 100644 --- a/drivers/memory/of_memory.c +++ b/drivers/memory/of_memory.c @@ -3,6 +3,7 @@ * OpenFirmware helpers for memory drivers * * Copyright (C) 2012 Texas Instruments, Inc. + * Copyright (C) 2019 Samsung Electronics Co., Ltd. */ #include <linux/device.h> @@ -149,3 +150,151 @@ default_timings: return lpddr2_jedec_timings; } EXPORT_SYMBOL(of_get_ddr_timings); + +/** + * of_lpddr3_get_min_tck() - extract min timing values for lpddr3 + * @np: pointer to ddr device tree node + * @device: device requesting for min timing values + * + * Populates the lpddr3_min_tck structure by extracting data + * from device tree node. Returns a pointer to the populated + * structure. If any error in populating the structure, returns NULL. + */ +const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, + struct device *dev) +{ + int ret = 0; + struct lpddr3_min_tck *min; + + min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL); + if (!min) + goto default_min_tck; + + ret |= of_property_read_u32(np, "tRFC-min-tck", &min->tRFC); + ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD); + ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab); + ret |= of_property_read_u32(np, "tRPpb-min-tck", &min->tRPpb); + ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD); + ret |= of_property_read_u32(np, "tRC-min-tck", &min->tRC); + ret |= of_property_read_u32(np, "tRAS-min-tck", &min->tRAS); + ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR); + ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR); + ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP); + ret |= of_property_read_u32(np, "tW2W-C2C-min-tck", &min->tW2W_C2C); + ret |= of_property_read_u32(np, "tR2R-C2C-min-tck", &min->tR2R_C2C); + ret |= of_property_read_u32(np, "tWL-min-tck", &min->tWL); + ret |= of_property_read_u32(np, "tDQSCK-min-tck", &min->tDQSCK); + ret |= of_property_read_u32(np, "tRL-min-tck", &min->tRL); + ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW); + ret |= of_property_read_u32(np, "tXSR-min-tck", &min->tXSR); + ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP); + ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE); + ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR); + ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD); + + if (ret) { + dev_warn(dev, "%s: errors while parsing min-tck values\n", + __func__); + devm_kfree(dev, min); + goto default_min_tck; + } + + return min; + +default_min_tck: + dev_warn(dev, "%s: using default min-tck values\n", __func__); + return NULL; +} +EXPORT_SYMBOL(of_lpddr3_get_min_tck); + +static int of_lpddr3_do_get_timings(struct device_node *np, + struct lpddr3_timings *tim) +{ + int ret; + + /* The 'reg' param required since DT has changed, used as 'max-freq' */ + ret = of_property_read_u32(np, "reg", &tim->max_freq); + ret |= of_property_read_u32(np, "min-freq", &tim->min_freq); + ret |= of_property_read_u32(np, "tRFC", &tim->tRFC); + ret |= of_property_read_u32(np, "tRRD", &tim->tRRD); + ret |= of_property_read_u32(np, "tRPab", &tim->tRPab); + ret |= of_property_read_u32(np, "tRPpb", &tim->tRPpb); + ret |= of_property_read_u32(np, "tRCD", &tim->tRCD); + ret |= of_property_read_u32(np, "tRC", &tim->tRC); + ret |= of_property_read_u32(np, "tRAS", &tim->tRAS); + ret |= of_property_read_u32(np, "tWTR", &tim->tWTR); + ret |= of_property_read_u32(np, "tWR", &tim->tWR); + ret |= of_property_read_u32(np, "tRTP", &tim->tRTP); + ret |= of_property_read_u32(np, "tW2W-C2C", &tim->tW2W_C2C); + ret |= of_property_read_u32(np, "tR2R-C2C", &tim->tR2R_C2C); + ret |= of_property_read_u32(np, "tFAW", &tim->tFAW); + ret |= of_property_read_u32(np, "tXSR", &tim->tXSR); + ret |= of_property_read_u32(np, "tXP", &tim->tXP); + ret |= of_property_read_u32(np, "tCKE", &tim->tCKE); + ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR); + ret |= of_property_read_u32(np, "tMRD", &tim->tMRD); + + return ret; +} + +/** + * of_lpddr3_get_ddr_timings() - extracts the lpddr3 timings and updates no of + * frequencies available. + * @np_ddr: Pointer to ddr device tree node + * @dev: Device requesting for ddr timings + * @device_type: Type of ddr + * @nr_frequencies: No of frequencies available for ddr + * (updated by this function) + * + * Populates lpddr3_timings structure by extracting data from device + * tree node. Returns pointer to populated structure. If any error + * while populating, returns NULL. + */ +const struct lpddr3_timings +*of_lpddr3_get_ddr_timings(struct device_node *np_ddr, struct device *dev, + u32 device_type, u32 *nr_frequencies) +{ + struct lpddr3_timings *timings = NULL; + u32 arr_sz = 0, i = 0; + struct device_node *np_tim; + char *tim_compat = NULL; + + switch (device_type) { + case DDR_TYPE_LPDDR3: + tim_compat = "jedec,lpddr3-timings"; + break; + default: + dev_warn(dev, "%s: un-supported memory type\n", __func__); + } + + for_each_child_of_node(np_ddr, np_tim) + if (of_device_is_compatible(np_tim, tim_compat)) + arr_sz++; + + if (arr_sz) + timings = devm_kcalloc(dev, arr_sz, sizeof(*timings), + GFP_KERNEL); + + if (!timings) + goto default_timings; + + for_each_child_of_node(np_ddr, np_tim) { + if (of_device_is_compatible(np_tim, tim_compat)) { + if (of_lpddr3_do_get_timings(np_tim, &timings[i])) { + devm_kfree(dev, timings); + goto default_timings; + } + i++; + } + } + + *nr_frequencies = arr_sz; + + return timings; + +default_timings: + dev_warn(dev, "%s: failed to get timings\n", __func__); + *nr_frequencies = 0; + return NULL; +} +EXPORT_SYMBOL(of_lpddr3_get_ddr_timings); diff --git a/drivers/memory/of_memory.h b/drivers/memory/of_memory.h index b077cc836b0b..e39ecc4c733d 100644 --- a/drivers/memory/of_memory.h +++ b/drivers/memory/of_memory.h @@ -14,6 +14,11 @@ extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, extern const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, u32 device_type, u32 *nr_frequencies); +extern const struct lpddr3_min_tck + *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev); +extern const struct lpddr3_timings + *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, + struct device *dev, u32 device_type, u32 *nr_frequencies); #else static inline const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, struct device *dev) @@ -27,6 +32,19 @@ static inline const struct lpddr2_timings { return NULL; } + +static inline const struct lpddr3_min_tck + *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev) +{ + return NULL; +} + +static inline const struct lpddr3_timings + *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, + struct device *dev, u32 device_type, u32 *nr_frequencies) +{ + return NULL; +} #endif /* CONFIG_OF && CONFIG_DDR */ #endif /* __LINUX_MEMORY_OF_REG_ */ diff --git a/drivers/memory/samsung/Kconfig b/drivers/memory/samsung/Kconfig index 79ce7ea58903..e9c3ce92350c 100644 --- a/drivers/memory/samsung/Kconfig +++ b/drivers/memory/samsung/Kconfig @@ -7,6 +7,19 @@ config SAMSUNG_MC if SAMSUNG_MC +config EXYNOS5422_DMC + tristate "EXYNOS5422 Dynamic Memory Controller driver" + depends on ARCH_EXYNOS || (COMPILE_TEST && HAS_IOMEM) + select DDR + depends on DEVFREQ_GOV_SIMPLE_ONDEMAND + depends on (PM_DEVFREQ && PM_DEVFREQ_EVENT) + help + This adds driver for Exynos5422 DMC (Dynamic Memory Controller). + The driver provides support for Dynamic Voltage and Frequency Scaling in + DMC and DRAM. It also supports changing timings of DRAM running with + different frequency. The timings are calculated based on DT memory + information. + config EXYNOS_SROM bool "Exynos SROM controller driver" if COMPILE_TEST depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM) diff --git a/drivers/memory/samsung/Makefile b/drivers/memory/samsung/Makefile index 00587be66211..ea071be21c44 100644 --- a/drivers/memory/samsung/Makefile +++ b/drivers/memory/samsung/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_EXYNOS5422_DMC) += exynos5422-dmc.o obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c new file mode 100644 index 000000000000..47dbf6d1789f --- /dev/null +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -0,0 +1,1550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Author: Lukasz Luba <l.luba@partner.samsung.com> + */ + +#include <linux/clk.h> +#include <linux/devfreq.h> +#include <linux/devfreq-event.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_opp.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include "../jedec_ddr.h" +#include "../of_memory.h" + +#define EXYNOS5_DREXI_TIMINGAREF (0x0030) +#define EXYNOS5_DREXI_TIMINGROW0 (0x0034) +#define EXYNOS5_DREXI_TIMINGDATA0 (0x0038) +#define EXYNOS5_DREXI_TIMINGPOWER0 (0x003C) +#define EXYNOS5_DREXI_TIMINGROW1 (0x00E4) +#define EXYNOS5_DREXI_TIMINGDATA1 (0x00E8) +#define EXYNOS5_DREXI_TIMINGPOWER1 (0x00EC) +#define CDREX_PAUSE (0x2091c) +#define CDREX_LPDDR3PHY_CON3 (0x20a20) +#define CDREX_LPDDR3PHY_CLKM_SRC (0x20700) +#define EXYNOS5_TIMING_SET_SWI BIT(28) +#define USE_MX_MSPLL_TIMINGS (1) +#define USE_BPLL_TIMINGS (0) +#define EXYNOS5_AREF_NORMAL (0x2e) + +#define DREX_PPCCLKCON (0x0130) +#define DREX_PEREV2CONFIG (0x013c) +#define DREX_PMNC_PPC (0xE000) +#define DREX_CNTENS_PPC (0xE010) +#define DREX_CNTENC_PPC (0xE020) +#define DREX_INTENS_PPC (0xE030) +#define DREX_INTENC_PPC (0xE040) +#define DREX_FLAG_PPC (0xE050) +#define DREX_PMCNT2_PPC (0xE130) + +/* + * A value for register DREX_PMNC_PPC which should be written to reset + * the cycle counter CCNT (a reference wall clock). It sets zero to the + * CCNT counter. |
