mirror of https://gitee.com/openkylin/linux.git
Samsung DMC driver for v5.5
Add Samsung Dynamic Memory Controller for Exynos5422 which provides scaling of frequency and voltage of memory controller and DRAM. The driver allows to reduce energy usage without performance impact. -----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEE3dJiKD0RGyM7briowTdm5oaLg9cFAl2t8FkQHGtyemtAa2Vy bmVsLm9yZwAKCRDBN2bmhouD1+wZD/9O3cKcO2T5WmDw0sZt+a7LZBbm2nCCZNk1 v+aHTYbjYEfgSfPaCHbGNjC2HjHN+ThyY3f4UyPkG3WEsRvu2DTio7RjXWGNJHQg rQ4c4sZU+5if0nXjw6QG3jtOj0q77q4XYgX9hg3Xl1hab7f8MjrhxvuRGkHIumzI SRCSVi3L4K93YTKqDa/Z1xQtCOuXDtTtnAdy2iY4YY8JE9mRgHlGKlcgdWXPlXia Fo0K9nGXOsRmA25twlqnA7ZkRh18PGQMC85o8vccqt39DOyUVwylwduvSPKosWsX bn6LwpaYwAAj2nd65evRXs5W8EHx8ODGVEmELNYiVV0bLu3eaxKyynI/F5BxGuPO ZYa2+BqG7TSHIJbb/6siYKZOhf8FRKRapjWy3Rx0oMK/pr0yGjmVyQDSp2lRvYk4 lUOm3d9IV5u3YNHgdiEdsiVSGEFK2enk7C2Lq/0++FkhT4GzRkJ13kj53fJxQxN/ xAs7L8mEpSqcFJMDoUjUOYCeNmJhNjkYVDG32I/quL2wtl8GQVOgty7sRwJBTDv5 6gRCfBU2xBjSWuWCRT7O4z2bpW2V5TaqYiCNvivFbWpE/z7G1Z0z/iQalgYoVxZs ztuuizkGuKcZAl1EA8DTmA5/Hk4uo2BirOxfsRZy5CLjukbUp+lh7S0mzC0+su70 hhNtFpRWdQ== =+imO -----END PGP SIGNATURE----- Merge tag 'samsung-drivers-dmc-5.5' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into arm/drivers Samsung DMC driver for v5.5 Add Samsung Dynamic Memory Controller for Exynos5422 which provides scaling of frequency and voltage of memory controller and DRAM. The driver allows to reduce energy usage without performance impact. * tag 'samsung-drivers-dmc-5.5' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux: memory: samsung: exynos5422-dmc: Add support for interrupt from performance counters memory: samsung: exynos5422-dmc: Fix kfree() of devm-allocated memory and missing static memory: samsung: exynos5422-dmc: Fix spelling mistake "counld" -> "could" memory: Add DMC driver for Exynos5422 memory: Extend of_memory with LPDDR3 support Link: https://lore.kernel.org/r/20191021180453.29455-3-krzk@kernel.org Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
2051818b34
|
@ -4969,6 +4969,14 @@ F: include/linux/dma-direct.h
|
|||
F: include/linux/dma-mapping.h
|
||||
F: include/linux/dma-noncoherent.h
|
||||
|
||||
DMC FREQUENCY DRIVER FOR SAMSUNG EXYNOS5422
|
||||
M: Lukasz Luba <l.luba@partner.samsung.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
L: linux-samsung-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/memory/samsung/exynos5422-dmc.c
|
||||
F: Documentation/devicetree/bindings/memory-controllers/exynos5422-dmc.txt
|
||||
|
||||
DME1737 HARDWARE MONITOR DRIVER
|
||||
M: Juerg Haefliger <juergh@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
|
|||
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);
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_EXYNOS5422_DMC) += exynos5422-dmc.o
|
||||
obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue